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}