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}