001/** 002 * 003 * Copyright 2003-2007 Jive Software. 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 */ 017 018package org.jivesoftware.smackx.jingleold.packet; 019 020import org.jivesoftware.smack.packet.IQ; 021import org.jivesoftware.smackx.jingleold.JingleActionEnum; 022import org.jxmpp.jid.Jid; 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.Iterator; 027import java.util.List; 028 029/** 030 * An Jingle sub-packet, which is used by XMPP clients to exchange info like 031 * descriptions and transports. <p/> The following link summarizes the 032 * requirements of Jingle IM: <a 033 * href="http://www.xmpp.org/extensions/jep-0166.html">Valid tags</a>. 034 * <p/> 035 * <p/> Warning: this is an non-standard protocol documented by <a 036 * href="http://www.xmpp.org/extensions/jep-0166.html">XEP-166</a>. Because this is 037 * a non-standard protocol, it is subject to change. 038 * 039 * @author Alvaro Saurin 040 */ 041public class Jingle extends IQ { 042 043 // static 044 045 public static final String NAMESPACE = "urn:xmpp:tmp:jingle"; 046 047 public static final String NODENAME = "jingle"; 048 049 // non-static 050 051 private String sid; // The session id 052 053 private JingleActionEnum action; // The action associated to the Jingle 054 055 private Jid initiator; // The initiator as a "user@host/resource" 056 057 private Jid responder; // The responder 058 059 // Sub-elements of a Jingle object. 060 061 private final List<JingleContent> contents = new ArrayList<JingleContent>(); 062 063 private JingleContentInfo contentInfo; 064 065 /** 066 * A constructor where the main components can be initialized. 067 */ 068 public Jingle(final List<JingleContent> contents, final JingleContentInfo mi, 069 final String sid) { 070 this(); 071 072 if (contents != null) { 073 this.contents.addAll(contents); 074 } 075 076 setContentInfo(mi); 077 setSid(sid); 078 079 // Set null all other fields in the packet 080 initiator = null; 081 responder = null; 082 action = null; 083 } 084 085 /** 086 * Constructor with a contents. 087 * 088 * @param content a content 089 */ 090 public Jingle(final JingleContent content) { 091 this(); 092 093 addContent(content); 094 095 // Set null all other fields in the packet 096 initiator = null; 097 responder = null; 098 099 // Some default values for the most common situation... 100 action = JingleActionEnum.UNKNOWN; 101 this.setType(IQ.Type.set); 102 } 103 104 /** 105 * Constructor with a content info. 106 * 107 * @param info The content info 108 */ 109 public Jingle(final JingleContentInfo info) { 110 this(); 111 112 setContentInfo(info); 113 114 // Set null all other fields in the packet 115 initiator = null; 116 responder = null; 117 118 // Some default values for the most common situation... 119 action = JingleActionEnum.UNKNOWN; 120 this.setType(IQ.Type.set); 121 } 122 123 /** 124 * A constructor where the action can be specified. 125 * 126 * @param action The action. 127 */ 128 public Jingle(final JingleActionEnum action) { 129 this(null, null, null); 130 this.action = action; 131 132 // In general, a Jingle with an action is used in a SET packet... 133 this.setType(IQ.Type.set); 134 } 135 136 /** 137 * A constructor where the session ID can be specified. 138 * 139 * @param sid The session ID related to the negotiation. 140 * @see #setSid(String) 141 */ 142 public Jingle(final String sid) { 143 this(null, null, sid); 144 } 145 146 /** 147 * The default constructor. 148 */ 149 public Jingle() { 150 super(NODENAME, NAMESPACE); 151 } 152 153 /** 154 * Set the session ID related to this session. The session ID is a unique 155 * identifier generated by the initiator. This should match the XML Nmtoken 156 * production so that XML character escaping is not needed for characters 157 * such as &. 158 * 159 * @param sid the session ID 160 */ 161 public final void setSid(final String sid) { 162 this.sid = sid; 163 } 164 165 /** 166 * Returns the session ID related to the session. The session ID is a unique 167 * identifier generated by the initiator. This should match the XML Nmtoken 168 * production so that XML character escaping is not needed for characters 169 * such as &. 170 * 171 * @return Returns the session ID related to the session. 172 * @see #setSid(String) 173 */ 174 public String getSid() { 175 176 return sid; 177 } 178 179 /** 180 * Returns the XML element name of the extension sub-packet root element. 181 * Always returns "jingle" 182 * 183 * @return the XML element name of the stanza(/packet) extension. 184 */ 185 public static String getElementName() { 186 return NODENAME; 187 } 188 189 /** 190 * Returns the XML namespace of the extension sub-packet root element. 191 * 192 * @return the XML namespace of the stanza(/packet) extension. 193 */ 194 public static String getNamespace() { 195 return NAMESPACE; 196 } 197 198 /** 199 * Jingle content info. 200 * 201 * @return the audioInfo. 202 */ 203 public JingleContentInfo getContentInfo() { 204 return contentInfo; 205 } 206 207 /** 208 * Set content info. 209 * 210 * @param contentInfo the audioInfo to set. 211 */ 212 public void setContentInfo(final JingleContentInfo contentInfo) { 213 this.contentInfo = contentInfo; 214 } 215 216 /** 217 * Get an iterator for the contents. 218 * 219 * @return the contents 220 */ 221 public Iterator<JingleContent> getContents() { 222 synchronized (contents) { 223 return Collections.unmodifiableList(new ArrayList<JingleContent>(contents)).iterator(); 224 } 225 } 226 227 /** 228 * Get an iterator for the content. 229 * 230 * @return the contents 231 */ 232 public List<JingleContent> getContentsList() { 233 synchronized (contents) { 234 return new ArrayList<JingleContent>(contents); 235 } 236 } 237 238 /** 239 * Add a new content. 240 * 241 * @param content the content to add 242 */ 243 public void addContent(final JingleContent content) { 244 if (content != null) { 245 synchronized (contents) { 246 contents.add(content); 247 } 248 } 249 } 250 251 /** 252 * Add a list of JingleContent elements. 253 * 254 * @param contentList the list of contents to add 255 */ 256 public void addContents(final List<JingleContent> contentList) { 257 if (contentList != null) { 258 synchronized (contents) { 259 contents.addAll(contentList); 260 } 261 } 262 } 263 264 /** 265 * Get the action specified in the packet. 266 * 267 * @return the action 268 */ 269 public JingleActionEnum getAction() { 270 return action; 271 } 272 273 /** 274 * Set the action in the packet. 275 * 276 * @param action the action to set 277 */ 278 public void setAction(final JingleActionEnum action) { 279 this.action = action; 280 } 281 282 /** 283 * Get the initiator. The initiator will be the full JID of the entity that 284 * has initiated the flow (which may be different to the "from" address in 285 * the IQ) 286 * 287 * @return the initiator 288 */ 289 public Jid getInitiator() { 290 return initiator; 291 } 292 293 /** 294 * Set the initiator. The initiator must be the full JID of the entity that 295 * has initiated the flow (which may be different to the "from" address in 296 * the IQ) 297 * 298 * @param initiator the initiator to set 299 */ 300 public void setInitiator(final Jid initiator) { 301 this.initiator = initiator; 302 } 303 304 /** 305 * Get the responder. The responder is the full JID of the entity that has 306 * replied to the initiation (which may be different to the "to" addresss in 307 * the IQ). 308 * 309 * @return the responder 310 */ 311 public Jid getResponder() { 312 return responder; 313 } 314 315 /** 316 * Set the responder. The responder must be the full JID of the entity that 317 * has replied to the initiation (which may be different to the "to" 318 * addresss in the IQ). 319 * 320 * @param resp the responder to set 321 */ 322 public void setResponder(final Jid resp) { 323 responder = resp; 324 } 325 326 /** 327 * Get a hash key for the session this stanza(/packet) belongs to. 328 * 329 * @param sid The session id 330 * @param initiator The initiator 331 * @return A hash key 332 */ 333 public static int getSessionHash(final String sid, final Jid initiator) { 334 final int PRIME = 31; 335 int result = 1; 336 result = PRIME * result + (initiator == null ? 0 : initiator.hashCode()); 337 result = PRIME * result + (sid == null ? 0 : sid.hashCode()); 338 return result; 339 } 340 341 /** 342 * Return the XML representation of the packet. 343 * 344 * @return the XML string 345 */ 346 @Override 347 protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) { 348 if (getInitiator() != null) { 349 buf.append(" initiator=\"").append(getInitiator()).append('"'); 350 } 351 if (getResponder() != null) { 352 buf.append(" responder=\"").append(getResponder()).append('"'); 353 } 354 if (getAction() != null) { 355 buf.append(" action=\"").append(getAction().name()).append('"'); 356 } 357 if (getSid() != null) { 358 buf.append(" sid=\"").append(getSid()).append('"'); 359 } 360 buf.append('>'); 361 362 synchronized (contents) { 363 for (JingleContent content : contents) { 364 buf.append(content.toXML()); 365 } 366 } 367 368 // and the same for audio jmf info 369 if (contentInfo != null) { 370 buf.append(contentInfo.toXML()); 371 } 372 373 return buf; 374 } 375}