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