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}