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