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 */
043public class JingleSessionStateUnknown extends JingleSessionState {
044    private static final Logger LOGGER = Logger.getLogger(JingleSessionStateUnknown.class.getName());
045
046    private static JingleSessionStateUnknown INSTANCE = null;
047
048    protected JingleSessionStateUnknown() {
049        // Prevent instantiation of the class.
050    }
051
052    /**
053     *  A thread-safe means of getting the one instance of this class.
054     *  @return The singleton instance of this class.
055     */
056    public static synchronized JingleSessionState getInstance() {
057        if (INSTANCE == null) {
058            INSTANCE = new JingleSessionStateUnknown();
059        }
060        return INSTANCE;
061    }
062
063    @Override
064    public void enter() {
065        // TODO Auto-generated method stub
066
067    }
068
069    @Override
070    public void exit() {
071        // TODO Auto-generated method stub
072
073    }
074
075    @Override
076    public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) throws SmackException, InterruptedException {
077        IQ response;
078
079        switch (action) {
080            case SESSION_INITIATE:
081                response = receiveSessionInitiateAction(session, jingle);
082                break;
083
084            case SESSION_TERMINATE:
085                response = receiveSessionTerminateAction(session, jingle);
086                break;
087
088            default:
089                // Anything other than session-initiate is an error.
090                response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
091                break;
092        }
093
094        return response;
095    }
096
097    /**
098     * In the UNKNOWN state we received a <session-initiate> action.
099     * This method processes that action.
100     * @throws SmackException
101     * @throws InterruptedException
102     */
103
104    private IQ receiveSessionInitiateAction(JingleSession session, Jingle inJingle) throws SmackException, InterruptedException {
105
106        IQ response;
107        boolean shouldAck = true;
108
109        // According to XEP-166 when we get a session-initiate we need to check for:
110        //      1. Initiator unknown
111        //      2. Receiver redirection
112        //      3. Does not support Jingle
113        //      4. Does not support any <description> formats
114        //      5. Does not support any <transport> formats
115        // If all of the above are OK then we send an IQ type = result to ACK the session-initiate.
116
117        // 1. Initiator unknown
118        // TODO
119
120        // 2. Receiver redirection
121        // TODO
122
123        // 3. Does not support Jingle
124        // Handled by Smack's lower layer.
125
126        // 4. Does not support any <description> formats
127        // TODO
128
129        // 5. Does not support any <transport> formats
130        // TODO
131
132        if (!shouldAck) {
133
134            response = session.createJingleError(inJingle, JingleError.NEGOTIATION_ERROR);
135
136        } else {
137
138            // Create the Ack
139            response = session.createAck(inJingle);
140
141            session.setSessionState(JingleSessionStatePending.getInstance());
142
143            // Now set up all of the initial content negotiators for the session.
144            for (JingleContent jingleContent : inJingle.getContentsList()) {
145                // First create the content negotiator for this <content> section.
146                ContentNegotiator contentNeg = new ContentNegotiator(session, jingleContent.getCreator(), jingleContent
147                        .getName());
148
149                // Get the media negotiator that goes with the <description> of this content.
150                JingleDescription jingleDescription = jingleContent.getDescription();
151
152                // Loop through each media manager looking for the ones that matches the incoming
153                // session-initiate <content> choices.
154                // (Set the first media manager as the default, so that in case things don't match we can still negotiate.)
155                JingleMediaManager chosenMediaManager = session.getMediaManagers().get(0);
156                for (JingleMediaManager mediaManager : session.getMediaManagers()) {
157                    boolean matches = true;
158                    for (PayloadType mediaPayloadType : mediaManager.getPayloads()) {
159                        for (PayloadType descPayloadType2 : jingleDescription.getPayloadTypesList()) {
160                            if (mediaPayloadType.getId() != descPayloadType2.getId()) {
161                                matches = false;
162                            }
163                        }
164                        if (matches) {
165                            chosenMediaManager = mediaManager;
166                        }
167                    }
168                }
169
170                // Create the media negotiator for this content description.
171                contentNeg.setMediaNegotiator(new MediaNegotiator(session, chosenMediaManager, jingleDescription
172                        .getPayloadTypesList(), contentNeg));
173
174                // For each transport type in this content, try to find the corresponding transport manager.
175                // Then create a transport negotiator for that transport.
176                for (JingleTransport jingleTransport : jingleContent.getJingleTransportsList()) {
177                    for (JingleMediaManager mediaManager : session.getMediaManagers()) {
178
179                        JingleTransportManager transportManager = mediaManager.getTransportManager();
180                        TransportResolver resolver = null;
181                        try {
182                            resolver = transportManager.getResolver(session);
183                        } catch (XMPPException e) {
184                            LOGGER.log(Level.WARNING, "exception", e);
185                        }
186
187                        if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
188                            contentNeg.setTransportNegotiator(new TransportNegotiator.RawUdp(session, resolver, contentNeg));
189                        }
190                        if (resolver.getType().equals(TransportResolver.Type.ice)) {
191                            contentNeg.setTransportNegotiator(new TransportNegotiator.Ice(session, resolver, contentNeg));
192                        }
193                    }
194                }
195
196                // Add the content negotiator to the session.
197                session.addContentNegotiator(contentNeg);
198            }
199
200            // Now setup to track the media negotiators, so that we know when (if) to send a session-accept.
201            session.setupListeners();
202        }
203
204        return response;
205    }
206
207    /**
208     * Receive and process the <session-terminate> action.
209     */
210    private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
211
212        // According to XEP-166 the only thing we can do is ack.
213        IQ response = session.createAck(jingle);
214
215        try {
216            session.terminate("Closed remotely");
217        } catch (Exception e) {
218            LOGGER.log(Level.WARNING, "exception", e);
219        }
220
221        return response;
222    }
223
224}