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 * @param contents the contents. 071 * @param mi the jingle content info 072 * @param sid the sid. 073 */ 074 @SuppressWarnings("this-escape") 075 public Jingle(final List<JingleContent> contents, final JingleContentInfo mi, 076 final String sid) { 077 this(); 078 079 if (contents != null) { 080 this.contents.addAll(contents); 081 } 082 083 setContentInfo(mi); 084 setSid(sid); 085 086 // Set null all other fields in the packet 087 initiator = null; 088 responder = null; 089 action = null; 090 } 091 092 /** 093 * Constructor with a contents. 094 * 095 * @param content a content 096 */ 097 @SuppressWarnings("this-escape") 098 public Jingle(final JingleContent content) { 099 this(); 100 101 addContent(content); 102 103 // Set null all other fields in the packet 104 initiator = null; 105 responder = null; 106 107 // Some default values for the most common situation... 108 action = JingleActionEnum.UNKNOWN; 109 this.setType(IQ.Type.set); 110 } 111 112 /** 113 * Constructor with a content info. 114 * 115 * @param info The content info 116 */ 117 @SuppressWarnings("this-escape") 118 public Jingle(final JingleContentInfo info) { 119 this(); 120 121 setContentInfo(info); 122 123 // Set null all other fields in the packet 124 initiator = null; 125 responder = null; 126 127 // Some default values for the most common situation... 128 action = JingleActionEnum.UNKNOWN; 129 this.setType(IQ.Type.set); 130 } 131 132 /** 133 * A constructor where the action can be specified. 134 * 135 * @param action The action. 136 */ 137 @SuppressWarnings("this-escape") 138 public Jingle(final JingleActionEnum action) { 139 this(null, null, null); 140 this.action = action; 141 142 // In general, a Jingle with an action is used in a SET packet... 143 this.setType(IQ.Type.set); 144 } 145 146 /** 147 * A constructor where the session ID can be specified. 148 * 149 * @param sid The session ID related to the negotiation. 150 * @see #setSid(String) 151 */ 152 public Jingle(final String sid) { 153 this(null, null, sid); 154 } 155 156 /** 157 * The default constructor. 158 */ 159 public Jingle() { 160 super(NODENAME, NAMESPACE); 161 } 162 163 /** 164 * Set the session ID related to this session. The session ID is a unique 165 * identifier generated by the initiator. This should match the XML Nmtoken 166 * production so that XML character escaping is not needed for characters 167 * such as &. 168 * 169 * @param sid the session ID 170 */ 171 public final void setSid(final String sid) { 172 this.sid = sid; 173 } 174 175 /** 176 * Returns the session ID related to the session. The session ID is a unique 177 * identifier generated by the initiator. This should match the XML Nmtoken 178 * production so that XML character escaping is not needed for characters 179 * such as &. 180 * 181 * @return Returns the session ID related to the session. 182 * @see #setSid(String) 183 */ 184 public String getSid() { 185 186 return sid; 187 } 188 189 /** 190 * Jingle content info. 191 * 192 * @return the audioInfo. 193 */ 194 public JingleContentInfo getContentInfo() { 195 return contentInfo; 196 } 197 198 /** 199 * Set content info. 200 * 201 * @param contentInfo the audioInfo to set. 202 */ 203 public void setContentInfo(final JingleContentInfo contentInfo) { 204 this.contentInfo = contentInfo; 205 } 206 207 /** 208 * Get an iterator for the contents. 209 * 210 * @return the contents 211 */ 212 public Iterator<JingleContent> getContents() { 213 synchronized (contents) { 214 return Collections.unmodifiableList(new ArrayList<>(contents)).iterator(); 215 } 216 } 217 218 /** 219 * Get an iterator for the content. 220 * 221 * @return the contents 222 */ 223 public List<JingleContent> getContentsList() { 224 synchronized (contents) { 225 return new ArrayList<>(contents); 226 } 227 } 228 229 /** 230 * Add a new content. 231 * 232 * @param content the content to add 233 */ 234 public void addContent(final JingleContent content) { 235 if (content != null) { 236 synchronized (contents) { 237 contents.add(content); 238 } 239 } 240 } 241 242 /** 243 * Add a list of JingleContent elements. 244 * 245 * @param contentList the list of contents to add 246 */ 247 public void addContents(final List<JingleContent> contentList) { 248 if (contentList != null) { 249 synchronized (contents) { 250 contents.addAll(contentList); 251 } 252 } 253 } 254 255 /** 256 * Get the action specified in the packet. 257 * 258 * @return the action 259 */ 260 public JingleActionEnum getAction() { 261 return action; 262 } 263 264 /** 265 * Set the action in the packet. 266 * 267 * @param action the action to set 268 */ 269 public void setAction(final JingleActionEnum action) { 270 this.action = action; 271 } 272 273 /** 274 * Get the initiator. The initiator will be the full JID of the entity that 275 * has initiated the flow (which may be different to the "from" address in 276 * the IQ) 277 * 278 * @return the initiator 279 */ 280 public Jid getInitiator() { 281 return initiator; 282 } 283 284 /** 285 * Set the initiator. The initiator must 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 * @param initiator the initiator to set 290 */ 291 public void setInitiator(final Jid initiator) { 292 this.initiator = initiator; 293 } 294 295 /** 296 * Get the responder. The responder is the full JID of the entity that has 297 * replied to the initiation (which may be different to the "to" address in 298 * the IQ). 299 * 300 * @return the responder 301 */ 302 public Jid getResponder() { 303 return responder; 304 } 305 306 /** 307 * Set the responder. The responder must be the full JID of the entity that 308 * has replied to the initiation (which may be different to the "to" 309 * address in the IQ). 310 * 311 * @param resp the responder to set 312 */ 313 public void setResponder(final Jid resp) { 314 responder = resp; 315 } 316 317 /** 318 * Get a hash key for the session this stanza belongs to. 319 * 320 * @param sid The session id 321 * @param initiator The initiator 322 * @return A hash key 323 */ 324 public static int getSessionHash(final String sid, final Jid initiator) { 325 final int PRIME = 31; 326 int result = 1; 327 result = PRIME * result + (initiator == null ? 0 : initiator.hashCode()); 328 result = PRIME * result + (sid == null ? 0 : sid.hashCode()); 329 return result; 330 } 331 332 /** 333 * Return the XML representation of the packet. 334 * 335 * @return the XML string 336 */ 337 @Override 338 protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) { 339 if (getInitiator() != null) { 340 buf.append(" initiator=\"").append(getInitiator()).append('"'); 341 } 342 if (getResponder() != null) { 343 buf.append(" responder=\"").append(getResponder()).append('"'); 344 } 345 if (getAction() != null) { 346 buf.append(" action=\"").append(getAction().toString()).append('"'); 347 } 348 if (getSid() != null) { 349 buf.append(" sid=\"").append(getSid()).append('"'); 350 } 351 buf.append('>'); 352 353 synchronized (contents) { 354 for (JingleContent content : contents) { 355 buf.append(content.toXML()); 356 } 357 } 358 359 // and the same for audio jmf info 360 if (contentInfo != null) { 361 buf.append(contentInfo.toXML()); 362 } 363 364 return buf; 365 } 366}