mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-12-07 05:31:08 +01:00
Jingle Extension added to Smack Repository
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6517 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
f1972c2571
commit
4b6de6647b
104 changed files with 16411 additions and 0 deletions
|
|
@ -0,0 +1,440 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
|
||||
* ====================================================================
|
||||
* The Jive Software License (based on Apache Software License, Version 1.1)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by
|
||||
* Jive Software (http://www.jivesoftware.com)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Smack" and "Jive Software" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please
|
||||
* contact webmaster@jivesoftware.com.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Smack",
|
||||
* nor may "Smack" appear in their name, without prior written
|
||||
* permission of Jive Software.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.jingle;
|
||||
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.jingle.listeners.JingleMediaListener;
|
||||
import org.jivesoftware.smackx.jingle.listeners.JingleTransportListener;
|
||||
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
|
||||
import org.jivesoftware.smackx.jingle.media.MediaNegotiator;
|
||||
import org.jivesoftware.smackx.jingle.media.PayloadType;
|
||||
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
|
||||
import org.jivesoftware.smackx.jingle.nat.TransportNegotiator;
|
||||
import org.jivesoftware.smackx.jingle.nat.TransportResolver;
|
||||
import org.jivesoftware.smackx.packet.Jingle;
|
||||
import org.jivesoftware.smackx.packet.JingleContentDescription;
|
||||
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
|
||||
import org.jivesoftware.smackx.packet.JingleError;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An outgoing Jingle session implementation.
|
||||
* This class has especific bahavior to Request and establish a new Jingle Session.
|
||||
*
|
||||
* This class is not directly used by users. Instead, users should refer to the
|
||||
* JingleManager class, that will create the appropiate instance...
|
||||
*
|
||||
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
|
||||
*/
|
||||
public class OutgoingJingleSession extends JingleSession {
|
||||
|
||||
// states
|
||||
|
||||
private final Inviting inviting;
|
||||
|
||||
private final Pending pending;
|
||||
|
||||
private final Active active;
|
||||
|
||||
/**
|
||||
* Constructor for a Jingle outgoing session.
|
||||
*
|
||||
* @param conn the XMPP connection
|
||||
* @param responder the other endpoint
|
||||
* @param payloadTypes A list of payload types, in order of preference.
|
||||
* @param resolver The transport resolver.
|
||||
*/
|
||||
protected OutgoingJingleSession(XMPPConnection conn, String responder,
|
||||
List payloadTypes, TransportResolver resolver) {
|
||||
|
||||
super(conn, conn.getUser(), responder);
|
||||
|
||||
setSid(generateSessionId());
|
||||
|
||||
// Initialize the states.
|
||||
inviting = new Inviting(this);
|
||||
pending = new Pending(this);
|
||||
active = new Active(this);
|
||||
|
||||
// Create description and transport negotiatiors...
|
||||
setMediaNeg(new MediaNegotiator(this, payloadTypes));
|
||||
if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
|
||||
setTransportNeg(new TransportNegotiator.RawUdp(this, resolver));
|
||||
}
|
||||
if (resolver.getType().equals(TransportResolver.Type.ice)) {
|
||||
setTransportNeg(new TransportNegotiator.Ice(this, resolver));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a Jingle outgoing session with a defined Media Manager
|
||||
*
|
||||
* @param conn the XMPP connection
|
||||
* @param responder the other endpoint
|
||||
* @param payloadTypes A list of payload types, in order of preference.
|
||||
* @param resolver The transport resolver.
|
||||
* @param jingleMediaManager The Media Manager for this Session
|
||||
*/
|
||||
protected OutgoingJingleSession(XMPPConnection conn, String responder,
|
||||
List payloadTypes, TransportResolver resolver, JingleMediaManager jingleMediaManager) {
|
||||
this(conn, responder, payloadTypes, resolver);
|
||||
this.jingleMediaManager = jingleMediaManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the negotiation with an invitation. This method must be invoked
|
||||
* for starting all negotiations. It is the initial starting point and,
|
||||
* afterwards, any other packet processing is done with the packet listener
|
||||
* callback...
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void start(JingleSessionRequest req) throws IllegalStateException {
|
||||
if (invalidState()) {
|
||||
setState(inviting);
|
||||
|
||||
// Use the standard behavior, using a null Jingle packet
|
||||
try {
|
||||
updatePacketListener();
|
||||
respond((Jingle) null);
|
||||
} catch (XMPPException e) {
|
||||
e.printStackTrace();
|
||||
close();
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Starting session without null state.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the negotiation with an invitation. This method must be invoked
|
||||
* for starting all negotiations. It is the initial starting point and,
|
||||
* afterwards, any other packet processing is done with the packet listener
|
||||
* callback...
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void start() throws IllegalStateException {
|
||||
start(null);
|
||||
}
|
||||
|
||||
// States
|
||||
|
||||
/**
|
||||
* Current state when we want to invite the other endpoint.
|
||||
*/
|
||||
public class Inviting extends JingleNegotiator.State {
|
||||
|
||||
public Inviting(JingleNegotiator neg) {
|
||||
super(neg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invitation packet.
|
||||
*/
|
||||
public Jingle eventInvite() {
|
||||
// Create an invitation packet, saving the Packet ID, for any ACK
|
||||
return new Jingle(Jingle.Action.SESSIONINITIATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The receiver has partially accepted our invitation. We go to the
|
||||
* pending state while the content and transport negotiators work...
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
|
||||
*/
|
||||
public Jingle eventAck(IQ iq) {
|
||||
setState(pending);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The other endpoint has declined the invitation with an error.
|
||||
*
|
||||
* @throws XMPPException
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventError(org.jivesoftware.smack.packet.IQ)
|
||||
*/
|
||||
public void eventError(IQ iq) throws XMPPException {
|
||||
triggerSessionDeclined(null);
|
||||
super.eventError(iq);
|
||||
}
|
||||
|
||||
/**
|
||||
* The other endpoint wants to redirect this connection.
|
||||
*/
|
||||
public Jingle eventRedirect(Jingle jin) {
|
||||
String redirArg = null;
|
||||
|
||||
// TODO: parse the redirection parameters...
|
||||
|
||||
triggerSessionRedirect(redirArg);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Pending" state: we are waiting for the transport and content
|
||||
* negotiators.
|
||||
* <p/>
|
||||
* Note: the transition from/to this state is done with listeners...
|
||||
*/
|
||||
public class Pending extends JingleNegotiator.State {
|
||||
JingleMediaListener jingleMediaListener;
|
||||
|
||||
JingleTransportListener jingleTransportListener;
|
||||
|
||||
public Pending(JingleNegotiator neg) {
|
||||
super(neg);
|
||||
|
||||
// Create the listeners that will send a "session-accept" when
|
||||
// the sub-negotiators are done.
|
||||
jingleMediaListener = new JingleMediaListener() {
|
||||
public void mediaClosed(PayloadType cand) {
|
||||
}
|
||||
|
||||
public void mediaEstablished(PayloadType pt) {
|
||||
checkFullyEstablished();
|
||||
}
|
||||
};
|
||||
|
||||
jingleTransportListener = new JingleTransportListener() {
|
||||
public void transportEstablished(TransportCandidate local,
|
||||
TransportCandidate remote) {
|
||||
checkFullyEstablished();
|
||||
}
|
||||
|
||||
public void transportClosed(TransportCandidate cand) {
|
||||
}
|
||||
|
||||
public void transportClosedOnError(XMPPException e) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter in the pending state: install the listeners.
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
|
||||
*/
|
||||
public void eventEnter() {
|
||||
// Add the listeners to the sub-negotiators...
|
||||
addMediaListener(jingleMediaListener);
|
||||
addTransportListener(jingleTransportListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit of the state: remove the listeners.
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventExit()
|
||||
*/
|
||||
public void eventExit() {
|
||||
removeMediaListener(jingleMediaListener);
|
||||
removeTransportListener(jingleTransportListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the session has been fully accepted by all the
|
||||
* sub-negotiators and, in that case, send an "accept" message...
|
||||
*/
|
||||
private void checkFullyEstablished() {
|
||||
|
||||
if (isFullyEstablished()) {
|
||||
|
||||
PayloadType.Audio bestCommonAudioPt = getMediaNeg()
|
||||
.getBestCommonAudioPt();
|
||||
TransportCandidate bestRemoteCandidate = getTransportNeg()
|
||||
.getBestRemoteCandidate();
|
||||
|
||||
// Ok, send a packet saying that we accept this session
|
||||
// with the audio payload type and the transport
|
||||
// candidate
|
||||
Jingle jout = new Jingle(Jingle.Action.SESSIONACCEPT);
|
||||
jout.addDescription(new JingleContentDescription.Audio(
|
||||
new JinglePayloadType(bestCommonAudioPt)));
|
||||
jout.addTransport(getTransportNeg().getJingleTransport(
|
||||
bestRemoteCandidate));
|
||||
|
||||
// Send the "accept" and wait for the ACK
|
||||
addExpectedId(jout.getPacketID());
|
||||
sendFormattedJingle(jout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The other endpoint has finally accepted our invitation.
|
||||
*
|
||||
* @throws XMPPException
|
||||
*/
|
||||
public Jingle eventAccept(Jingle jin) throws XMPPException {
|
||||
|
||||
PayloadType acceptedPayloadType = null;
|
||||
TransportCandidate acceptedLocalCandidate = null;
|
||||
|
||||
// We process the "accepted" if we have finished the
|
||||
// sub-negotiators. Maybe this is not needed (ie, the other endpoint
|
||||
// can take the first valid transport candidate), but otherwise we
|
||||
// must cancel the negotiators...
|
||||
//
|
||||
if (isFullyEstablished()) {
|
||||
acceptedPayloadType = getAcceptedAudioPayloadType(jin);
|
||||
acceptedLocalCandidate = getAcceptedLocalCandidate(jin);
|
||||
|
||||
if (acceptedPayloadType != null && acceptedLocalCandidate != null) {
|
||||
if (acceptedPayloadType.equals(getMediaNeg().getBestCommonAudioPt())
|
||||
&& acceptedLocalCandidate.equals(getTransportNeg()
|
||||
.getAcceptedLocalCandidate())) {
|
||||
setState(active);
|
||||
}
|
||||
} else {
|
||||
throw new JingleException(JingleError.NEGOTIATION_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have received the Ack of our "accept"
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
|
||||
*/
|
||||
public Jingle eventAck(IQ iq) {
|
||||
setState(active);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The other endpoint wants to redirect this connection.
|
||||
*/
|
||||
public Jingle eventRedirect(Jingle jin) {
|
||||
String redirArg = null;
|
||||
|
||||
// TODO: parse the redirection parameters...
|
||||
|
||||
triggerSessionRedirect(redirArg);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The other endpoint has rejected our invitation.
|
||||
*
|
||||
* @throws XMPPException
|
||||
*/
|
||||
public Jingle eventTerminate(Jingle jin) throws XMPPException {
|
||||
triggerSessionDeclined(null);
|
||||
return super.eventTerminate(jin);
|
||||
}
|
||||
|
||||
/**
|
||||
* An error has occurred.
|
||||
*
|
||||
* @throws XMPPException
|
||||
*/
|
||||
public void eventError(IQ iq) throws XMPPException {
|
||||
triggerSessionClosedOnError(new XMPPException(iq.getError().getMessage()));
|
||||
super.eventError(iq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State when we have an established session.
|
||||
*/
|
||||
public class Active extends JingleNegotiator.State {
|
||||
public Active(JingleNegotiator neg) {
|
||||
super(neg);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a established session: notify the listeners
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
|
||||
*/
|
||||
public void eventEnter() {
|
||||
PayloadType.Audio bestCommonAudioPt = getMediaNeg().getBestCommonAudioPt();
|
||||
TransportCandidate bestRemoteCandidate = getTransportNeg()
|
||||
.getBestRemoteCandidate();
|
||||
TransportCandidate acceptedLocalCandidate = getTransportNeg()
|
||||
.getAcceptedLocalCandidate();
|
||||
|
||||
// Trigger the session established flag
|
||||
System.out.println("eventEntered");
|
||||
triggerSessionEstablished(bestCommonAudioPt, bestRemoteCandidate,
|
||||
acceptedLocalCandidate);
|
||||
|
||||
super.eventEnter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the connection.
|
||||
*
|
||||
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventTerminate(org.jivesoftware.smackx.packet.Jingle)
|
||||
*/
|
||||
public Jingle eventTerminate(Jingle jin) throws XMPPException {
|
||||
triggerSessionClosed("Closed Remotely");
|
||||
return super.eventTerminate(jin);
|
||||
}
|
||||
|
||||
/**
|
||||
* An error has occurred.
|
||||
*
|
||||
* @throws XMPPException
|
||||
*/
|
||||
public void eventError(IQ iq) throws XMPPException {
|
||||
triggerSessionClosedOnError(new XMPPException(iq.getError().getMessage()));
|
||||
super.eventError(iq);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue