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