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("").append(elementName).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);
+ }
+}
]