2003-08-01 23:13:36 +02:00
|
|
|
/**
|
|
|
|
* $RCSfile$
|
|
|
|
* $Revision$
|
|
|
|
* $Date$
|
|
|
|
*
|
2004-11-03 00:37:00 +01:00
|
|
|
* Copyright 2003-2004 Jive Software.
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
2004-11-03 00:37:00 +01:00
|
|
|
* All rights reserved. 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
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
2004-11-03 00:37:00 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
2004-11-03 00:37:00 +01:00
|
|
|
* 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.
|
2003-08-01 23:13:36 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
package org.jivesoftware.smackx;
|
|
|
|
|
2006-07-18 07:14:33 +02:00
|
|
|
import org.jivesoftware.smack.PacketCollector;
|
|
|
|
import org.jivesoftware.smack.SmackConfiguration;
|
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2003-08-01 23:13:36 +02:00
|
|
|
import org.jivesoftware.smack.filter.PacketIDFilter;
|
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
2006-07-18 07:14:33 +02:00
|
|
|
import org.jivesoftware.smack.provider.IQProvider;
|
|
|
|
import org.jivesoftware.smackx.packet.DefaultPrivateData;
|
|
|
|
import org.jivesoftware.smackx.packet.PrivateData;
|
|
|
|
import org.jivesoftware.smackx.provider.PrivateDataProvider;
|
2003-08-01 23:13:36 +02:00
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
|
|
|
|
|
|
import java.util.Hashtable;
|
2006-07-18 07:14:33 +02:00
|
|
|
import java.util.Map;
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
/**
|
2003-08-20 18:03:32 +02:00
|
|
|
* Manages private data, which is a mechanism to allow users to store arbitrary XML
|
|
|
|
* data on an XMPP server. Each private data chunk is defined by a element name and
|
|
|
|
* XML namespace. Example private data:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* <color xmlns="http://example.com/xmpp/color">
|
|
|
|
* <favorite>blue</blue>
|
|
|
|
* <leastFavorite>puce</leastFavorite>
|
|
|
|
* </color>
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* {@link PrivateDataProvider} instances are responsible for translating the XML into objects.
|
|
|
|
* If no PrivateDataProvider is registered for a given element name and namespace, then
|
2003-08-31 01:57:00 +02:00
|
|
|
* a {@link DefaultPrivateData} instance will be returned.<p>
|
|
|
|
*
|
|
|
|
* Warning: this is an non-standard protocol documented by
|
|
|
|
* <a href="http://www.jabber.org/jeps/jep-0049.html">JEP-49</a>. Because this is a
|
|
|
|
* non-standard protocol, it is subject to change.
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
|
|
|
* @author Matt Tucker
|
|
|
|
*/
|
|
|
|
public class PrivateDataManager {
|
|
|
|
|
2003-08-20 17:32:40 +02:00
|
|
|
/**
|
|
|
|
* Map of provider instances.
|
|
|
|
*/
|
2003-08-01 23:13:36 +02:00
|
|
|
private static Map privateDataProviders = new Hashtable();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the private data provider registered to the specified XML element name and namespace.
|
|
|
|
* For example, if a provider was registered to the element name "prefs" and the
|
|
|
|
* namespace "http://www.xmppclient.com/prefs", then the following packet would trigger
|
|
|
|
* the provider:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
|
|
|
|
* <query xmlns='jabber:iq:private'>
|
|
|
|
* <prefs xmlns='http://www.xmppclient.com/prefs'>
|
|
|
|
* <value1>ABC</value1>
|
|
|
|
* <value2>XYZ</value2>
|
|
|
|
* </prefs>
|
|
|
|
* </query>
|
|
|
|
* </iq></pre>
|
|
|
|
*
|
|
|
|
* <p>Note: this method is generally only called by the internal Smack classes.
|
|
|
|
*
|
|
|
|
* @param elementName the XML element name.
|
|
|
|
* @param namespace the XML namespace.
|
2003-08-20 16:56:03 +02:00
|
|
|
* @return the PrivateData provider.
|
2003-08-01 23:13:36 +02:00
|
|
|
*/
|
2003-08-20 18:38:57 +02:00
|
|
|
public static PrivateDataProvider getPrivateDataProvider(String elementName, String namespace) {
|
2003-08-01 23:13:36 +02:00
|
|
|
String key = getProviderKey(elementName, namespace);
|
2003-08-20 18:38:57 +02:00
|
|
|
return (PrivateDataProvider)privateDataProviders.get(key);
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a private data provider with the specified element name and name space. The provider
|
|
|
|
* will override any providers loaded through the classpath.
|
|
|
|
*
|
|
|
|
* @param elementName the XML element name.
|
|
|
|
* @param namespace the XML namespace.
|
2003-08-20 16:56:03 +02:00
|
|
|
* @param provider the private data provider.
|
2003-08-01 23:13:36 +02:00
|
|
|
*/
|
2003-08-10 03:34:50 +02:00
|
|
|
public static void addPrivateDataProvider(String elementName, String namespace,
|
2003-08-20 16:56:03 +02:00
|
|
|
PrivateDataProvider provider)
|
2003-08-01 23:13:36 +02:00
|
|
|
{
|
|
|
|
String key = getProviderKey(elementName, namespace);
|
|
|
|
privateDataProviders.put(key, provider);
|
|
|
|
}
|
|
|
|
|
2006-05-27 00:42:07 +02:00
|
|
|
/**
|
|
|
|
* Removes a private data provider with the specified element name and namespace.
|
|
|
|
*
|
|
|
|
* @param elementName The XML element name.
|
|
|
|
* @param namespace The XML namespace.
|
|
|
|
*/
|
|
|
|
public static void removePrivateDataProvider(String elementName, String namespace) {
|
|
|
|
String key = getProviderKey(elementName, namespace);
|
|
|
|
privateDataProviders.remove(key);
|
|
|
|
}
|
|
|
|
|
2003-08-20 17:32:40 +02:00
|
|
|
|
2003-08-20 18:03:32 +02:00
|
|
|
private XMPPConnection connection;
|
2003-08-20 17:32:40 +02:00
|
|
|
|
2003-08-24 16:20:44 +02:00
|
|
|
/**
|
|
|
|
* The user to get and set private data for. In most cases, this value should
|
|
|
|
* be <tt>null</tt>, as the typical use of private data is to get and set
|
|
|
|
* your own private data and not others.
|
|
|
|
*/
|
|
|
|
private String user;
|
|
|
|
|
2003-08-01 23:13:36 +02:00
|
|
|
/**
|
2003-08-20 18:03:32 +02:00
|
|
|
* Creates a new private data manager. The connection must have
|
|
|
|
* undergone a successful login before being used to construct an instance of
|
|
|
|
* this class.
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
2003-08-20 18:03:32 +02:00
|
|
|
* @param connection an XMPP connection which must have already undergone a
|
|
|
|
* successful login.
|
2003-08-01 23:13:36 +02:00
|
|
|
*/
|
2003-08-20 18:03:32 +02:00
|
|
|
public PrivateDataManager(XMPPConnection connection) {
|
|
|
|
if (!connection.isAuthenticated()) {
|
|
|
|
throw new IllegalStateException("Must be logged in to XMPP server.");
|
|
|
|
}
|
|
|
|
this.connection = connection;
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
|
2003-08-24 16:20:44 +02:00
|
|
|
/**
|
|
|
|
* Creates a new private data manager for a specific user (special case). Most
|
|
|
|
* servers only support getting and setting private data for the user that
|
|
|
|
* authenticated via the connection. However, some servers support the ability
|
|
|
|
* to get and set private data for other users (for example, if you are the
|
|
|
|
* administrator). The connection must have undergone a successful login before
|
|
|
|
* being used to construct an instance of this class.
|
|
|
|
*
|
|
|
|
* @param connection an XMPP connection which must have already undergone a
|
|
|
|
* successful login.
|
|
|
|
* @param user the XMPP address of the user to get and set private data for.
|
|
|
|
*/
|
|
|
|
public PrivateDataManager(XMPPConnection connection, String user) {
|
|
|
|
if (!connection.isAuthenticated()) {
|
|
|
|
throw new IllegalStateException("Must be logged in to XMPP server.");
|
|
|
|
}
|
|
|
|
this.connection = connection;
|
|
|
|
this.user = user;
|
|
|
|
}
|
|
|
|
|
2003-08-01 23:13:36 +02:00
|
|
|
/**
|
2003-08-20 17:32:40 +02:00
|
|
|
* Returns the private data specified by the given element name and namespace. Each chunk
|
|
|
|
* of private data is uniquely identified by an element name and namespace pair.<p>
|
|
|
|
*
|
|
|
|
* If a PrivateDataProvider is registered for the specified element name/namespace pair then
|
|
|
|
* that provider will determine the specific object type that is returned. If no provider
|
|
|
|
* is registered, a {@link DefaultPrivateData} instance will be returned.
|
2003-08-01 23:13:36 +02:00
|
|
|
*
|
|
|
|
* @param elementName the element name.
|
|
|
|
* @param namespace the namespace.
|
|
|
|
* @return the private data.
|
2003-08-20 17:32:40 +02:00
|
|
|
* @throws XMPPException if an error occurs getting the private data.
|
2003-08-01 23:13:36 +02:00
|
|
|
*/
|
|
|
|
public PrivateData getPrivateData(final String elementName, final String namespace)
|
|
|
|
throws XMPPException
|
|
|
|
{
|
|
|
|
// Create an IQ packet to get the private data.
|
|
|
|
IQ privateDataGet = new IQ() {
|
|
|
|
public String getChildElementXML() {
|
2006-07-18 07:14:33 +02:00
|
|
|
StringBuilder buf = new StringBuilder();
|
2003-08-04 23:05:44 +02:00
|
|
|
buf.append("<query xmlns=\"jabber:iq:private\">");
|
2003-08-01 23:13:36 +02:00
|
|
|
buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\"/>");
|
2003-08-04 23:05:44 +02:00
|
|
|
buf.append("</query>");
|
2003-08-01 23:13:36 +02:00
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
privateDataGet.setType(IQ.Type.GET);
|
2003-08-24 16:20:44 +02:00
|
|
|
// Address the packet to the other account if user has been set.
|
|
|
|
if (user != null) {
|
|
|
|
privateDataGet.setTo(user);
|
|
|
|
}
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Setup a listener for the reply to the set operation.
|
|
|
|
String packetID = privateDataGet.getPacketID();
|
2003-08-20 18:03:32 +02:00
|
|
|
PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(packetID));
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Send the private data.
|
2003-08-20 18:03:32 +02:00
|
|
|
connection.sendPacket(privateDataGet);
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Wait up to five seconds for a response from the server.
|
2005-08-15 17:21:02 +02:00
|
|
|
IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
2004-07-16 22:46:16 +02:00
|
|
|
// Stop queuing results
|
|
|
|
collector.cancel();
|
2003-08-01 23:13:36 +02:00
|
|
|
if (response == null) {
|
|
|
|
throw new XMPPException("No response from the server.");
|
|
|
|
}
|
|
|
|
// If the server replied with an error, throw an exception.
|
|
|
|
else if (response.getType() == IQ.Type.ERROR) {
|
|
|
|
throw new XMPPException(response.getError());
|
|
|
|
}
|
|
|
|
return ((PrivateDataResult)response).getPrivateData();
|
|
|
|
}
|
|
|
|
|
2003-08-20 17:32:40 +02:00
|
|
|
/**
|
|
|
|
* Sets a private data value. Each chunk of private data is uniquely identified by an
|
|
|
|
* element name and namespace pair. If private data has already been set with the
|
|
|
|
* element name and namespace, then the new private data will overwrite the old value.
|
|
|
|
*
|
|
|
|
* @param privateData the private data.
|
|
|
|
* @throws XMPPException if setting the private data fails.
|
|
|
|
*/
|
2003-08-01 23:13:36 +02:00
|
|
|
public void setPrivateData(final PrivateData privateData) throws XMPPException {
|
|
|
|
// Create an IQ packet to set the private data.
|
|
|
|
IQ privateDataSet = new IQ() {
|
|
|
|
public String getChildElementXML() {
|
2006-07-18 07:14:33 +02:00
|
|
|
StringBuilder buf = new StringBuilder();
|
2003-08-04 23:05:44 +02:00
|
|
|
buf.append("<query xmlns=\"jabber:iq:private\">");
|
|
|
|
buf.append(privateData.toXML());
|
|
|
|
buf.append("</query>");
|
|
|
|
return buf.toString();
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
privateDataSet.setType(IQ.Type.SET);
|
2003-08-24 16:20:44 +02:00
|
|
|
// Address the packet to the other account if user has been set.
|
|
|
|
if (user != null) {
|
|
|
|
privateDataSet.setTo(user);
|
|
|
|
}
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Setup a listener for the reply to the set operation.
|
|
|
|
String packetID = privateDataSet.getPacketID();
|
2003-08-20 18:03:32 +02:00
|
|
|
PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(packetID));
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Send the private data.
|
2003-08-20 18:03:32 +02:00
|
|
|
connection.sendPacket(privateDataSet);
|
2003-08-01 23:13:36 +02:00
|
|
|
|
|
|
|
// Wait up to five seconds for a response from the server.
|
|
|
|
IQ response = (IQ)collector.nextResult(5000);
|
2004-07-16 22:46:16 +02:00
|
|
|
// Stop queuing results
|
|
|
|
collector.cancel();
|
2003-08-01 23:13:36 +02:00
|
|
|
if (response == null) {
|
|
|
|
throw new XMPPException("No response from the server.");
|
|
|
|
}
|
|
|
|
// If the server replied with an error, throw an exception.
|
|
|
|
else if (response.getType() == IQ.Type.ERROR) {
|
|
|
|
throw new XMPPException(response.getError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a String key for a given element name and namespace.
|
|
|
|
*
|
|
|
|
* @param elementName the element name.
|
|
|
|
* @param namespace the namespace.
|
|
|
|
* @return a unique key for the element name and namespace pair.
|
|
|
|
*/
|
|
|
|
private static String getProviderKey(String elementName, String namespace) {
|
2006-07-18 07:14:33 +02:00
|
|
|
StringBuilder buf = new StringBuilder();
|
2003-08-01 23:13:36 +02:00
|
|
|
buf.append("<").append(elementName).append("/><").append(namespace).append("/>");
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An IQ provider to parse IQ results containing private data.
|
|
|
|
*/
|
|
|
|
public static class PrivateDataIQProvider implements IQProvider {
|
|
|
|
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
2003-08-04 23:05:44 +02:00
|
|
|
PrivateData privateData = null;
|
2003-08-01 23:13:36 +02:00
|
|
|
boolean done = false;
|
|
|
|
while (!done) {
|
|
|
|
int eventType = parser.next();
|
|
|
|
if (eventType == XmlPullParser.START_TAG) {
|
|
|
|
String elementName = parser.getName();
|
|
|
|
String namespace = parser.getNamespace();
|
|
|
|
// See if any objects are registered to handle this private data type.
|
2003-08-25 22:03:53 +02:00
|
|
|
PrivateDataProvider provider = getPrivateDataProvider(elementName, namespace);
|
2003-08-01 23:13:36 +02:00
|
|
|
// If there is a registered provider, use it.
|
|
|
|
if (provider != null) {
|
2003-08-25 22:03:53 +02:00
|
|
|
privateData = provider.parsePrivateData(parser);
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
// Otherwise, use a DefaultPrivateData instance to store the private data.
|
|
|
|
else {
|
|
|
|
DefaultPrivateData data = new DefaultPrivateData(elementName, namespace);
|
|
|
|
boolean finished = false;
|
|
|
|
while (!finished) {
|
|
|
|
int event = parser.next();
|
|
|
|
if (event == XmlPullParser.START_TAG) {
|
|
|
|
String name = parser.getName();
|
|
|
|
// If an empty element, set the value with the empty string.
|
|
|
|
if (parser.isEmptyElementTag()) {
|
|
|
|
data.setValue(name,"");
|
|
|
|
}
|
|
|
|
// Otherwise, get the the element text.
|
|
|
|
else {
|
|
|
|
event = parser.next();
|
|
|
|
if (event == XmlPullParser.TEXT) {
|
|
|
|
String value = parser.getText();
|
|
|
|
data.setValue(name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (event == XmlPullParser.END_TAG) {
|
|
|
|
if (parser.getName().equals(elementName)) {
|
|
|
|
finished = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
privateData = data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (eventType == XmlPullParser.END_TAG) {
|
2003-08-04 23:05:44 +02:00
|
|
|
if (parser.getName().equals("query")) {
|
2003-08-01 23:13:36 +02:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-05-27 00:42:07 +02:00
|
|
|
return new PrivateDataResult(privateData);
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An IQ packet to hold PrivateData GET results.
|
|
|
|
*/
|
|
|
|
private static class PrivateDataResult extends IQ {
|
|
|
|
|
|
|
|
private PrivateData privateData;
|
|
|
|
|
|
|
|
PrivateDataResult(PrivateData privateData) {
|
|
|
|
this.privateData = privateData;
|
|
|
|
}
|
|
|
|
|
|
|
|
public PrivateData getPrivateData() {
|
|
|
|
return privateData;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getChildElementXML() {
|
2006-07-18 07:14:33 +02:00
|
|
|
StringBuilder buf = new StringBuilder();
|
2003-08-04 23:05:44 +02:00
|
|
|
buf.append("<query xmlns=\"jabber:iq:private\">");
|
|
|
|
if (privateData != null) {
|
|
|
|
privateData.toXML();
|
|
|
|
}
|
|
|
|
buf.append("</query>");
|
|
|
|
return buf.toString();
|
2003-08-01 23:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
2003-08-20 17:32:40 +02:00
|
|
|
}
|