package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
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.JingleTransportManager;
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.JingleContent;
import org.jivesoftware.smackx.packet.JingleDescription;
import org.jivesoftware.smackx.packet.JingleError;
import org.jivesoftware.smackx.packet.JingleTransport;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateUnknown extends JingleSessionState {
private static JingleSessionStateUnknown INSTANCE = null;
/**
* A thread-safe means of getting the one instance of this class.
*
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateUnknown();
}
return INSTANCE;
}
protected JingleSessionStateUnknown() {
// Prevent instantiation of the class.
}
@Override
public void enter() {
// TODO Auto-generated method stub
}
@Override
public void exit() {
// TODO Auto-generated method stub
}
@Override
public IQ processJingle(JingleSession session, Jingle jingle,
JingleActionEnum action) {
IQ response = null;
switch (action) {
case SESSION_INITIATE:
response = receiveSessionInitiateAction(session, jingle);
break;
case SESSION_TERMINATE:
response = receiveSessionTerminateAction(session, jingle);
break;
default:
// Anything other than session-initiate is an error.
response = session.createJingleError(jingle,
JingleError.MALFORMED_STANZA);
break;
}
return response;
}
/**
* In the UNKNOWN state we received a <session-initiate> action. This method
* processes that action.
*/
private IQ receiveSessionInitiateAction(JingleSession session,
Jingle inJingle) {
IQ response = null;
final boolean shouldAck = true;
// According to XEP-166 when we get a session-initiate we need to check
// for:
// 1. Initiator unknown
// 2. Receiver redirection
// 3. Does not support Jingle
// 4. Does not support any <description> formats
// 5. Does not support any <transport> formats
// If all of the above are OK then we send an IQ type = result to ACK
// the session-initiate.
// 1. Initiator unknown
// TODO
// 2. Receiver redirection
// TODO
// 3. Does not support Jingle
// Handled by Smack's lower layer.
// 4. Does not support any <description> formats
// TODO
// 5. Does not support any <transport> formats
// TODO
if (!shouldAck) {
response = session.createJingleError(inJingle,
JingleError.NEGOTIATION_ERROR);
} else {
// Create the Ack
response = session.createAck(inJingle);
session.setSessionState(JingleSessionStatePending.getInstance());
// Now set up all of the initial content negotiators for the
// session.
for (final JingleContent jingleContent : inJingle.getContentsList()) {
// First create the content negotiator for this <content>
// section.
final ContentNegotiator contentNeg = new ContentNegotiator(
session, jingleContent.getCreator(),
jingleContent.getName());
// Get the media negotiator that goes with the <description> of
// this content.
final JingleDescription jingleDescription = jingleContent
.getDescription();
// Loop through each media manager looking for the ones that
// matches the incoming
// session-initiate <content> choices.
// (Set the first media manager as the default, so that in case
// things don't match we can still negotiate.)
JingleMediaManager chosenMediaManager = session
.getMediaManagers().get(0);
for (final JingleMediaManager mediaManager : session
.getMediaManagers()) {
boolean matches = true;
for (final PayloadType mediaPayloadType : mediaManager
.getPayloads()) {
for (final PayloadType descPayloadType2 : jingleDescription
.getPayloadTypesList()) {
if (mediaPayloadType.getId() != descPayloadType2
.getId()) {
matches = false;
}
}
if (matches) {
chosenMediaManager = mediaManager;
}
}
}
// Create the media negotiator for this content description.
contentNeg.setMediaNegotiator(new MediaNegotiator(session,
chosenMediaManager, jingleDescription
.getPayloadTypesList(), contentNeg));
// For each transport type in this content, try to find the
// corresponding transport manager.
// Then create a transport negotiator for that transport.
for (final JingleTransport jingleTransport : jingleContent
.getJingleTransportsList()) {
for (final JingleMediaManager mediaManager : session
.getMediaManagers()) {
final JingleTransportManager transportManager = mediaManager
.getTransportManager();
TransportResolver resolver = null;
try {
resolver = transportManager.getResolver(session);
} catch (final XMPPException e) {
e.printStackTrace();
}
if (resolver.getType().equals(
TransportResolver.Type.rawupd)) {
contentNeg
.setTransportNegotiator(new TransportNegotiator.RawUdp(
session, resolver, contentNeg));
}
if (resolver.getType().equals(
TransportResolver.Type.ice)) {
contentNeg
.setTransportNegotiator(new TransportNegotiator.Ice(
session, resolver, contentNeg));
}
}
}
// Add the content negotiator to the session.
session.addContentNegotiator(contentNeg);
}
// Now setup to track the media negotiators, so that we know when
// (if) to send a session-accept.
session.setupListeners();
}
return response;
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session,
Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
final IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (final XMPPException e) {
e.printStackTrace();
}
return response;
}
}