From 54186891516be45b3ab67fedb74e36df025471e7 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Fri, 8 Aug 2014 15:49:38 +0200 Subject: [PATCH] Adds Rayo IQ provider to jabber extensions. --- .../jabber/extensions/rayo/EndExtension.java | 104 ++++ .../extensions/rayo/HeaderExtension.java | 78 +++ .../extensions/rayo/RayoIqProvider.java | 587 ++++++++++++++++++ .../extensions/rayo/ReasonExtension.java | 88 +++ 4 files changed, 857 insertions(+) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/EndExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/HeaderExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/RayoIqProvider.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/ReasonExtension.java diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/EndExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/EndExtension.java new file mode 100644 index 000000000..affb7f7cc --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/EndExtension.java @@ -0,0 +1,104 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.rayo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; +import org.jivesoftware.smack.packet.*; + +/** + * 'End' Rayo packet extension used to notify the client about call ended event. + * + * @author Pawel Domas + */ +public class EndExtension + extends AbstractPacketExtension +{ + /** + * XML element name of this extension. + */ + public static final String ELEMENT_NAME = "end"; + + /** + * End reason. + */ + private ReasonExtension reason; + + /** + * Creates new instance. + */ + protected EndExtension() + { + super(RayoIqProvider.NAMESPACE, ELEMENT_NAME); + } + + /** + * Checks if given elementName is valid end reason element. + * @param elementName the XML element name to check. + * @return true if given elementName is valid end reason + * element. + */ + public static boolean isValidReason(String elementName) + { + return ReasonExtension.BUSY.equals(elementName) + || ReasonExtension.ERROR.equals(elementName) + || ReasonExtension.HANGUP.equals(elementName) + || ReasonExtension.HANGUP_COMMND.equals(elementName) + || ReasonExtension.REJECTED.equals(elementName) + || ReasonExtension.TIMEOUT.equals(elementName); + } + + + /** + * Returns {@link ReasonExtension} associated with this instance. + * @return {@link ReasonExtension} associated with this instance. + */ + public ReasonExtension getReason() + { + return reason; + } + + /** + * Sets new {@link ReasonExtension} for this EndExtension instance. + * @param newReason the new {@link ReasonExtension} to set. + */ + public void setReason(ReasonExtension newReason) + { + if (this.reason != null) + { + getChildExtensions().remove(this.reason); + } + + this.reason = newReason; + + addChildExtension(newReason); + } + + /** + * Creates 'Presence' packet containing call ended Rayo notification that + * contains specified end reason. + * @param from source JID of this event. + * @param to destination JID. + * @param reason call end reason string. One of {@link ReasonExtension} + * static constants. + * @return 'Presence' packet containing call ended Rayo notification. + */ + public static Presence createEnd(String from, String to, String reason) + { + Presence presence = new Presence(Presence.Type.unavailable); + + presence.setFrom(from); + + presence.setTo(to); + + EndExtension end = new EndExtension(); + end.setReason(new ReasonExtension(reason)); + + presence.addExtension(end); + + return presence; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/HeaderExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/HeaderExtension.java new file mode 100644 index 000000000..08546531a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/HeaderExtension.java @@ -0,0 +1,78 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.rayo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Header packet extension optionally included in {@link RayoIqProvider.RayoIq}. + * Holds 'name' and 'value' attributes. + * + * @author Pawel Domas + */ +public class HeaderExtension + extends AbstractPacketExtension +{ + /** + * XML element name. + */ + public static final String ELEMENT_NAME = "header"; + + /** + * The name of 'name' attribute. + */ + public static final String NAME_ATTR_NAME = "name"; + + /** + * The name of 'value' attribute. + */ + public static final String VALUE_ATTR_NAME = "value"; + + /** + * Creates new instance of HeaderPacketExtension. + */ + public HeaderExtension() + { + super(null, ELEMENT_NAME); + } + + /** + * Return the value of 'name' attribute. + * @return the value of 'name' attribute. + */ + public String getName() + { + return getAttributeAsString(NAME_ATTR_NAME); + } + + /** + * Sets new value for 'name' attribute of this extension. + * @param name the new value to set for 'name' attribute. + */ + public void setName(String name) + { + setAttribute(NAME_ATTR_NAME, name); + } + + /** + * Returns the value of 'value' attribute. + * @return the value of 'value' attribute. + */ + public String getValue() + { + return getAttributeAsString(VALUE_ATTR_NAME); + } + + /** + * Sets new value for the 'value' attribute. + * @param value new value for the 'value' attribute to set. + */ + public void setValue(String value) + { + setAttribute(VALUE_ATTR_NAME, value); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/RayoIqProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/RayoIqProvider.java new file mode 100644 index 000000000..4d94ed9e8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/RayoIqProvider.java @@ -0,0 +1,587 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +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 ProviderManager. + * @param providerManager the ProviderManager to which this + * instance wil be bound to. + */ + public void registerRayoIQs(ProviderManager providerManager) + { + // + providerManager.addIQProvider( + DialIq.ELEMENT_NAME, + NAMESPACE, + this); + + // + providerManager.addIQProvider( + RefIq.ELEMENT_NAME, + NAMESPACE, + this); + + // + providerManager.addIQProvider( + HangUp.ELEMENT_NAME, + NAMESPACE, + this); + + // presence extension + providerManager.addExtensionProvider( + EndExtension.ELEMENT_NAME, + NAMESPACE, + new DefaultPacketExtensionProvider( + EndExtension.class)); + + //
extension + providerManager.addExtensionProvider( + HeaderExtension.ELEMENT_NAME, + "", + new DefaultPacketExtensionProvider( + 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
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 RayoIq. + * + * @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 out StringBuilder. + * @param out the StringBuilder 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 extensions = getExtensions(); + if (extensions.size() > 0) + { + xml.append(">"); + for (PacketExtension extension : extensions) + { + xml.append(extension.toXML()); + } + xml.append(""); + } + else + { + xml.append("/>"); + } + + return xml.toString(); + } + + /** + * Returns value of the header extension with given name + * (if any). + * @param name the name of header extension which value we want to + * retrieve. + * @return value of header extension with given name if it + * exists or null 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 DialIq. + */ + public DialIq() + { + super(DialIq.ELEMENT_NAME); + } + + /** + * Creates new DialIq 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 DialIq instance. + * @return new DialIq 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 DialIq. + * @return source address value of this DialIq. + */ + public String getSource() + { + return source; + } + + /** + * Sets new source address value on this DialIq. + * @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 DialIq. + * @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 RefIq. + */ + 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 RefIq. + */ + protected RefIq() + { + super(RefIq.ELEMENT_NAME); + } + + /** + * Creates new RefIq parametrized with given call uri. + * @param uri the call URI to be set on newly created RefIq. + * @return new RefIq parametrized with given call uri. + */ + public static RefIq create(String uri) + { + RefIq refIq = new RefIq(); + + refIq.setUri(uri); + + return refIq; + } + + /** + * Creates result RefIq for given requestIq + * parametrized with given call uri. + * @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 + * RefIq. + * @return result RefIq for given requestIq + * parametrized with given call uri. + */ + 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 uri value on this instance. + * @param uri the call uri 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 HangUp 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){ } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/ReasonExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/ReasonExtension.java new file mode 100644 index 000000000..447c04fbc --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/rayo/ReasonExtension.java @@ -0,0 +1,88 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.rayo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Rayo 'reason' packet extension. + * + * @author Pawel Domas + */ +public class ReasonExtension + extends AbstractPacketExtension +{ + /** + * The name of optional platform code attribute. + */ + public static final String PLATFORM_CODE_ATTRIBUTE = "platform-code"; + + /** + * Indication that the call ended due to a normal hangup by the remote + * party. + */ + public static final String HANGUP = "hangup"; + + /** + * Indication that the call ended due to a normal hangup triggered by + * a hangup command. + */ + public static final String HANGUP_COMMND = "hangup-command"; + + /** + * Indication that the call ended due to a timeout in contacting the remote + * party. + */ + public static final String TIMEOUT = "timeout"; + + /** + * Indication that the call ended due to being rejected by the remote party + * subsequent to being accepted. + */ + public static final String BUSY = "busy"; + + /** + * Indication that the call ended due to being rejected by the remote party + * before being accepted. + */ + public static final String REJECTED = "rejected"; + + /** + * Indication that the call ended due to a system error. + */ + public static final String ERROR = "error"; + + /** + * Creates an {@link ReasonExtension} instance for the specified + * namespace and elementName. + * + * @param elementName the name of the element + */ + public ReasonExtension(String elementName) + { + super(null, elementName); + } + + /** + * Returns the value of platform code attribute. + * @return the value of platform code attribute. + */ + public String getPlatformCode() + { + return getAttributeAsString(PLATFORM_CODE_ATTRIBUTE); + } + + /** + * Sets new value of platform code attribute. Pass null to remove. + * @param code new value of platform code attribute. Pass null to + * remove. + */ + public void setPlatformCode(String code) + { + setAttribute(PLATFORM_CODE_ATTRIBUTE, code); + } +}