/* * 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.rayo; import net.java.sip.communicator.impl.protocol.jabber.extensions.*; import org.jitsi.util.*; import org.jivesoftware.smack.packet.*; import org.jivesoftware.smack.provider.*; import org.xmlpull.v1.*; import java.util.*; /** * Provider handles parsing of Rayo IQ stanzas and converting objects back to * their XML representation. * * FIXME: implements only the minimum required to start and hang up a call * * @author Pawel Domas */ public class RayoIqProvider implements IQProvider { /** * Rayo namespace. */ public final static String NAMESPACE = "urn:xmpp:rayo:1"; /** * Registers this IQ provider into given <tt>ProviderManager</tt>. * @param providerManager the <tt>ProviderManager</tt> to which this * instance wil be bound to. */ public void registerRayoIQs(ProviderManager providerManager) { // <dial> providerManager.addIQProvider( DialIq.ELEMENT_NAME, NAMESPACE, this); // <ref> providerManager.addIQProvider( RefIq.ELEMENT_NAME, NAMESPACE, this); // <hangup> providerManager.addIQProvider( HangUp.ELEMENT_NAME, NAMESPACE, this); // <end> presence extension providerManager.addExtensionProvider( EndExtension.ELEMENT_NAME, NAMESPACE, new DefaultPacketExtensionProvider<EndExtension>( EndExtension.class)); // <header> extension providerManager.addExtensionProvider( HeaderExtension.ELEMENT_NAME, "", new DefaultPacketExtensionProvider<HeaderExtension>( HeaderExtension.class)); } /** * {@inheritDoc} */ @Override public IQ parseIQ(XmlPullParser parser) throws Exception { String namespace = parser.getNamespace(); // Check the namespace if (!NAMESPACE.equals(namespace)) { return null; } String rootElement = parser.getName(); RayoIq iq; DialIq dial; RefIq ref; //End end = null; if (DialIq.ELEMENT_NAME.equals(rootElement)) { iq = dial = new DialIq(); String src = parser.getAttributeValue("", DialIq.SRC_ATTR_NAME); String dst = parser.getAttributeValue("", DialIq.DST_ATTR_NAME); // Destination is mandatory if (StringUtils.isNullOrEmpty(dst)) return null; dial.setSource(src); dial.setDestination(dst); } else if (RefIq.ELEMENT_NAME.equals(rootElement)) { iq = ref = new RefIq(); String uri = parser.getAttributeValue("", RefIq.URI_ATTR_NAME); if (StringUtils.isNullOrEmpty(uri)) return null; ref.setUri(uri); } else if (HangUp.ELEMENT_NAME.equals(rootElement)) { iq = new HangUp(); } /*else if (End.ELEMENT_NAME.equals(rootElement)) { iq = end = new End(); }*/ else { return null; } boolean done = false; HeaderExtension header = null; //ReasonExtension reason = null; while (!done) { switch (parser.next()) { case XmlPullParser.END_TAG: { String name = parser.getName(); if (rootElement.equals(name)) { done = true; } else if (HeaderExtension.ELEMENT_NAME.equals( name)) { if (header != null) { iq.addExtension(header); header = null; } } /*else if (End.isValidReason(name)) { if (end != null && reason != null) { end.setReason(reason); reason = null; } }*/ break; } case XmlPullParser.START_TAG: { String name = parser.getName(); if (HeaderExtension.ELEMENT_NAME.equals(name)) { header = new HeaderExtension(); String nameAttr = parser.getAttributeValue( "", HeaderExtension.NAME_ATTR_NAME); header.setName(nameAttr); String valueAttr = parser.getAttributeValue( "", HeaderExtension.VALUE_ATTR_NAME); header.setValue(valueAttr); } /*else if (End.isValidReason(name)) { reason = new ReasonPacketExtension(name); String platformCode = parser.getAttributeValue( "", ReasonPacketExtension.PLATFORM_CODE_ATTRIBUTE); if (!StringUtils.isNullOrEmpty(platformCode)) { reason.setPlatformCode(platformCode); } }*/ break; } case XmlPullParser.TEXT: { // Parse some text here break; } } } return iq; } /** * Base class for all Ray IQs. Takes care of <header /> extension handling * as well as other functions shared by all IQs. */ public static abstract class RayoIq extends IQ { /** * The XML element name that will be used. */ private final String elementName; /** * Creates new instance of <tt>RayoIq</tt>. * * @param elementName the name of XML element that will be used. */ protected RayoIq(String elementName) { this.elementName = elementName; } /** * Implementing classes should print their attributes if any in XML * format to given <tt>out</tt> <tt>StringBuilder</tt>. * @param out the <tt>StringBuilder</tt> instance used to construct XML * representation of this element. */ protected abstract void printAttributes(StringBuilder out); /** * {@inheritDoc} */ @Override public String getChildElementXML() { StringBuilder xml = new StringBuilder(); xml.append('<').append(elementName); xml.append(" xmlns='").append(NAMESPACE).append("' "); printAttributes(xml); Collection<PacketExtension> extensions = getExtensions(); if (extensions.size() > 0) { xml.append(">"); for (PacketExtension extension : extensions) { xml.append(extension.toXML()); } xml.append("</").append(elementName).append(">"); } else { xml.append("/>"); } return xml.toString(); } /** * Returns value of the header extension with given <tt>name</tt> * (if any). * @param name the name of header extension which value we want to * retrieve. * @return value of header extension with given <tt>name</tt> if it * exists or <tt>null</tt> otherwise. */ public String getHeader(String name) { HeaderExtension header = findHeader(name); return header != null ? header.getValue() : null; } private HeaderExtension findHeader(String name) { for(PacketExtension ext: getExtensions()) { if (ext instanceof HeaderExtension) { HeaderExtension header = (HeaderExtension) ext; if(name.equals(header.getName())) return header; } } return null; } /** * Adds 'header' extension to this Rayo IQ with given name and value * attributes. * @param name the attribute name of the 'header' extension to be added. * @param value the 'value' attribute of the 'header' extension that * will be added to this IQ. */ public void setHeader(String name, String value) { HeaderExtension headerExt = findHeader(name); if (headerExt == null) { headerExt = new HeaderExtension(); headerExt.setName(name); addExtension(headerExt); } headerExt.setValue(value); } } /** * The 'dial' IQ used to initiate new outgoing call session in Rayo * protocol. */ public static class DialIq extends RayoIq { /** * The name of XML element for this IQ. */ public static final String ELEMENT_NAME = "dial"; /** * The name of source URI/address attribute. Referred as "source" to * avoid confusion with "getFrom" and "setFrom" in {@link IQ} class. */ public static final String SRC_ATTR_NAME = "from"; /** * The name of destination URI/address attribute. Referred as "source" * to avoid confusion with "getFrom" and "setFrom" in {@link IQ} class. */ public static final String DST_ATTR_NAME = "to"; /** * Source URI/address. */ private String source; /** * Destination URI/address. */ private String destination; /** * Creates new instance of <tt>DialIq</tt>. */ public DialIq() { super(DialIq.ELEMENT_NAME); } /** * Creates new <tt>DialIq</tt> for given source and destination * addresses. * @param to the destination address/call URI to be used. * @param from the source address that will be set on * new <tt>DialIq</tt> instance. * @return new <tt>DialIq</tt> parametrized with given source and * destination addresses. */ public static DialIq create(String to, String from) { DialIq dialIq = new DialIq(); dialIq.setSource(from); dialIq.setDestination(to); return dialIq; } /** * Return source address value set on this <tt>DialIq</tt>. * @return source address value of this <tt>DialIq</tt>. */ public String getSource() { return source; } /** * Sets new source address value on this <tt>DialIq</tt>. * @param source the new source address value to be set. */ public void setSource(String source) { this.source = source; } /** * Returns destination address/call URI associated with this instance. * @return destination address/call URI associated with this instance. */ public String getDestination() { return destination; } /** * Sets new destination address/call URI on this <tt>DialIq</tt>. * @param destination the new destination address/call URI to set. */ public void setDestination(String destination) { this.destination = destination; } /** * {@inheritDoc} */ @Override protected void printAttributes(StringBuilder out) { String src = getSource(); if (!StringUtils.isNullOrEmpty(src)) out.append(SRC_ATTR_NAME).append("='") .append(src).append("' "); String dst = getDestination(); if (!StringUtils.isNullOrEmpty(dst)) out.append(DST_ATTR_NAME).append("='") .append(dst).append("' "); } } /** * Rayo 'ref' IQ sent by the server as a reply to 'dial' request. Holds * created call's resource in 'uri' attribute. */ public static class RefIq extends RayoIq { /** * XML element name of <tt>RefIq</tt>. */ public static final String ELEMENT_NAME = "ref"; /** * Name of the URI attribute that stores call resource reference. */ public static final String URI_ATTR_NAME = "uri"; /** * Call resource/uri reference. */ private String uri; /** * Creates new <tt>RefIq</tt>. */ protected RefIq() { super(RefIq.ELEMENT_NAME); } /** * Creates new <tt>RefIq</tt> parametrized with given call <tt>uri</tt>. * @param uri the call URI to be set on newly created <tt>RefIq</tt>. * @return new <tt>RefIq</tt> parametrized with given call <tt>uri</tt>. */ public static RefIq create(String uri) { RefIq refIq = new RefIq(); refIq.setUri(uri); return refIq; } /** * Creates result <tt>RefIq</tt> for given <tt>requestIq</tt> * parametrized with given call <tt>uri</tt>. * @param requestIq the request IQ which 'from', 'to' and 'id' * attributes will be used for constructing result IQ. * @param uri the call URI that will be included in newly created * <tt>RefIq</tt>. * @return result <tt>RefIq</tt> for given <tt>requestIq</tt> * parametrized with given call <tt>uri</tt>. */ public static RefIq createResult(IQ requestIq, String uri) { RefIq refIq = create(uri); refIq.setType(IQ.Type.RESULT); refIq.setPacketID(requestIq.getPacketID()); refIq.setFrom(requestIq.getTo()); refIq.setTo(requestIq.getFrom()); return refIq; } /** * {@inheritDoc} */ @Override protected void printAttributes(StringBuilder out) { String uri = getUri(); if (!StringUtils.isNullOrEmpty(uri)) out.append(URI_ATTR_NAME).append("='") .append(uri).append("' "); } /** * Sets given call <tt>uri</tt> value on this instance. * @param uri the call <tt>uri</tt> to be stored in this instance. */ public void setUri(String uri) { this.uri = uri; } /** * Returns call URI held by this instance. * @return the call URI held by this instance. */ public String getUri() { return uri; } } /** * Rayo hangup IQ is sent by the controlling agent to tell the server that * call whose resource is mentioned in IQ's 'to' attribute should be * terminated. Server immediately replies with result IQ which means that * hangup operation is now scheduled. After it is actually executed presence * indication with {@link EndExtension} is sent through the presence to * confirm the operation. */ public static class HangUp extends RayoIq { /** * The name of 'hangup' element. */ public static final String ELEMENT_NAME = "hangup"; /** * Creates new instance of <tt>HangUp</tt> IQ. */ protected HangUp() { super(ELEMENT_NAME); } /** * Creates new, parametrized instance of {@link HangUp} IQ. * @param from source JID. * @param to the destination address/call URI to be ended by this IQ. * @return new, parametrized instance of {@link HangUp} IQ. */ public static HangUp create(String from, String to) { HangUp hangUp = new HangUp(); hangUp.setFrom(from); hangUp.setTo(to); hangUp.setType(Type.SET); return hangUp; } @Override protected void printAttributes(StringBuilder out){ } } }