diff --git a/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java b/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java
new file mode 100644
index 000000000..4ec51b2c8
--- /dev/null
+++ b/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java
@@ -0,0 +1,331 @@
+/**
+* $RCSfile$
+* $Revision$
+* $Date$
+*
+* Copyright (C) 2002-2003 Jive Software. All rights reserved.
+* ====================================================================
+* The Jive Software License (based on Apache Software License, Version 1.1)
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in
+* the documentation and/or other materials provided with the
+* distribution.
+*
+* 3. The end-user documentation included with the redistribution,
+* if any, must include the following acknowledgment:
+* "This product includes software developed by
+* Jive Software (http://www.jivesoftware.com)."
+* Alternately, this acknowledgment may appear in the software itself,
+* if and wherever such third-party acknowledgments normally appear.
+*
+* 4. The names "Smack" and "Jive Software" must not be used to
+* endorse or promote products derived from this software without
+* prior written permission. For written permission, please
+* contact webmaster@jivesoftware.com.
+*
+* 5. Products derived from this software may not be called "Smack",
+* nor may "Smack" appear in their name, without prior written
+* permission of Jive Software.
+*
+* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
+* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+* ====================================================================
+*/
+
+package org.jivesoftware.smackx;
+
+import java.util.*;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smackx.packet.*;
+
+/**
+ * Manages discovery of services in XMPP entities. This class provides:
+ *
+ * - A registry of supported features in this XMPP entity.
+ *
- Automatic response when this XMPP entity is queried for information.
+ *
- Ability to discover items and information of remote XMPP entities.
+ *
- Ability to publish publicly available items.
+ *
+ *
+ * @author Gaston Dombiak
+ */
+public class ServiceDiscoveryManager {
+
+ private static Map instances = new Hashtable();
+
+ private XMPPConnection connection;
+ private List features = new ArrayList();
+
+ /**
+ * Creates a new ServiceDiscoveryManager for a given XMPPConnection. This means that the
+ * service manager will respond to any service discovery request that the connection may
+ * receive.
+ *
+ * @param connection the connection to which a ServiceDiscoveryManager is going to be created
+ */
+ public ServiceDiscoveryManager(XMPPConnection connection) {
+ this.connection = connection;
+ init();
+ }
+
+ /**
+ * Returns the ServiceDiscoveryManager instance associated with a given XMPPConnection.
+ *
+ * @param connection the connection used to look for the proper ServiceDiscoveryManager
+ * @return the ServiceDiscoveryManager associated with a given XMPPConnection
+ */
+ public static ServiceDiscoveryManager getInstanceFor(XMPPConnection connection) {
+ return (ServiceDiscoveryManager)instances.get(connection);
+ }
+
+ /**
+ * Initializes the packet listeners of the connection that will answer to any
+ * service discovery request.
+ */
+ private void init() {
+ // Register the new instance and associate it with the connection
+ instances.put(connection, this);
+ // Add a listener to the connection that removes the registered instance when
+ // the connection is closed
+ connection.addConnectionListener(new ConnectionListener() {
+ public void connectionClosed() {
+ // Unregister this instance since the connection has been closed
+ instances.remove(connection);
+ }
+
+ public void connectionClosedOnError(Exception e) {
+ // Unregister this instance since the connection has been closed
+ instances.remove(connection);
+ }
+ });
+
+ // Listen for disco#items requests and answer with an empty result
+ PacketFilter packetFilter = new PacketTypeFilter(DiscoverItems.class);
+ PacketListener packetListener = new PacketListener() {
+ public void processPacket(Packet packet) {
+ DiscoverItems discoverItems = (DiscoverItems) packet;
+ // Answer an empty result if the request is of the GET type
+ // Note: In a future version Smack will enable to have items defined in the client
+ if (discoverItems != null && discoverItems.getType() == IQ.Type.GET) {
+ DiscoverItems response = new DiscoverItems();
+ response.setType(IQ.Type.RESULT);
+ response.setTo(discoverItems.getFrom());
+ response.setPacketID(discoverItems.getPacketID());
+ connection.sendPacket(response);
+ }
+ }
+ };
+ connection.addPacketListener(packetListener, packetFilter);
+
+ // Listen for disco#info requests and answer the client's supported features
+ // To add a new feature as supported use the #addFeature message
+ packetFilter = new PacketTypeFilter(DiscoverInfo.class);
+ packetListener = new PacketListener() {
+ public void processPacket(Packet packet) {
+ DiscoverInfo discoverInfo = (DiscoverInfo) packet;
+ // Answer the client's supported features if the request is of the GET type
+ if (discoverInfo != null && discoverInfo.getType() == IQ.Type.GET) {
+ DiscoverInfo response = new DiscoverInfo();
+ response.setType(IQ.Type.RESULT);
+ response.setTo(discoverInfo.getFrom());
+ response.setPacketID(discoverInfo.getPacketID());
+ // Add the registered features to the response
+ synchronized (features) {
+ for (Iterator it=getFeatures(); it.hasNext();) {
+ response.addFeature((String) it.next());
+ }
+ }
+ connection.sendPacket(response);
+ }
+ }
+ };
+ connection.addPacketListener(packetListener, packetFilter);
+ }
+
+ /**
+ * Returns the supported features by this XMPP entity.
+ *
+ * @return an Iterator on the supported features by this XMPP entity
+ */
+ public Iterator getFeatures() {
+ synchronized (features) {
+ return Collections.unmodifiableList(new ArrayList(features)).iterator();
+ }
+ }
+
+ /**
+ * Registers that a new feature is supported by this XMPP entity. When this client is
+ * queried for its information the registered features will be answered.
+ *
+ * @param feature the feature to register as supported
+ */
+ public void addFeature(String feature) {
+ synchronized (features) {
+ features.add(feature);
+ }
+ }
+
+ /**
+ * Removes the specified feature from the supported features by this XMPP entity.
+ *
+ * @param feature the feature to remove from the supported features
+ */
+ public void removeFeature(String feature) {
+ synchronized (features) {
+ features.remove(feature);
+ }
+ }
+
+ /**
+ * Returns true if the specified feature is registered in the ServiceDiscoveryManager.
+ *
+ * @param feature the feature to look for
+ * @return a boolean indicating if the specified featured is registered or not
+ */
+ public boolean includesFeature(String feature) {
+ synchronized (features) {
+ return features.contains(feature);
+ }
+ }
+
+ /**
+ * Returns the discovered information of a given XMPP entity addressed by its JID.
+ *
+ * @param entityID the address of the XMPP entity
+ * @return the discovered information
+ * @throws XMPPException if the operation failed for some reason
+ */
+ public DiscoverInfo discoverInfo(String entityID) throws XMPPException {
+ return discoverInfo(entityID, null);
+ }
+
+ /**
+ * Returns the discovered information of a given XMPP entity addressed by its JID and
+ * note attribute. Use this message only when trying to query information which is not
+ * directly addressable.
+ *
+ * @param entityID the address of the XMPP entity
+ * @param node the attribute that supplements the 'jid' attribute
+ * @return the discovered information
+ * @throws XMPPException if the operation failed for some reason
+ */
+ public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException {
+ // Discover the entity's info
+ DiscoverInfo disco = new DiscoverInfo();
+ disco.setType(IQ.Type.GET);
+ disco.setTo(entityID);
+ disco.setNode(node);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
+
+ connection.sendPacket(disco);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(5000);
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ return (DiscoverInfo) result;
+ }
+
+ /**
+ * Returns the discovered items of a given XMPP entity addressed by its JID.
+ *
+ * @param entityID the address of the XMPP entity
+ * @return the discovered information
+ * @throws XMPPException if the operation failed for some reason
+ */
+ public DiscoverItems discoverItems(String entityID) throws XMPPException {
+ return discoverItems(entityID, null);
+ }
+
+ /**
+ * Returns the discovered items of a given XMPP entity addressed by its JID and
+ * note attribute. Use this message only when trying to query information which is not
+ * directly addressable.
+ *
+ * @param entityID the address of the XMPP entity
+ * @param node the attribute that supplements the 'jid' attribute
+ * @return the discovered items
+ * @throws XMPPException if the operation failed for some reason
+ */
+ public DiscoverItems discoverItems(String entityID, String node) throws XMPPException {
+ // Discover the entity's items
+ DiscoverItems disco = new DiscoverItems();
+ disco.setType(IQ.Type.GET);
+ disco.setTo(entityID);
+ disco.setNode(node);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
+
+ connection.sendPacket(disco);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(5000);
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ return (DiscoverItems) result;
+ }
+
+ /**
+ * Publishes new items to a parent entity. The item elements to publish MUST have at least
+ * a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
+ * specifies the action being taken for that item. Possible action values are: "update" and
+ * "remove".
+ *
+ * @param entityID the address of the XMPP entity
+ * @param discoverItems the DiscoveryItems to publish
+ * @throws XMPPException if the operation failed for some reason
+ */
+ public void publishItems(String entityID, DiscoverItems discoverItems) throws XMPPException {
+ discoverItems.setType(IQ.Type.SET);
+ discoverItems.setTo(entityID);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(discoverItems.getPacketID()));
+
+ connection.sendPacket(discoverItems);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(5000);
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/org/jivesoftware/smackx/provider/DiscoverInfoProvider.java b/source/org/jivesoftware/smackx/provider/DiscoverInfoProvider.java
new file mode 100644
index 000000000..44d88a69f
--- /dev/null
+++ b/source/org/jivesoftware/smackx/provider/DiscoverInfoProvider.java
@@ -0,0 +1,109 @@
+/**
+* $RCSfile$
+* $Revision$
+* $Date$
+*
+* Copyright (C) 2002-2003 Jive Software. All rights reserved.
+* ====================================================================
+* The Jive Software License (based on Apache Software License, Version 1.1)
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in
+* the documentation and/or other materials provided with the
+* distribution.
+*
+* 3. The end-user documentation included with the redistribution,
+* if any, must include the following acknowledgment:
+* "This product includes software developed by
+* Jive Software (http://www.jivesoftware.com)."
+* Alternately, this acknowledgment may appear in the software itself,
+* if and wherever such third-party acknowledgments normally appear.
+*
+* 4. The names "Smack" and "Jive Software" must not be used to
+* endorse or promote products derived from this software without
+* prior written permission. For written permission, please
+* contact webmaster@jivesoftware.com.
+*
+* 5. Products derived from this software may not be called "Smack",
+* nor may "Smack" appear in their name, without prior written
+* permission of Jive Software.
+*
+* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
+* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+* ====================================================================
+*/
+
+package org.jivesoftware.smackx.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+* The DiscoverInfoProvider parses Service Discovery information packets.
+*
+* @author Gaston Dombiak
+*/
+public class DiscoverInfoProvider implements IQProvider {
+
+ public IQ parseIQ(XmlPullParser parser) throws Exception {
+ DiscoverInfo discoverInfo = new DiscoverInfo();
+ boolean done = false;
+ DiscoverInfo.Feature feature = null;
+ DiscoverInfo.Identity identity = null;
+ String category = "";
+ String name = "";
+ String type = "";
+ String variable = "";
+ discoverInfo.setNode(parser.getAttributeValue("", "node"));
+ while (!done) {
+ int eventType = parser.next();
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals("identity")) {
+ // Initialize the variables from the parsed XML
+ category = parser.getAttributeValue("", "category");
+ name = parser.getAttributeValue("", "name");
+ type = parser.getAttributeValue("", "type");
+ }
+ if (parser.getName().equals("feature")) {
+ // Initialize the variables from the parsed XML
+ variable = parser.getAttributeValue("", "var");
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals("identity")) {
+ // Create a new identity and add it to the discovered info.
+ identity = new DiscoverInfo.Identity(category, name);
+ identity.setType(type);
+ discoverInfo.addIdentity(identity);
+ }
+ if (parser.getName().equals("feature")) {
+ // Create a new feature and add it to the discovered info.
+ discoverInfo.addFeature(variable);
+ }
+ if (parser.getName().equals("query")) {
+ done = true;
+ }
+ }
+ }
+
+ return discoverInfo;
+ }
+}
\ No newline at end of file
diff --git a/source/org/jivesoftware/smackx/provider/DiscoverItemsProvider.java b/source/org/jivesoftware/smackx/provider/DiscoverItemsProvider.java
new file mode 100644
index 000000000..18b82ed41
--- /dev/null
+++ b/source/org/jivesoftware/smackx/provider/DiscoverItemsProvider.java
@@ -0,0 +1,103 @@
+/**
+* $RCSfile$
+* $Revision$
+* $Date$
+*
+* Copyright (C) 2002-2003 Jive Software. All rights reserved.
+* ====================================================================
+* The Jive Software License (based on Apache Software License, Version 1.1)
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in
+* the documentation and/or other materials provided with the
+* distribution.
+*
+* 3. The end-user documentation included with the redistribution,
+* if any, must include the following acknowledgment:
+* "This product includes software developed by
+* Jive Software (http://www.jivesoftware.com)."
+* Alternately, this acknowledgment may appear in the software itself,
+* if and wherever such third-party acknowledgments normally appear.
+*
+* 4. The names "Smack" and "Jive Software" must not be used to
+* endorse or promote products derived from this software without
+* prior written permission. For written permission, please
+* contact webmaster@jivesoftware.com.
+*
+* 5. Products derived from this software may not be called "Smack",
+* nor may "Smack" appear in their name, without prior written
+* permission of Jive Software.
+*
+* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
+* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+* ====================================================================
+*/
+
+package org.jivesoftware.smackx.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smackx.packet.*;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+* The DiscoverInfoProvider parses Service Discovery items packets.
+*
+* @author Gaston Dombiak
+*/
+public class DiscoverItemsProvider implements IQProvider {
+
+ public IQ parseIQ(XmlPullParser parser) throws Exception {
+ DiscoverItems discoverItems = new DiscoverItems();
+ boolean done = false;
+ DiscoverItems.Item item = null;
+ String jid = "";
+ String name = "";
+ String action = "";
+ String node = "";
+ discoverItems.setNode(parser.getAttributeValue("", "node"));
+ while (!done) {
+ int eventType = parser.next();
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ // Initialize the variables from the parsed XML
+ jid = parser.getAttributeValue("", "jid");
+ name = parser.getAttributeValue("", "name");
+ node = parser.getAttributeValue("", "node");
+ action = parser.getAttributeValue("", "action");
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals("item")) {
+ // Create a new Item and add it to DiscoverItems.
+ item = new DiscoverItems.Item(jid);
+ item.setName(name);
+ item.setNode(node);
+ item.setAction(action);
+ discoverItems.addItem(item);
+ }
+ if (parser.getName().equals("query")) {
+ done = true;
+ }
+ }
+ }
+
+ return discoverItems;
+ }
+}
\ No newline at end of file