001/**
002 *
003 * Copyright the original author or authors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingleold;
018
019import java.util.logging.Level;
020import java.util.logging.Logger;
021
022import org.jivesoftware.smack.SmackException;
023import org.jivesoftware.smack.XMPPException;
024import org.jivesoftware.smack.packet.IQ;
025
026import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
027import org.jivesoftware.smackx.jingleold.media.MediaNegotiator;
028import org.jivesoftware.smackx.jingleold.media.PayloadType;
029import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
030import org.jivesoftware.smackx.jingleold.nat.TransportNegotiator;
031import org.jivesoftware.smackx.jingleold.nat.TransportResolver;
032import org.jivesoftware.smackx.jingleold.packet.Jingle;
033import org.jivesoftware.smackx.jingleold.packet.JingleContent;
034import org.jivesoftware.smackx.jingleold.packet.JingleDescription;
035import org.jivesoftware.smackx.jingleold.packet.JingleError;
036import org.jivesoftware.smackx.jingleold.packet.JingleTransport;
037
038/**
039 * Jingle.
040 *  @author Jeff Williams
041 *  @see JingleSessionState
042 */
043@SuppressWarnings("UnusedVariable")
044public class JingleSessionStateUnknown extends JingleSessionState {
045    private static final Logger LOGGER = Logger.getLogger(JingleSessionStateUnknown.class.getName());
046
047    private static JingleSessionStateUnknown INSTANCE = null;
048
049    protected JingleSessionStateUnknown() {
050        // Prevent instantiation of the class.
051    }
052
053    /**
054     *  A thread-safe means of getting the one instance of this class.
055     *  @return The singleton instance of this class.
056     */
057    public static synchronized JingleSessionState getInstance() {
058        if (INSTANCE == null) {
059            INSTANCE = new JingleSessionStateUnknown();
060        }
061        return INSTANCE;
062    }
063
064    @Override
065    public void enter() {
066        // TODO Auto-generated method stub
067
068    }
069
070    @Override
071    public void exit() {
072        // TODO Auto-generated method stub
073
074    }
075
076    @Override
077    public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) throws SmackException, InterruptedException {
078        IQ response;
079
080        switch (action) {
081            case SESSION_INITIATE:
082                response = receiveSessionInitiateAction(session, jingle);
083                break;
084
085            case SESSION_TERMINATE:
086                response = receiveSessionTerminateAction(session, jingle);
087                break;
088
089            default:
090                // Anything other than session-initiate is an error.
091                response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
092                break;
093        }
094
095        return response;
096    }
097
098    /**
099     * In the UNKNOWN state we received a <session-initiate> action.
100     * This method processes that action.
101     * @throws SmackException if Smack detected an exceptional situation.
102     * @throws InterruptedException if the calling thread was interrupted.
103     */
104    private static IQ receiveSessionInitiateAction(JingleSession session, Jingle inJingle)
105                    throws SmackException, InterruptedException {
106
107        IQ response;
108        boolean shouldAck = true;
109
110        // According to XEP-166 when we get a session-initiate we need to check for:
111        //      1. Initiator unknown
112        //      2. Receiver redirection
113        //      3. Does not support Jingle
114        //      4. Does not support any <description> formats
115        //      5. Does not support any <transport> formats
116        // If all of the above are OK then we send an IQ type = result to ACK the session-initiate.
117
118        // 1. Initiator unknown
119        // TODO
120
121        // 2. Receiver redirection
122        // TODO
123
124        // 3. Does not support Jingle
125        // Handled by Smack's lower layer.
126
127        // 4. Does not support any <description> formats
128        // TODO
129
130        // 5. Does not support any <transport> formats
131        // TODO
132
133        if (!shouldAck) {
134
135            response = session.createJingleError(inJingle, JingleError.NEGOTIATION_ERROR);
136
137        } else {
138
139            // Create the Ack
140            response = session.createAck(inJingle);
141
142            session.setSessionState(JingleSessionStatePending.getInstance());
143
144            // Now set up all of the initial content negotiators for the session.
145            for (JingleContent jingleContent : inJingle.getContentsList()) {
146                // First create the content negotiator for this <content> section.
147                ContentNegotiator contentNeg = new ContentNegotiator(session, jingleContent.getCreator(), jingleContent
148                        .getName());
149
150                // Get the media negotiator that goes with the <description> of this content.
151                JingleDescription jingleDescription = jingleContent.getDescription();
152
153                // Loop through each media manager looking for the ones that matches the incoming
154                // session-initiate <content> choices.
155                // (Set the first media manager as the default, so that in case things don't match we can still negotiate.)
156                JingleMediaManager chosenMediaManager = session.getMediaManagers().get(0);
157                for (JingleMediaManager mediaManager : session.getMediaManagers()) {
158                    boolean matches = true;
159                    for (PayloadType mediaPayloadType : mediaManager.getPayloads()) {
160                        for (PayloadType descPayloadType2 : jingleDescription.getPayloadTypesList()) {
161                            if (mediaPayloadType.getId() != descPayloadType2.getId()) {
162                                matches = false;
163                            }
164                        }
165                        if (matches) {
166                            chosenMediaManager = mediaManager;
167                        }
168                    }
169                }
170
171                // Create the media negotiator for this content description.
172                contentNeg.setMediaNegotiator(new MediaNegotiator(session, chosenMediaManager, jingleDescription
173                        .getPayloadTypesList(), contentNeg));
174
175                // For each transport type in this content, try to find the corresponding transport manager.
176                // Then create a transport negotiator for that transport.
177                for (JingleTransport jingleTransport : jingleContent.getJingleTransportsList()) {
178                    for (JingleMediaManager mediaManager : session.getMediaManagers()) {
179
180                        JingleTransportManager transportManager = mediaManager.getTransportManager();
181                        TransportResolver resolver = null;
182                        try {
183                            resolver = transportManager.getResolver(session);
184                        } catch (XMPPException e) {
185                            LOGGER.log(Level.WARNING, "exception", e);
186                        }
187
188                        if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
189                            contentNeg.setTransportNegotiator(new TransportNegotiator.RawUdp(session, resolver, contentNeg));
190                        }
191                        if (resolver.getType().equals(TransportResolver.Type.ice)) {
192                            contentNeg.setTransportNegotiator(new TransportNegotiator.Ice(session, resolver, contentNeg));
193                        }
194                    }
195                }
196
197                // Add the content negotiator to the session.
198                session.addContentNegotiator(contentNeg);
199            }
200
201            // Now setup to track the media negotiators, so that we know when (if) to send a session-accept.
202            session.setupListeners();
203        }
204
205        return response;
206    }
207
208    /**
209     * Receive and process the <session-terminate> action.
210     */
211    private static IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
212
213        // According to XEP-166 the only thing we can do is ack.
214        IQ response = session.createAck(jingle);
215
216        try {
217            session.terminate("Closed remotely");
218        } catch (Exception e) {
219            LOGGER.log(Level.WARNING, "exception", e);
220        }
221
222        return response;
223    }
224
225}