/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
import java.math.*;
import java.security.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.packet.*;
/**
* A straightforward extension of the IQ. A <tt>JingleIQ</tt> object is created
* by smack via the {@link JingleIQProvider}. It contains all the information
* extracted from a <tt>jingle</tt> IQ.
*
* @author Emil Ivov
*/
public class JingleIQ extends IQ
{
/**
* The name space that jingle belongs to.
*/
public static final String NAMESPACE = "urn:xmpp:jingle:1";
/**
* The name of the element that contains the jingle data.
*/
public static final String ELEMENT_NAME = "jingle";
/**
* The name of the argument that contains the jingle action value.
*/
public static final String ACTION_ATTR_NAME = "action";
/**
* The name of the argument that contains the "initiator" jid.
*/
public static final String INITIATOR_ATTR_NAME = "initiator";
/**
* The name of the argument that contains the "responder" jid.
*/
public static final String RESPONDER_ATTR_NAME = "responder";
/**
* The name of the argument that contains the session id.
*/
public static final String SID_ATTR_NAME = "sid";
/**
* The <tt>JingleAction</tt> that describes the purpose of this
* <tt>jingle</tt> element.
*/
private JingleAction action;
/**
* The full JID of the entity that has initiated the session flow. Only
* present when the <tt>JingleAction</tt> is <tt>session-accept</tt>.
*/
private String initiator;
/**
* The full JID of the entity that replies to a Jingle initiation. The
* <tt>responder</tt> can be different from the 'to' address on the IQ-set.
* Only present when the <tt>JingleAction</tt> is <tt>session-accept</tt>.
*/
private String responder;
/**
* The ID of the Jingle session that this IQ belongs to. XEP-0167: A sid is
* a random session identifier generated by the initiator, which
* effectively maps to the local-part of a SIP "Call-ID" parameter
*/
private String sid;
/**
* The <tt>reason</tt> extension in a <tt>jingle</tt> IQ providers machine
* and possibly human-readable information about the reason for the action.
*/
private ReasonPacketExtension reason;
/**
* Any session info extensions that this packet may contain.
*/
private SessionInfoPacketExtension sessionInfo;
/**
* The list of "content" elements included in this IQ.
*/
private final List<ContentPacketExtension> contentList
= new ArrayList<ContentPacketExtension>();
/**
* Returns the XML string of this Jingle IQ's "section" sub-element.
*
* Extensions of this class must override this method.
*
* @return the child element section of the IQ XML.
*/
@Override
public String getChildElementXML()
{
StringBuilder bldr = new StringBuilder("<" + ELEMENT_NAME);
bldr.append(" xmlns='" + NAMESPACE + "'");
bldr.append(" " + ACTION_ATTR_NAME + "='" + getAction() + "'");
if( initiator != null)
bldr.append(" " + INITIATOR_ATTR_NAME
+ "='" + getInitiator() + "'");
if( responder != null)
bldr.append(" " + RESPONDER_ATTR_NAME
+ "='" + getResponder() + "'");
bldr.append(" " + SID_ATTR_NAME
+ "='" + getSID() + "'");
CharSequence extensionsXMLSeq = getExtensionsXML();
String extensionsXML = extensionsXMLSeq.toString();
if ((contentList.size() == 0)
&& (reason == null)
&& (sessionInfo == null)
&& ((extensionsXML == null) || (extensionsXML.length() == 0)))
{
bldr.append("/>");
}
else
{
bldr.append(">");//it is possible to have empty jingle elements
//content
for(ContentPacketExtension cpe : contentList)
{
bldr.append(cpe.toXML());
}
//reason
if (reason != null)
bldr.append(reason.toXML());
//session-info
//XXX: this is RTP specific so we should probably handle it in a
//subclass
if (sessionInfo != null)
bldr.append(sessionInfo.toXML());
// extensions
if ((extensionsXML != null) && (extensionsXML.length() != 0))
bldr.append(extensionsXML);
bldr.append("</" + ELEMENT_NAME + ">");
}
return bldr.toString();
}
/**
* Sets this element's session ID value. A "sid" is a random session
* identifier generated by the initiator, which effectively maps to the
* local-part of a SIP "Call-ID" parameter.
*
* @param sid the session ID to set
*/
public void setSID(String sid)
{
this.sid = sid;
}
/**
* Returns this element's session ID value. A "sid" is a random session
* identifier generated by the initiator, which effectively maps to the
* local-part of a SIP "Call-ID" parameter.
*
*
* @return this element's session ID.
*/
public String getSID()
{
return sid;
}
/**
* Generates a random <tt>String</tt> usable as a jingle session ID.
*
* @return a newly generated random sid <tt>String</tt>
*/
public static String generateSID()
{
return new BigInteger(64, new SecureRandom()).toString(32);
}
/**
* Sets the full JID of the entity that replies to a Jingle initiation. The
* <tt>responder</tt> can be different from the 'to' address on the IQ-set.
* Only present when the <tt>JingleAction</tt> is <tt>session-accept</tt>.
*
* @param responder the full JID of the session <tt>responder</tt>.
*/
public void setResponder(String responder)
{
this.responder = responder;
}
/**
* Returns the full JID of the entity that replies to a Jingle initiation.
* The <tt>responder</tt> can be different from the 'to' address on the
* IQ-set. Only present when the <tt>JingleAction</tt> is
* <tt>session-accept</tt>.
*
* @return the full JID of the session <tt>responder</tt>
*/
public String getResponder()
{
return responder;
}
/**
* Sets the full JID of the entity that has initiated the session flow. Only
* present when the <tt>JingleAction</tt> is <tt>session-accept</tt>.
*
* @param initiator the full JID of the initiator.
*/
public void setInitiator(String initiator)
{
this.initiator = initiator;
}
/**
* Returns the full JID of the entity that has initiated the session flow.
* Only present when the <tt>JingleAction</tt> is <tt>session-accept</tt>.
*
* @return the full JID of the initiator.
*/
public String getInitiator()
{
return initiator;
}
/**
* Sets the value of this element's <tt>action</tt> attribute. The value of
* the 'action' attribute MUST be one of the values enumerated here. If an
* entity receives a value not defined here, it MUST ignore the attribute
* and MUST return a <tt>bad-request</tt> error to the sender. There is no
* default value for the 'action' attribute.
*
* @param action the value of the <tt>action</tt> attribute.
*/
public void setAction(JingleAction action)
{
this.action = action;
}
/**
* Returns the value of this element's <tt>action</tt> attribute. The value
* of the 'action' attribute MUST be one of the values enumerated here. If
* an entity receives a value not defined here, it MUST ignore the attribute
* and MUST return a <tt>bad-request</tt> error to the sender. There is no
* default value for the 'action' attribute.
*
* @return the value of the <tt>action</tt> attribute.
*/
public JingleAction getAction()
{
return action;
}
/**
* Specifies this IQ's <tt>reason</tt> extension. The <tt>reason</tt>
* extension in a <tt>jingle</tt> IQ provides machine and possibly human
* -readable information about the reason for the action.
*
* @param reason this IQ's <tt>reason</tt> extension.
*/
public void setReason(ReasonPacketExtension reason)
{
this.reason = reason;
}
/**
* Returns this IQ's <tt>reason</tt> extension. The <tt>reason</tt>
* extension in a <tt>jingle</tt> IQ provides machine and possibly human
* -readable information about the reason for the action.
*
* @return this IQ's <tt>reason</tt> extension.
*/
public ReasonPacketExtension getReason()
{
return reason;
}
/**
* Returns a reference (and not a copy so be careful how you are handling
* it) of this element's content list.
*
* @return a reference to this element's content list.
*/
public List<ContentPacketExtension> getContentList()
{
synchronized(contentList)
{
return new ArrayList<ContentPacketExtension>(contentList);
}
}
/**
* Adds <tt>contentPacket</tt> to this IQ's content list.
*
* @param contentPacket the content packet extension we'd like to add to
* this element's content list.
*/
public void addContent(ContentPacketExtension contentPacket)
{
synchronized(contentList)
{
this.contentList.add(contentPacket);
}
}
/**
* Determines if this packet contains a <tt>content</tt> with a child
* matching the specified <tt>contentType</tt>. The method is meant to allow
* to easily determine the purpose of a jingle IQ. A telephony initiation
* IQ would for example contain a <tt>content</tt> element of type {@link
* RtpDescriptionPacketExtension}.
*
* @param contentType the type of the content child we are looking for.
*
* @return <tt>true</tt> if one of this IQ's <tt>content</tt> elements
* contains a child of the specified <tt>contentType</tt> and <tt>false</tt>
* otherwise.
*/
public boolean containsContentChildOfType(
Class<? extends PacketExtension> contentType)
{
if(getContentForType(contentType) != null)
return true;
return false;
}
/**
* Determines if this packet contains a <tt>content</tt> with a child
* matching the specified <tt>contentType</tt> and returns it. Returns
* <tt>null</tt> otherwise. The method is meant to allow to easily extract
* specific IQ elements like an RTP description for example.
*
* @param contentType the type of the content child we are looking for.
*
* @return a reference to the content element that has a child of the
* specified <tt>contentType</tt> or <tt>null</tt> if no such child was
* found.
*/
public ContentPacketExtension getContentForType(
Class<? extends PacketExtension> contentType)
{
synchronized(contentList)
{
for(ContentPacketExtension content : contentList)
{
PacketExtension child
= content.getFirstChildOfType(contentType);
if(child != null)
return content;
}
}
return null;
}
/**
* Finds <tt>ContentPacketExtension</tt> that matches given
* <tt>contentName</tt>.
* @param contentName the name of the content for which extension will be
* returned
* @return <tt>ContentPacketExtension</tt> that matches given
* <tt>contentName</tt> or <tt>null</tt> if not found.
*/
public ContentPacketExtension getContentByName(String contentName)
{
synchronized(contentList)
{
for(ContentPacketExtension content : contentList)
{
if (contentName.equals(content.getName()))
{
return content;
}
}
}
return null;
}
/**
* Sets <tt>si</tt> as the session info extension for this packet.
*
* @param si a {@link SessionInfoPacketExtension} that we'd like to add
* here.
*/
public void setSessionInfo(SessionInfoPacketExtension si)
{
this.sessionInfo = si;
}
/**
* Returns a {@link SessionInfoPacketExtension} if this <tt>JingleIQ</tt>
* contains one and <tt>null</tt> otherwise.
*
* @return a {@link SessionInfoPacketExtension} if this <tt>JingleIQ</tt>
* contains one and <tt>null</tt> otherwise.
*/
public SessionInfoPacketExtension getSessionInfo()
{
return this.sessionInfo;
}
}