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; } }