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