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