aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/packet
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smack/packet')
-rw-r--r--src/org/jivesoftware/smack/packet/Authentication.java186
-rw-r--r--src/org/jivesoftware/smack/packet/Bind.java71
-rw-r--r--src/org/jivesoftware/smack/packet/DefaultPacketExtension.java133
-rw-r--r--src/org/jivesoftware/smack/packet/IQ.java244
-rw-r--r--src/org/jivesoftware/smack/packet/Message.java672
-rw-r--r--src/org/jivesoftware/smack/packet/Packet.java509
-rw-r--r--src/org/jivesoftware/smack/packet/PacketExtension.java56
-rw-r--r--src/org/jivesoftware/smack/packet/Presence.java358
-rw-r--r--src/org/jivesoftware/smack/packet/Privacy.java323
-rw-r--r--src/org/jivesoftware/smack/packet/PrivacyItem.java462
-rw-r--r--src/org/jivesoftware/smack/packet/Registration.java155
-rw-r--r--src/org/jivesoftware/smack/packet/RosterPacket.java311
-rw-r--r--src/org/jivesoftware/smack/packet/Session.java45
-rw-r--r--src/org/jivesoftware/smack/packet/StreamError.java106
-rw-r--r--src/org/jivesoftware/smack/packet/XMPPError.java453
-rw-r--r--src/org/jivesoftware/smack/packet/package.html1
16 files changed, 4085 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/packet/Authentication.java b/src/org/jivesoftware/smack/packet/Authentication.java
new file mode 100644
index 0000000..a47c079
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Authentication.java
@@ -0,0 +1,186 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * Authentication packet, which can be used to login to a XMPP server as well
+ * as discover login information from the server.
+ */
+public class Authentication extends IQ {
+
+ private String username = null;
+ private String password = null;
+ private String digest = null;
+ private String resource = null;
+
+ /**
+ * Create a new authentication packet. By default, the packet will be in
+ * "set" mode in order to perform an actual authentication with the server.
+ * In order to send a "get" request to get the available authentication
+ * modes back from the server, change the type of the IQ packet to "get":
+ * <p/>
+ * <p><tt>setType(IQ.Type.GET);</tt>
+ */
+ public Authentication() {
+ setType(IQ.Type.SET);
+ }
+
+ /**
+ * Returns the username, or <tt>null</tt> if the username hasn't been sent.
+ *
+ * @return the username.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Sets the username.
+ *
+ * @param username the username.
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Returns the plain text password or <tt>null</tt> if the password hasn't
+ * been set.
+ *
+ * @return the password.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Sets the plain text password.
+ *
+ * @param password the password.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Returns the password digest or <tt>null</tt> if the digest hasn't
+ * been set. Password digests offer a more secure alternative for
+ * authentication compared to plain text. The digest is the hex-encoded
+ * SHA-1 hash of the connection ID plus the user's password. If the
+ * digest and password are set, digest authentication will be used. If
+ * only one value is set, the respective authentication mode will be used.
+ *
+ * @return the digest of the user's password.
+ */
+ public String getDigest() {
+ return digest;
+ }
+
+ /**
+ * Sets the digest value using a connection ID and password. Password
+ * digests offer a more secure alternative for authentication compared to
+ * plain text. The digest is the hex-encoded SHA-1 hash of the connection ID
+ * plus the user's password. If the digest and password are set, digest
+ * authentication will be used. If only one value is set, the respective
+ * authentication mode will be used.
+ *
+ * @param connectionID the connection ID.
+ * @param password the password.
+ * @see org.jivesoftware.smack.Connection#getConnectionID()
+ */
+ public void setDigest(String connectionID, String password) {
+ this.digest = StringUtils.hash(connectionID + password);
+ }
+
+ /**
+ * Sets the digest value directly. Password digests offer a more secure
+ * alternative for authentication compared to plain text. The digest is
+ * the hex-encoded SHA-1 hash of the connection ID plus the user's password.
+ * If the digest and password are set, digest authentication will be used.
+ * If only one value is set, the respective authentication mode will be used.
+ *
+ * @param digest the digest, which is the SHA-1 hash of the connection ID
+ * the user's password, encoded as hex.
+ * @see org.jivesoftware.smack.Connection#getConnectionID()
+ */
+ public void setDigest(String digest) {
+ this.digest = digest;
+ }
+
+ /**
+ * Returns the resource or <tt>null</tt> if the resource hasn't been set.
+ *
+ * @return the resource.
+ */
+ public String getResource() {
+ return resource;
+ }
+
+ /**
+ * Sets the resource.
+ *
+ * @param resource the resource.
+ */
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<query xmlns=\"jabber:iq:auth\">");
+ if (username != null) {
+ if (username.equals("")) {
+ buf.append("<username/>");
+ }
+ else {
+ buf.append("<username>").append(username).append("</username>");
+ }
+ }
+ if (digest != null) {
+ if (digest.equals("")) {
+ buf.append("<digest/>");
+ }
+ else {
+ buf.append("<digest>").append(digest).append("</digest>");
+ }
+ }
+ if (password != null && digest == null) {
+ if (password.equals("")) {
+ buf.append("<password/>");
+ }
+ else {
+ buf.append("<password>").append(StringUtils.escapeForXML(password)).append("</password>");
+ }
+ }
+ if (resource != null) {
+ if (resource.equals("")) {
+ buf.append("<resource/>");
+ }
+ else {
+ buf.append("<resource>").append(resource).append("</resource>");
+ }
+ }
+ buf.append("</query>");
+ return buf.toString();
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/Bind.java b/src/org/jivesoftware/smack/packet/Bind.java
new file mode 100644
index 0000000..07cd193
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Bind.java
@@ -0,0 +1,71 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+/**
+ * IQ packet used by Smack to bind a resource and to obtain the jid assigned by the server.
+ * There are two ways to bind a resource. One is simply sending an empty Bind packet where the
+ * server will assign a new resource for this connection. The other option is to set a desired
+ * resource but the server may return a modified version of the sent resource.<p>
+ *
+ * For more information refer to the following
+ * <a href=http://www.xmpp.org/specs/rfc3920.html#bind>link</a>.
+ *
+ * @author Gaston Dombiak
+ */
+public class Bind extends IQ {
+
+ private String resource = null;
+ private String jid = null;
+
+ public Bind() {
+ setType(IQ.Type.SET);
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public String getJid() {
+ return jid;
+ }
+
+ public void setJid(String jid) {
+ this.jid = jid;
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
+ if (resource != null) {
+ buf.append("<resource>").append(resource).append("</resource>");
+ }
+ if (jid != null) {
+ buf.append("<jid>").append(jid).append("</jid>");
+ }
+ buf.append("</bind>");
+ return buf.toString();
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/DefaultPacketExtension.java b/src/org/jivesoftware/smack/packet/DefaultPacketExtension.java
new file mode 100644
index 0000000..6cc7934
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/DefaultPacketExtension.java
@@ -0,0 +1,133 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import java.util.*;
+
+/**
+ * Default implementation of the PacketExtension interface. Unless a PacketExtensionProvider
+ * is registered with {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager},
+ * instances of this class will be returned when getting packet extensions.<p>
+ *
+ * This class provides a very simple representation of an XML sub-document. Each element
+ * is a key in a Map with its CDATA being the value. For example, given the following
+ * XML sub-document:
+ *
+ * <pre>
+ * &lt;foo xmlns="http://bar.com"&gt;
+ * &lt;color&gt;blue&lt;/color&gt;
+ * &lt;food&gt;pizza&lt;/food&gt;
+ * &lt;/foo&gt;</pre>
+ *
+ * In this case, getValue("color") would return "blue", and getValue("food") would
+ * return "pizza". This parsing mechanism mechanism is very simplistic and will not work
+ * as desired in all cases (for example, if some of the elements have attributes. In those
+ * cases, a custom PacketExtensionProvider should be used.
+ *
+ * @author Matt Tucker
+ */
+public class DefaultPacketExtension implements PacketExtension {
+
+ private String elementName;
+ private String namespace;
+ private Map<String,String> map;
+
+ /**
+ * Creates a new generic packet extension.
+ *
+ * @param elementName the name of the element of the XML sub-document.
+ * @param namespace the namespace of the element.
+ */
+ public DefaultPacketExtension(String elementName, String namespace) {
+ this.elementName = elementName;
+ this.namespace = namespace;
+ }
+
+ /**
+ * Returns the XML element name of the extension sub-packet root element.
+ *
+ * @return the XML element name of the packet extension.
+ */
+ public String getElementName() {
+ return elementName;
+ }
+
+ /**
+ * Returns the XML namespace of the extension sub-packet root element.
+ *
+ * @return the XML namespace of the packet extension.
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\">");
+ for (String name : getNames()) {
+ String value = getValue(name);
+ buf.append("<").append(name).append(">");
+ buf.append(value);
+ buf.append("</").append(name).append(">");
+ }
+ buf.append("</").append(elementName).append(">");
+ return buf.toString();
+ }
+
+ /**
+ * Returns an unmodifiable collection of the names that can be used to get
+ * values of the packet extension.
+ *
+ * @return the names.
+ */
+ public synchronized Collection<String> getNames() {
+ if (map == null) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(new HashMap<String,String>(map).keySet());
+ }
+
+ /**
+ * Returns a packet extension value given a name.
+ *
+ * @param name the name.
+ * @return the value.
+ */
+ public synchronized String getValue(String name) {
+ if (map == null) {
+ return null;
+ }
+ return map.get(name);
+ }
+
+ /**
+ * Sets a packet extension value using the given name.
+ *
+ * @param name the name.
+ * @param value the value.
+ */
+ public synchronized void setValue(String name, String value) {
+ if (map == null) {
+ map = new HashMap<String,String>();
+ }
+ map.put(name, value);
+ }
+} \ No newline at end of file
diff --git a/src/org/jivesoftware/smack/packet/IQ.java b/src/org/jivesoftware/smack/packet/IQ.java
new file mode 100644
index 0000000..8e1f7d4
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/IQ.java
@@ -0,0 +1,244 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * The base IQ (Info/Query) packet. IQ packets are used to get and set information
+ * on the server, including authentication, roster operations, and creating
+ * accounts. Each IQ packet has a specific type that indicates what type of action
+ * is being taken: "get", "set", "result", or "error".<p>
+ *
+ * IQ packets can contain a single child element that exists in a specific XML
+ * namespace. The combination of the element name and namespace determines what
+ * type of IQ packet it is. Some example IQ subpacket snippets:<ul>
+ *
+ * <li>&lt;query xmlns="jabber:iq:auth"&gt; -- an authentication IQ.
+ * <li>&lt;query xmlns="jabber:iq:private"&gt; -- a private storage IQ.
+ * <li>&lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt; -- a pubsub IQ.
+ * </ul>
+ *
+ * @author Matt Tucker
+ */
+public abstract class IQ extends Packet {
+
+ private Type type = Type.GET;
+
+ public IQ() {
+ super();
+ }
+
+ public IQ(IQ iq) {
+ super(iq);
+ type = iq.getType();
+ }
+ /**
+ * Returns the type of the IQ packet.
+ *
+ * @return the type of the IQ packet.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the IQ packet.
+ *
+ * @param type the type of the IQ packet.
+ */
+ public void setType(Type type) {
+ if (type == null) {
+ this.type = Type.GET;
+ }
+ else {
+ this.type = type;
+ }
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<iq ");
+ if (getPacketID() != null) {
+ buf.append("id=\"" + getPacketID() + "\" ");
+ }
+ if (getTo() != null) {
+ buf.append("to=\"").append(StringUtils.escapeForXML(getTo())).append("\" ");
+ }
+ if (getFrom() != null) {
+ buf.append("from=\"").append(StringUtils.escapeForXML(getFrom())).append("\" ");
+ }
+ if (type == null) {
+ buf.append("type=\"get\">");
+ }
+ else {
+ buf.append("type=\"").append(getType()).append("\">");
+ }
+ // Add the query section if there is one.
+ String queryXML = getChildElementXML();
+ if (queryXML != null) {
+ buf.append(queryXML);
+ }
+ // Add the error sub-packet, if there is one.
+ XMPPError error = getError();
+ if (error != null) {
+ buf.append(error.toXML());
+ }
+ buf.append("</iq>");
+ return buf.toString();
+ }
+
+ /**
+ * Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
+ * isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
+ *
+ * Extensions of this class must override this method.
+ *
+ * @return the child element section of the IQ XML.
+ */
+ public abstract String getChildElementXML();
+
+ /**
+ * Convenience method to create a new empty {@link Type#RESULT IQ.Type.RESULT}
+ * IQ based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
+ * IQ. The new packet will be initialized with:<ul>
+ * <li>The sender set to the recipient of the originating IQ.
+ * <li>The recipient set to the sender of the originating IQ.
+ * <li>The type set to {@link Type#RESULT IQ.Type.RESULT}.
+ * <li>The id set to the id of the originating IQ.
+ * <li>No child element of the IQ element.
+ * </ul>
+ *
+ * @param iq the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
+ * @throws IllegalArgumentException if the IQ packet does not have a type of
+ * {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
+ * @return a new {@link Type#RESULT IQ.Type.RESULT} IQ based on the originating IQ.
+ */
+ public static IQ createResultIQ(final IQ request) {
+ if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
+ throw new IllegalArgumentException(
+ "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
+ }
+ final IQ result = new IQ() {
+ public String getChildElementXML() {
+ return null;
+ }
+ };
+ result.setType(Type.RESULT);
+ result.setPacketID(request.getPacketID());
+ result.setFrom(request.getTo());
+ result.setTo(request.getFrom());
+ return result;
+ }
+
+ /**
+ * Convenience method to create a new {@link Type#ERROR IQ.Type.ERROR} IQ
+ * based on a {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}
+ * IQ. The new packet will be initialized with:<ul>
+ * <li>The sender set to the recipient of the originating IQ.
+ * <li>The recipient set to the sender of the originating IQ.
+ * <li>The type set to {@link Type#ERROR IQ.Type.ERROR}.
+ * <li>The id set to the id of the originating IQ.
+ * <li>The child element contained in the associated originating IQ.
+ * <li>The provided {@link XMPPError XMPPError}.
+ * </ul>
+ *
+ * @param iq the {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET} IQ packet.
+ * @param error the error to associate with the created IQ packet.
+ * @throws IllegalArgumentException if the IQ packet does not have a type of
+ * {@link Type#GET IQ.Type.GET} or {@link Type#SET IQ.Type.SET}.
+ * @return a new {@link Type#ERROR IQ.Type.ERROR} IQ based on the originating IQ.
+ */
+ public static IQ createErrorResponse(final IQ request, final XMPPError error) {
+ if (!(request.getType() == Type.GET || request.getType() == Type.SET)) {
+ throw new IllegalArgumentException(
+ "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
+ }
+ final IQ result = new IQ() {
+ public String getChildElementXML() {
+ return request.getChildElementXML();
+ }
+ };
+ result.setType(Type.ERROR);
+ result.setPacketID(request.getPacketID());
+ result.setFrom(request.getTo());
+ result.setTo(request.getFrom());
+ result.setError(error);
+ return result;
+ }
+
+ /**
+ * A class to represent the type of the IQ packet. The types are:
+ *
+ * <ul>
+ * <li>IQ.Type.GET
+ * <li>IQ.Type.SET
+ * <li>IQ.Type.RESULT
+ * <li>IQ.Type.ERROR
+ * </ul>
+ */
+ public static class Type {
+
+ public static final Type GET = new Type("get");
+ public static final Type SET = new Type("set");
+ public static final Type RESULT = new Type("result");
+ public static final Type ERROR = new Type("error");
+
+ /**
+ * Converts a String into the corresponding types. Valid String values
+ * that can be converted to types are: "get", "set", "result", and "error".
+ *
+ * @param type the String value to covert.
+ * @return the corresponding Type.
+ */
+ public static Type fromString(String type) {
+ if (type == null) {
+ return null;
+ }
+ type = type.toLowerCase();
+ if (GET.toString().equals(type)) {
+ return GET;
+ }
+ else if (SET.toString().equals(type)) {
+ return SET;
+ }
+ else if (ERROR.toString().equals(type)) {
+ return ERROR;
+ }
+ else if (RESULT.toString().equals(type)) {
+ return RESULT;
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String value;
+
+ private Type(String value) {
+ this.value = value;
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/Message.java b/src/org/jivesoftware/smack/packet/Message.java
new file mode 100644
index 0000000..d28a9f4
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Message.java
@@ -0,0 +1,672 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+import java.util.*;
+
+/**
+ * Represents XMPP message packets. A message can be one of several types:
+ *
+ * <ul>
+ * <li>Message.Type.NORMAL -- (Default) a normal text message used in email like interface.
+ * <li>Message.Type.CHAT -- a typically short text message used in line-by-line chat interfaces.
+ * <li>Message.Type.GROUP_CHAT -- a chat message sent to a groupchat server for group chats.
+ * <li>Message.Type.HEADLINE -- a text message to be displayed in scrolling marquee displays.
+ * <li>Message.Type.ERROR -- indicates a messaging error.
+ * </ul>
+ *
+ * For each message type, different message fields are typically used as follows:
+ * <p>
+ * <table border="1">
+ * <tr><td>&nbsp;</td><td colspan="5"><b>Message type</b></td></tr>
+ * <tr><td><i>Field</i></td><td><b>Normal</b></td><td><b>Chat</b></td><td><b>Group Chat</b></td><td><b>Headline</b></td><td><b>XMPPError</b></td></tr>
+ * <tr><td><i>subject</i></td> <td>SHOULD</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td></tr>
+ * <tr><td><i>thread</i></td> <td>OPTIONAL</td><td>SHOULD</td><td>OPTIONAL</td><td>OPTIONAL</td><td>SHOULD NOT</td></tr>
+ * <tr><td><i>body</i></td> <td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD NOT</td></tr>
+ * <tr><td><i>error</i></td> <td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST</td></tr>
+ * </table>
+ *
+ * @author Matt Tucker
+ */
+public class Message extends Packet {
+
+ private Type type = Type.normal;
+ private String thread = null;
+ private String language;
+
+ private final Set<Subject> subjects = new HashSet<Subject>();
+ private final Set<Body> bodies = new HashSet<Body>();
+
+ /**
+ * Creates a new, "normal" message.
+ */
+ public Message() {
+ }
+
+ /**
+ * Creates a new "normal" message to the specified recipient.
+ *
+ * @param to the recipient of the message.
+ */
+ public Message(String to) {
+ setTo(to);
+ }
+
+ /**
+ * Creates a new message of the specified type to a recipient.
+ *
+ * @param to the user to send the message to.
+ * @param type the message type.
+ */
+ public Message(String to, Type type) {
+ setTo(to);
+ this.type = type;
+ }
+
+ /**
+ * Returns the type of the message. If no type has been set this method will return {@link
+ * org.jivesoftware.smack.packet.Message.Type#normal}.
+ *
+ * @return the type of the message.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the message.
+ *
+ * @param type the type of the message.
+ * @throws IllegalArgumentException if null is passed in as the type
+ */
+ public void setType(Type type) {
+ if (type == null) {
+ throw new IllegalArgumentException("Type cannot be null.");
+ }
+ this.type = type;
+ }
+
+ /**
+ * Returns the default subject of the message, or null if the subject has not been set.
+ * The subject is a short description of message contents.
+ * <p>
+ * The default subject of a message is the subject that corresponds to the message's language.
+ * (see {@link #getLanguage()}) or if no language is set to the applications default
+ * language (see {@link Packet#getDefaultLanguage()}).
+ *
+ * @return the subject of the message.
+ */
+ public String getSubject() {
+ return getSubject(null);
+ }
+
+ /**
+ * Returns the subject corresponding to the language. If the language is null, the method result
+ * will be the same as {@link #getSubject()}. Null will be returned if the language does not have
+ * a corresponding subject.
+ *
+ * @param language the language of the subject to return.
+ * @return the subject related to the passed in language.
+ */
+ public String getSubject(String language) {
+ Subject subject = getMessageSubject(language);
+ return subject == null ? null : subject.subject;
+ }
+
+ private Subject getMessageSubject(String language) {
+ language = determineLanguage(language);
+ for (Subject subject : subjects) {
+ if (language.equals(subject.language)) {
+ return subject;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a set of all subjects in this Message, including the default message subject accessible
+ * from {@link #getSubject()}.
+ *
+ * @return a collection of all subjects in this message.
+ */
+ public Collection<Subject> getSubjects() {
+ return Collections.unmodifiableCollection(subjects);
+ }
+
+ /**
+ * Sets the subject of the message. The subject is a short description of
+ * message contents.
+ *
+ * @param subject the subject of the message.
+ */
+ public void setSubject(String subject) {
+ if (subject == null) {
+ removeSubject(""); // use empty string because #removeSubject(null) is ambiguous
+ return;
+ }
+ addSubject(null, subject);
+ }
+
+ /**
+ * Adds a subject with a corresponding language.
+ *
+ * @param language the language of the subject being added.
+ * @param subject the subject being added to the message.
+ * @return the new {@link org.jivesoftware.smack.packet.Message.Subject}
+ * @throws NullPointerException if the subject is null, a null pointer exception is thrown
+ */
+ public Subject addSubject(String language, String subject) {
+ language = determineLanguage(language);
+ Subject messageSubject = new Subject(language, subject);
+ subjects.add(messageSubject);
+ return messageSubject;
+ }
+
+ /**
+ * Removes the subject with the given language from the message.
+ *
+ * @param language the language of the subject which is to be removed
+ * @return true if a subject was removed and false if it was not.
+ */
+ public boolean removeSubject(String language) {
+ language = determineLanguage(language);
+ for (Subject subject : subjects) {
+ if (language.equals(subject.language)) {
+ return subjects.remove(subject);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the subject from the message and returns true if the subject was removed.
+ *
+ * @param subject the subject being removed from the message.
+ * @return true if the subject was successfully removed and false if it was not.
+ */
+ public boolean removeSubject(Subject subject) {
+ return subjects.remove(subject);
+ }
+
+ /**
+ * Returns all the languages being used for the subjects, not including the default subject.
+ *
+ * @return the languages being used for the subjects.
+ */
+ public Collection<String> getSubjectLanguages() {
+ Subject defaultSubject = getMessageSubject(null);
+ List<String> languages = new ArrayList<String>();
+ for (Subject subject : subjects) {
+ if (!subject.equals(defaultSubject)) {
+ languages.add(subject.language);
+ }
+ }
+ return Collections.unmodifiableCollection(languages);
+ }
+
+ /**
+ * Returns the default body of the message, or null if the body has not been set. The body
+ * is the main message contents.
+ * <p>
+ * The default body of a message is the body that corresponds to the message's language.
+ * (see {@link #getLanguage()}) or if no language is set to the applications default
+ * language (see {@link Packet#getDefaultLanguage()}).
+ *
+ * @return the body of the message.
+ */
+ public String getBody() {
+ return getBody(null);
+ }
+
+ /**
+ * Returns the body corresponding to the language. If the language is null, the method result
+ * will be the same as {@link #getBody()}. Null will be returned if the language does not have
+ * a corresponding body.
+ *
+ * @param language the language of the body to return.
+ * @return the body related to the passed in language.
+ * @since 3.0.2
+ */
+ public String getBody(String language) {
+ Body body = getMessageBody(language);
+ return body == null ? null : body.message;
+ }
+
+ private Body getMessageBody(String language) {
+ language = determineLanguage(language);
+ for (Body body : bodies) {
+ if (language.equals(body.language)) {
+ return body;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a set of all bodies in this Message, including the default message body accessible
+ * from {@link #getBody()}.
+ *
+ * @return a collection of all bodies in this Message.
+ * @since 3.0.2
+ */
+ public Collection<Body> getBodies() {
+ return Collections.unmodifiableCollection(bodies);
+ }
+
+ /**
+ * Sets the body of the message. The body is the main message contents.
+ *
+ * @param body the body of the message.
+ */
+ public void setBody(String body) {
+ if (body == null) {
+ removeBody(""); // use empty string because #removeBody(null) is ambiguous
+ return;
+ }
+ addBody(null, body);
+ }
+
+ /**
+ * Adds a body with a corresponding language.
+ *
+ * @param language the language of the body being added.
+ * @param body the body being added to the message.
+ * @return the new {@link org.jivesoftware.smack.packet.Message.Body}
+ * @throws NullPointerException if the body is null, a null pointer exception is thrown
+ * @since 3.0.2
+ */
+ public Body addBody(String language, String body) {
+ language = determineLanguage(language);
+ Body messageBody = new Body(language, body);
+ bodies.add(messageBody);
+ return messageBody;
+ }
+
+ /**
+ * Removes the body with the given language from the message.
+ *
+ * @param language the language of the body which is to be removed
+ * @return true if a body was removed and false if it was not.
+ */
+ public boolean removeBody(String language) {
+ language = determineLanguage(language);
+ for (Body body : bodies) {
+ if (language.equals(body.language)) {
+ return bodies.remove(body);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the body from the message and returns true if the body was removed.
+ *
+ * @param body the body being removed from the message.
+ * @return true if the body was successfully removed and false if it was not.
+ * @since 3.0.2
+ */
+ public boolean removeBody(Body body) {
+ return bodies.remove(body);
+ }
+
+ /**
+ * Returns all the languages being used for the bodies, not including the default body.
+ *
+ * @return the languages being used for the bodies.
+ * @since 3.0.2
+ */
+ public Collection<String> getBodyLanguages() {
+ Body defaultBody = getMessageBody(null);
+ List<String> languages = new ArrayList<String>();
+ for (Body body : bodies) {
+ if (!body.equals(defaultBody)) {
+ languages.add(body.language);
+ }
+ }
+ return Collections.unmodifiableCollection(languages);
+ }
+
+ /**
+ * Returns the thread id of the message, which is a unique identifier for a sequence
+ * of "chat" messages. If no thread id is set, <tt>null</tt> will be returned.
+ *
+ * @return the thread id of the message, or <tt>null</tt> if it doesn't exist.
+ */
+ public String getThread() {
+ return thread;
+ }
+
+ /**
+ * Sets the thread id of the message, which is a unique identifier for a sequence
+ * of "chat" messages.
+ *
+ * @param thread the thread id of the message.
+ */
+ public void setThread(String thread) {
+ this.thread = thread;
+ }
+
+ /**
+ * Returns the xml:lang of this Message.
+ *
+ * @return the xml:lang of this Message.
+ * @since 3.0.2
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the xml:lang of this Message.
+ *
+ * @param language the xml:lang of this Message.
+ * @since 3.0.2
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ private String determineLanguage(String language) {
+
+ // empty string is passed by #setSubject() and #setBody() and is the same as null
+ language = "".equals(language) ? null : language;
+
+ // if given language is null check if message language is set
+ if (language == null && this.language != null) {
+ return this.language;
+ }
+ else if (language == null) {
+ return getDefaultLanguage();
+ }
+ else {
+ return language;
+ }
+
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<message");
+ if (getXmlns() != null) {
+ buf.append(" xmlns=\"").append(getXmlns()).append("\"");
+ }
+ if (language != null) {
+ buf.append(" xml:lang=\"").append(getLanguage()).append("\"");
+ }
+ if (getPacketID() != null) {
+ buf.append(" id=\"").append(getPacketID()).append("\"");
+ }
+ if (getTo() != null) {
+ buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
+ }
+ if (getFrom() != null) {
+ buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
+ }
+ if (type != Type.normal) {
+ buf.append(" type=\"").append(type).append("\"");
+ }
+ buf.append(">");
+ // Add the subject in the default language
+ Subject defaultSubject = getMessageSubject(null);
+ if (defaultSubject != null) {
+ buf.append("<subject>").append(StringUtils.escapeForXML(defaultSubject.subject)).append("</subject>");
+ }
+ // Add the subject in other languages
+ for (Subject subject : getSubjects()) {
+ // Skip the default language
+ if(subject.equals(defaultSubject))
+ continue;
+ buf.append("<subject xml:lang=\"").append(subject.language).append("\">");
+ buf.append(StringUtils.escapeForXML(subject.subject));
+ buf.append("</subject>");
+ }
+ // Add the body in the default language
+ Body defaultBody = getMessageBody(null);
+ if (defaultBody != null) {
+ buf.append("<body>").append(StringUtils.escapeForXML(defaultBody.message)).append("</body>");
+ }
+ // Add the bodies in other languages
+ for (Body body : getBodies()) {
+ // Skip the default language
+ if(body.equals(defaultBody))
+ continue;
+ buf.append("<body xml:lang=\"").append(body.getLanguage()).append("\">");
+ buf.append(StringUtils.escapeForXML(body.getMessage()));
+ buf.append("</body>");
+ }
+ if (thread != null) {
+ buf.append("<thread>").append(thread).append("</thread>");
+ }
+ // Append the error subpacket if the message type is an error.
+ if (type == Type.error) {
+ XMPPError error = getError();
+ if (error != null) {
+ buf.append(error.toXML());
+ }
+ }
+ // Add packet extensions, if any are defined.
+ buf.append(getExtensionsXML());
+ buf.append("</message>");
+ return buf.toString();
+ }
+
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Message message = (Message) o;
+
+ if(!super.equals(message)) { return false; }
+ if (bodies.size() != message.bodies.size() || !bodies.containsAll(message.bodies)) {
+ return false;
+ }
+ if (language != null ? !language.equals(message.language) : message.language != null) {
+ return false;
+ }
+ if (subjects.size() != message.subjects.size() || !subjects.containsAll(message.subjects)) {
+ return false;
+ }
+ if (thread != null ? !thread.equals(message.thread) : message.thread != null) {
+ return false;
+ }
+ return type == message.type;
+
+ }
+
+ public int hashCode() {
+ int result;
+ result = (type != null ? type.hashCode() : 0);
+ result = 31 * result + subjects.hashCode();
+ result = 31 * result + (thread != null ? thread.hashCode() : 0);
+ result = 31 * result + (language != null ? language.hashCode() : 0);
+ result = 31 * result + bodies.hashCode();
+ return result;
+ }
+
+ /**
+ * Represents a message subject, its language and the content of the subject.
+ */
+ public static class Subject {
+
+ private String subject;
+ private String language;
+
+ private Subject(String language, String subject) {
+ if (language == null) {
+ throw new NullPointerException("Language cannot be null.");
+ }
+ if (subject == null) {
+ throw new NullPointerException("Subject cannot be null.");
+ }
+ this.language = language;
+ this.subject = subject;
+ }
+
+ /**
+ * Returns the language of this message subject.
+ *
+ * @return the language of this message subject.
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Returns the subject content.
+ *
+ * @return the content of the subject.
+ */
+ public String getSubject() {
+ return subject;
+ }
+
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + this.language.hashCode();
+ result = prime * result + this.subject.hashCode();
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Subject other = (Subject) obj;
+ // simplified comparison because language and subject are always set
+ return this.language.equals(other.language) && this.subject.equals(other.subject);
+ }
+
+ }
+
+ /**
+ * Represents a message body, its language and the content of the message.
+ */
+ public static class Body {
+
+ private String message;
+ private String language;
+
+ private Body(String language, String message) {
+ if (language == null) {
+ throw new NullPointerException("Language cannot be null.");
+ }
+ if (message == null) {
+ throw new NullPointerException("Message cannot be null.");
+ }
+ this.language = language;
+ this.message = message;
+ }
+
+ /**
+ * Returns the language of this message body.
+ *
+ * @return the language of this message body.
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Returns the message content.
+ *
+ * @return the content of the message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + this.language.hashCode();
+ result = prime * result + this.message.hashCode();
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Body other = (Body) obj;
+ // simplified comparison because language and message are always set
+ return this.language.equals(other.language) && this.message.equals(other.message);
+ }
+
+ }
+
+ /**
+ * Represents the type of a message.
+ */
+ public enum Type {
+
+ /**
+ * (Default) a normal text message used in email like interface.
+ */
+ normal,
+
+ /**
+ * Typically short text message used in line-by-line chat interfaces.
+ */
+ chat,
+
+ /**
+ * Chat message sent to a groupchat server for group chats.
+ */
+ groupchat,
+
+ /**
+ * Text message to be displayed in scrolling marquee displays.
+ */
+ headline,
+
+ /**
+ * indicates a messaging error.
+ */
+ error;
+
+ public static Type fromString(String name) {
+ try {
+ return Type.valueOf(name);
+ }
+ catch (Exception e) {
+ return normal;
+ }
+ }
+
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/Packet.java b/src/org/jivesoftware/smack/packet/Packet.java
new file mode 100644
index 0000000..3f1185e
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Packet.java
@@ -0,0 +1,509 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * Base class for XMPP packets. Every packet has a unique ID (which is automatically
+ * generated, but can be overriden). Optionally, the "to" and "from" fields can be set,
+ * as well as an arbitrary number of properties.
+ *
+ * Properties provide an easy mechanism for clients to share data. Each property has a
+ * String name, and a value that is a Java primitive (int, long, float, double, boolean)
+ * or any Serializable object (a Java object is Serializable when it implements the
+ * Serializable interface).
+ *
+ * @author Matt Tucker
+ */
+public abstract class Packet {
+
+ protected static final String DEFAULT_LANGUAGE =
+ java.util.Locale.getDefault().getLanguage().toLowerCase();
+
+ private static String DEFAULT_XML_NS = null;
+
+ /**
+ * Constant used as packetID to indicate that a packet has no id. To indicate that a packet
+ * has no id set this constant as the packet's id. When the packet is asked for its id the
+ * answer will be <tt>null</tt>.
+ */
+ public static final String ID_NOT_AVAILABLE = "ID_NOT_AVAILABLE";
+
+ /**
+ * Date format as defined in XEP-0082 - XMPP Date and Time Profiles.
+ * The time zone is set to UTC.
+ * <p>
+ * Date formats are not synchronized. Since multiple threads access the format concurrently,
+ * it must be synchronized externally.
+ */
+ public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ static {
+ XEP_0082_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+
+ /**
+ * A prefix helps to make sure that ID's are unique across mutliple instances.
+ */
+ private static String prefix = StringUtils.randomString(5) + "-";
+
+ /**
+ * Keeps track of the current increment, which is appended to the prefix to
+ * forum a unique ID.
+ */
+ private static long id = 0;
+
+ private String xmlns = DEFAULT_XML_NS;
+
+ /**
+ * Returns the next unique id. Each id made up of a short alphanumeric
+ * prefix along with a unique numeric value.
+ *
+ * @return the next id.
+ */
+ public static synchronized String nextID() {
+ return prefix + Long.toString(id++);
+ }
+
+ public static void setDefaultXmlns(String defaultXmlns) {
+ DEFAULT_XML_NS = defaultXmlns;
+ }
+
+ private String packetID = null;
+ private String to = null;
+ private String from = null;
+ private final List<PacketExtension> packetExtensions
+ = new CopyOnWriteArrayList<PacketExtension>();
+
+ private final Map<String,Object> properties = new HashMap<String, Object>();
+ private XMPPError error = null;
+
+ public Packet() {
+ }
+
+ public Packet(Packet p) {
+ packetID = p.getPacketID();
+ to = p.getTo();
+ from = p.getFrom();
+ xmlns = p.xmlns;
+ error = p.error;
+
+ // Copy extensions
+ for (PacketExtension pe : p.getExtensions()) {
+ addExtension(pe);
+ }
+ }
+
+ /**
+ * Returns the unique ID of the packet. The returned value could be <tt>null</tt> when
+ * ID_NOT_AVAILABLE was set as the packet's id.
+ *
+ * @return the packet's unique ID or <tt>null</tt> if the packet's id is not available.
+ */
+ public String getPacketID() {
+ if (ID_NOT_AVAILABLE.equals(packetID)) {
+ return null;
+ }
+
+ if (packetID == null) {
+ packetID = nextID();
+ }
+ return packetID;
+ }
+
+ /**
+ * Sets the unique ID of the packet. To indicate that a packet has no id
+ * pass the constant ID_NOT_AVAILABLE as the packet's id value.
+ *
+ * @param packetID the unique ID for the packet.
+ */
+ public void setPacketID(String packetID) {
+ this.packetID = packetID;
+ }
+
+ /**
+ * Returns who the packet is being sent "to", or <tt>null</tt> if
+ * the value is not set. The XMPP protocol often makes the "to"
+ * attribute optional, so it does not always need to be set.<p>
+ *
+ * The StringUtils class provides several useful methods for dealing with
+ * XMPP addresses such as parsing the
+ * {@link StringUtils#parseBareAddress(String) bare address},
+ * {@link StringUtils#parseName(String) user name},
+ * {@link StringUtils#parseServer(String) server}, and
+ * {@link StringUtils#parseResource(String) resource}.
+ *
+ * @return who the packet is being sent to, or <tt>null</tt> if the
+ * value has not been set.
+ */
+ public String getTo() {
+ return to;
+ }
+
+ /**
+ * Sets who the packet is being sent "to". The XMPP protocol often makes
+ * the "to" attribute optional, so it does not always need to be set.
+ *
+ * @param to who the packet is being sent to.
+ */
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ /**
+ * Returns who the packet is being sent "from" or <tt>null</tt> if
+ * the value is not set. The XMPP protocol often makes the "from"
+ * attribute optional, so it does not always need to be set.<p>
+ *
+ * The StringUtils class provides several useful methods for dealing with
+ * XMPP addresses such as parsing the
+ * {@link StringUtils#parseBareAddress(String) bare address},
+ * {@link StringUtils#parseName(String) user name},
+ * {@link StringUtils#parseServer(String) server}, and
+ * {@link StringUtils#parseResource(String) resource}.
+ *
+ * @return who the packet is being sent from, or <tt>null</tt> if the
+ * value has not been set.
+ */
+ public String getFrom() {
+ return from;
+ }
+
+ /**
+ * Sets who the packet is being sent "from". The XMPP protocol often
+ * makes the "from" attribute optional, so it does not always need to
+ * be set.
+ *
+ * @param from who the packet is being sent to.
+ */
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ /**
+ * Returns the error associated with this packet, or <tt>null</tt> if there are
+ * no errors.
+ *
+ * @return the error sub-packet or <tt>null</tt> if there isn't an error.
+ */
+ public XMPPError getError() {
+ return error;
+ }
+
+ /**
+ * Sets the error for this packet.
+ *
+ * @param error the error to associate with this packet.
+ */
+ public void setError(XMPPError error) {
+ this.error = error;
+ }
+
+ /**
+ * Returns an unmodifiable collection of the packet extensions attached to the packet.
+ *
+ * @return the packet extensions.
+ */
+ public synchronized Collection<PacketExtension> getExtensions() {
+ if (packetExtensions == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(new ArrayList<PacketExtension>(packetExtensions));
+ }
+
+ /**
+ * Returns the first extension of this packet that has the given namespace.
+ *
+ * @param namespace the namespace of the extension that is desired.
+ * @return the packet extension with the given namespace.
+ */
+ public PacketExtension getExtension(String namespace) {
+ return getExtension(null, namespace);
+ }
+
+ /**
+ * Returns the first packet extension that matches the specified element name and
+ * namespace, or <tt>null</tt> if it doesn't exist. If the provided elementName is null
+ * than only the provided namespace is attempted to be matched. Packet extensions are
+ * are arbitrary XML sub-documents in standard XMPP packets. By default, a
+ * DefaultPacketExtension instance will be returned for each extension. However,
+ * PacketExtensionProvider instances can be registered with the
+ * {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager}
+ * class to handle custom parsing. In that case, the type of the Object
+ * will be determined by the provider.
+ *
+ * @param elementName the XML element name of the packet extension. (May be null)
+ * @param namespace the XML element namespace of the packet extension.
+ * @return the extension, or <tt>null</tt> if it doesn't exist.
+ */
+ public PacketExtension getExtension(String elementName, String namespace) {
+ if (namespace == null) {
+ return null;
+ }
+ for (PacketExtension ext : packetExtensions) {
+ if ((elementName == null || elementName.equals(ext.getElementName()))
+ && namespace.equals(ext.getNamespace()))
+ {
+ return ext;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a packet extension to the packet. Does nothing if extension is null.
+ *
+ * @param extension a packet extension.
+ */
+ public void addExtension(PacketExtension extension) {
+ if (extension == null) return;
+ packetExtensions.add(extension);
+ }
+
+ /**
+ * Adds a collection of packet extensions to the packet. Does nothing if extensions is null.
+ *
+ * @param extensions a collection of packet extensions
+ */
+ public void addExtensions(Collection<PacketExtension> extensions) {
+ if (extensions == null) return;
+ packetExtensions.addAll(extensions);
+ }
+
+ /**
+ * Removes a packet extension from the packet.
+ *
+ * @param extension the packet extension to remove.
+ */
+ public void removeExtension(PacketExtension extension) {
+ packetExtensions.remove(extension);
+ }
+
+ /**
+ * Returns the packet property with the specified name or <tt>null</tt> if the
+ * property doesn't exist. Property values that were originally primitives will
+ * be returned as their object equivalent. For example, an int property will be
+ * returned as an Integer, a double as a Double, etc.
+ *
+ * @param name the name of the property.
+ * @return the property, or <tt>null</tt> if the property doesn't exist.
+ */
+ public synchronized Object getProperty(String name) {
+ if (properties == null) {
+ return null;
+ }
+ return properties.get(name);
+ }
+
+ /**
+ * Sets a property with an Object as the value. The value must be Serializable
+ * or an IllegalArgumentException will be thrown.
+ *
+ * @param name the name of the property.
+ * @param value the value of the property.
+ */
+ public synchronized void setProperty(String name, Object value) {
+ if (!(value instanceof Serializable)) {
+ throw new IllegalArgumentException("Value must be serialiazble");
+ }
+ properties.put(name, value);
+ }
+
+ /**
+ * Deletes a property.
+ *
+ * @param name the name of the property to delete.
+ */
+ public synchronized void deleteProperty(String name) {
+ if (properties == null) {
+ return;
+ }
+ properties.remove(name);
+ }
+
+ /**
+ * Returns an unmodifiable collection of all the property names that are set.
+ *
+ * @return all property names.
+ */
+ public synchronized Collection<String> getPropertyNames() {
+ if (properties == null) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(new HashSet<String>(properties.keySet()));
+ }
+
+ /**
+ * Returns the packet as XML. Every concrete extension of Packet must implement
+ * this method. In addition to writing out packet-specific data, every sub-class
+ * should also write out the error and the extensions data if they are defined.
+ *
+ * @return the XML format of the packet as a String.
+ */
+ public abstract String toXML();
+
+ /**
+ * Returns the extension sub-packets (including properties data) as an XML
+ * String, or the Empty String if there are no packet extensions.
+ *
+ * @return the extension sub-packets as XML or the Empty String if there
+ * are no packet extensions.
+ */
+ protected synchronized String getExtensionsXML() {
+ StringBuilder buf = new StringBuilder();
+ // Add in all standard extension sub-packets.
+ for (PacketExtension extension : getExtensions()) {
+ buf.append(extension.toXML());
+ }
+ // Add in packet properties.
+ if (properties != null && !properties.isEmpty()) {
+ buf.append("<properties xmlns=\"http://www.jivesoftware.com/xmlns/xmpp/properties\">");
+ // Loop through all properties and write them out.
+ for (String name : getPropertyNames()) {
+ Object value = getProperty(name);
+ buf.append("<property>");
+ buf.append("<name>").append(StringUtils.escapeForXML(name)).append("</name>");
+ buf.append("<value type=\"");
+ if (value instanceof Integer) {
+ buf.append("integer\">").append(value).append("</value>");
+ }
+ else if (value instanceof Long) {
+ buf.append("long\">").append(value).append("</value>");
+ }
+ else if (value instanceof Float) {
+ buf.append("float\">").append(value).append("</value>");
+ }
+ else if (value instanceof Double) {
+ buf.append("double\">").append(value).append("</value>");
+ }
+ else if (value instanceof Boolean) {
+ buf.append("boolean\">").append(value).append("</value>");
+ }
+ else if (value instanceof String) {
+ buf.append("string\">");
+ buf.append(StringUtils.escapeForXML((String)value));
+ buf.append("</value>");
+ }
+ // Otherwise, it's a generic Serializable object. Serialized objects are in
+ // a binary format, which won't work well inside of XML. Therefore, we base-64
+ // encode the binary data before adding it.
+ else {
+ ByteArrayOutputStream byteStream = null;
+ ObjectOutputStream out = null;
+ try {
+ byteStream = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(byteStream);
+ out.writeObject(value);
+ buf.append("java-object\">");
+ String encodedVal = StringUtils.encodeBase64(byteStream.toByteArray());
+ buf.append(encodedVal).append("</value>");
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ finally {
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (Exception e) {
+ // Ignore.
+ }
+ }
+ if (byteStream != null) {
+ try {
+ byteStream.close();
+ }
+ catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+ }
+ buf.append("</property>");
+ }
+ buf.append("</properties>");
+ }
+ return buf.toString();
+ }
+
+ public String getXmlns() {
+ return this.xmlns;
+ }
+
+ /**
+ * Returns the default language used for all messages containing localized content.
+ *
+ * @return the default language
+ */
+ public static String getDefaultLanguage() {
+ return DEFAULT_LANGUAGE;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Packet packet = (Packet) o;
+
+ if (error != null ? !error.equals(packet.error) : packet.error != null) { return false; }
+ if (from != null ? !from.equals(packet.from) : packet.from != null) { return false; }
+ if (!packetExtensions.equals(packet.packetExtensions)) { return false; }
+ if (packetID != null ? !packetID.equals(packet.packetID) : packet.packetID != null) {
+ return false;
+ }
+ if (properties != null ? !properties.equals(packet.properties)
+ : packet.properties != null) {
+ return false;
+ }
+ if (to != null ? !to.equals(packet.to) : packet.to != null) { return false; }
+ return !(xmlns != null ? !xmlns.equals(packet.xmlns) : packet.xmlns != null);
+ }
+
+ public int hashCode() {
+ int result;
+ result = (xmlns != null ? xmlns.hashCode() : 0);
+ result = 31 * result + (packetID != null ? packetID.hashCode() : 0);
+ result = 31 * result + (to != null ? to.hashCode() : 0);
+ result = 31 * result + (from != null ? from.hashCode() : 0);
+ result = 31 * result + packetExtensions.hashCode();
+ result = 31 * result + properties.hashCode();
+ result = 31 * result + (error != null ? error.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/PacketExtension.java b/src/org/jivesoftware/smack/packet/PacketExtension.java
new file mode 100644
index 0000000..d2afbf8
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/PacketExtension.java
@@ -0,0 +1,56 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+/**
+ * Interface to represent packet extensions. A packet extension is an XML subdocument
+ * with a root element name and namespace. Packet extensions are used to provide
+ * extended functionality beyond what is in the base XMPP specification. Examples of
+ * packet extensions include message events, message properties, and extra presence data.
+ * IQ packets cannot contain packet extensions.
+ *
+ * @see DefaultPacketExtension
+ * @see org.jivesoftware.smack.provider.PacketExtensionProvider
+ * @author Matt Tucker
+ */
+public interface PacketExtension {
+
+ /**
+ * Returns the root element name.
+ *
+ * @return the element name.
+ */
+ public String getElementName();
+
+ /**
+ * Returns the root element XML namespace.
+ *
+ * @return the namespace.
+ */
+ public String getNamespace();
+
+ /**
+ * Returns the XML representation of the PacketExtension.
+ *
+ * @return the packet extension as XML.
+ */
+ public String toXML();
+}
diff --git a/src/org/jivesoftware/smack/packet/Presence.java b/src/org/jivesoftware/smack/packet/Presence.java
new file mode 100644
index 0000000..84fcfef
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Presence.java
@@ -0,0 +1,358 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * Represents XMPP presence packets. Every presence packet has a type, which is one of
+ * the following values:
+ * <ul>
+ * <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to
+ * receive messages.
+ * <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages.
+ * <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence.
+ * <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence.
+ * <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to
+ * sender's presence.
+ * <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to
+ * sender's presence.
+ * <li>{@link Presence.Type#error error} -- the presence packet contains an error message.
+ * </ul><p>
+ *
+ * A number of attributes are optional:
+ * <ul>
+ * <li>Status -- free-form text describing a user's presence (i.e., gone to lunch).
+ * <li>Priority -- non-negative numerical priority of a sender's resource. The
+ * highest resource priority is the default recipient of packets not addressed
+ * to a particular resource.
+ * <li>Mode -- one of five presence modes: {@link Mode#available available} (the default),
+ * {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and
+ * {@link Mode#dnd dnd} (do not disturb).
+ * </ul><p>
+ *
+ * Presence packets are used for two purposes. First, to notify the server of our
+ * the clients current presence status. Second, they are used to subscribe and
+ * unsubscribe users from the roster.
+ *
+ * @see RosterPacket
+ * @author Matt Tucker
+ */
+public class Presence extends Packet {
+
+ private Type type = Type.available;
+ private String status = null;
+ private int priority = Integer.MIN_VALUE;
+ private Mode mode = null;
+ private String language;
+
+ /**
+ * Creates a new presence update. Status, priority, and mode are left un-set.
+ *
+ * @param type the type.
+ */
+ public Presence(Type type) {
+ setType(type);
+ }
+
+ /**
+ * Creates a new presence update with a specified status, priority, and mode.
+ *
+ * @param type the type.
+ * @param status a text message describing the presence update.
+ * @param priority the priority of this presence update.
+ * @param mode the mode type for this presence update.
+ */
+ public Presence(Type type, String status, int priority, Mode mode) {
+ setType(type);
+ setStatus(status);
+ setPriority(priority);
+ setMode(mode);
+ }
+
+ /**
+ * Returns true if the {@link Type presence type} is available (online) and
+ * false if the user is unavailable (offline), or if this is a presence packet
+ * involved in a subscription operation. This is a convenience method
+ * equivalent to <tt>getType() == Presence.Type.available</tt>. Note that even
+ * when the user is available, their presence mode may be {@link Mode#away away},
+ * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use
+ * {@link #isAway()} to determine if the user is away.
+ *
+ * @return true if the presence type is available.
+ */
+ public boolean isAvailable() {
+ return type == Type.available;
+ }
+
+ /**
+ * Returns true if the presence type is {@link Type#available available} and the presence
+ * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or
+ * {@link Mode#dnd do not disturb}. False will be returned when the type or mode
+ * is any other value, including when the presence type is unavailable (offline).
+ * This is a convenience method equivalent to
+ * <tt>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</tt>.
+ *
+ * @return true if the presence type is available and the presence mode is away, xa, or dnd.
+ */
+ public boolean isAway() {
+ return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd);
+ }
+
+ /**
+ * Returns the type of this presence packet.
+ *
+ * @return the type of the presence packet.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the presence packet.
+ *
+ * @param type the type of the presence packet.
+ */
+ public void setType(Type type) {
+ if(type == null) {
+ throw new NullPointerException("Type cannot be null");
+ }
+ this.type = type;
+ }
+
+ /**
+ * Returns the status message of the presence update, or <tt>null</tt> if there
+ * is not a status. The status is free-form text describing a user's presence
+ * (i.e., "gone to lunch").
+ *
+ * @return the status message.
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * Sets the status message of the presence update. The status is free-form text
+ * describing a user's presence (i.e., "gone to lunch").
+ *
+ * @param status the status message.
+ */
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ /**
+ * Returns the priority of the presence, or Integer.MIN_VALUE if no priority has been set.
+ *
+ * @return the priority.
+ */
+ public int getPriority() {
+ return priority;
+ }
+
+ /**
+ * Sets the priority of the presence. The valid range is -128 through 128.
+ *
+ * @param priority the priority of the presence.
+ * @throws IllegalArgumentException if the priority is outside the valid range.
+ */
+ public void setPriority(int priority) {
+ if (priority < -128 || priority > 128) {
+ throw new IllegalArgumentException("Priority value " + priority +
+ " is not valid. Valid range is -128 through 128.");
+ }
+ this.priority = priority;
+ }
+
+ /**
+ * Returns the mode of the presence update, or <tt>null</tt> if the mode is not set.
+ * A null presence mode value is interpreted to be the same thing as
+ * {@link Presence.Mode#available}.
+ *
+ * @return the mode.
+ */
+ public Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets the mode of the presence update. A null presence mode value is interpreted
+ * to be the same thing as {@link Presence.Mode#available}.
+ *
+ * @param mode the mode.
+ */
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Returns the xml:lang of this Presence, or null if one has not been set.
+ *
+ * @return the xml:lang of this Presence, or null if one has not been set.
+ * @since 3.0.2
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the xml:lang of this Presence.
+ *
+ * @param language the xml:lang of this Presence.
+ * @since 3.0.2
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<presence");
+ if(getXmlns() != null) {
+ buf.append(" xmlns=\"").append(getXmlns()).append("\"");
+ }
+ if (language != null) {
+ buf.append(" xml:lang=\"").append(getLanguage()).append("\"");
+ }
+ if (getPacketID() != null) {
+ buf.append(" id=\"").append(getPacketID()).append("\"");
+ }
+ if (getTo() != null) {
+ buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
+ }
+ if (getFrom() != null) {
+ buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
+ }
+ if (type != Type.available) {
+ buf.append(" type=\"").append(type).append("\"");
+ }
+ buf.append(">");
+ if (status != null) {
+ buf.append("<status>").append(StringUtils.escapeForXML(status)).append("</status>");
+ }
+ if (priority != Integer.MIN_VALUE) {
+ buf.append("<priority>").append(priority).append("</priority>");
+ }
+ if (mode != null && mode != Mode.available) {
+ buf.append("<show>").append(mode).append("</show>");
+ }
+
+ buf.append(this.getExtensionsXML());
+
+ // Add the error sub-packet, if there is one.
+ XMPPError error = getError();
+ if (error != null) {
+ buf.append(error.toXML());
+ }
+
+ buf.append("</presence>");
+
+ return buf.toString();
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(type);
+ if (mode != null) {
+ buf.append(": ").append(mode);
+ }
+ if (getStatus() != null) {
+ buf.append(" (").append(getStatus()).append(")");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * A enum to represent the presecence type. Not that presence type is often confused
+ * with presence mode. Generally, if a user is signed into a server, they have a presence
+ * type of {@link #available available}, even if the mode is {@link Mode#away away},
+ * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when
+ * the user is signing out of the server.
+ */
+ public enum Type {
+
+ /**
+ * The user is available to receive messages (default).
+ */
+ available,
+
+ /**
+ * The user is unavailable to receive messages.
+ */
+ unavailable,
+
+ /**
+ * Request subscription to recipient's presence.
+ */
+ subscribe,
+
+ /**
+ * Grant subscription to sender's presence.
+ */
+ subscribed,
+
+ /**
+ * Request removal of subscription to sender's presence.
+ */
+ unsubscribe,
+
+ /**
+ * Grant removal of subscription to sender's presence.
+ */
+ unsubscribed,
+
+ /**
+ * The presence packet contains an error message.
+ */
+ error
+ }
+
+ /**
+ * An enum to represent the presence mode.
+ */
+ public enum Mode {
+
+ /**
+ * Free to chat.
+ */
+ chat,
+
+ /**
+ * Available (the default).
+ */
+ available,
+
+ /**
+ * Away.
+ */
+ away,
+
+ /**
+ * Away for an extended period of time.
+ */
+ xa,
+
+ /**
+ * Do not disturb.
+ */
+ dnd
+ }
+} \ No newline at end of file
diff --git a/src/org/jivesoftware/smack/packet/Privacy.java b/src/org/jivesoftware/smack/packet/Privacy.java
new file mode 100644
index 0000000..a62d578
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Privacy.java
@@ -0,0 +1,323 @@
+/**
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2006-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import java.util.*;
+
+/**
+ * A Privacy IQ Packet, is used by the {@link org.jivesoftware.smack.PrivacyListManager}
+ * and {@link org.jivesoftware.smack.provider.PrivacyProvider} to allow and block
+ * communications from other users. It contains the appropriate structure to suit
+ * user-defined privacy lists. Different configured Privacy packages are used in the
+ * server & manager communication in order to:
+ * <ul>
+ * <li>Retrieving one's privacy lists.
+ * <li>Adding, removing, and editing one's privacy lists.
+ * <li>Setting, changing, or declining active lists.
+ * <li>Setting, changing, or declining the default list (i.e., the list that is active by default).
+ * </ul>
+ * Privacy Items can handle different kind of blocking communications based on JID, group,
+ * subscription type or globally {@link PrivacyItem}
+ *
+ * @author Francisco Vives
+ */
+public class Privacy extends IQ {
+ /** declineActiveList is true when the user declines the use of the active list **/
+ private boolean declineActiveList=false;
+ /** activeName is the name associated with the active list set for the session **/
+ private String activeName;
+ /** declineDefaultList is true when the user declines the use of the default list **/
+ private boolean declineDefaultList=false;
+ /** defaultName is the name of the default list that applies to the user as a whole **/
+ private String defaultName;
+ /** itemLists holds the set of privacy items classified in lists. It is a map where the
+ * key is the name of the list and the value a collection with privacy items. **/
+ private Map<String, List<PrivacyItem>> itemLists = new HashMap<String, List<PrivacyItem>>();
+
+ /**
+ * Set or update a privacy list with privacy items.
+ *
+ * @param listName the name of the new privacy list.
+ * @param listItem the {@link PrivacyItem} that rules the list.
+ * @return the privacy List.
+ */
+ public List<PrivacyItem> setPrivacyList(String listName, List<PrivacyItem> listItem) {
+ // Add new list to the itemLists
+ this.getItemLists().put(listName, listItem);
+ return listItem;
+ }
+
+ /**
+ * Set the active list based on the default list.
+ *
+ * @return the active List.
+ */
+ public List<PrivacyItem> setActivePrivacyList() {
+ this.setActiveName(this.getDefaultName());
+ return this.getItemLists().get(this.getActiveName());
+ }
+
+ /**
+ * Deletes an existing privacy list. If the privacy list being deleted was the default list
+ * then the user will end up with no default list. Therefore, the user will have to set a new
+ * default list.
+ *
+ * @param listName the name of the list being deleted.
+ */
+ public void deletePrivacyList(String listName) {
+ // Remove the list from the cache
+ this.getItemLists().remove(listName);
+
+ // Check if deleted list was the default list
+ if (this.getDefaultName() != null && listName.equals(this.getDefaultName())) {
+ this.setDefaultName(null);
+ }
+ }
+
+ /**
+ * Returns the active privacy list or <tt>null</tt> if none was found.
+ *
+ * @return list with {@link PrivacyItem} or <tt>null</tt> if none was found.
+ */
+ public List<PrivacyItem> getActivePrivacyList() {
+ // Check if we have the default list
+ if (this.getActiveName() == null) {
+ return null;
+ } else {
+ return this.getItemLists().get(this.getActiveName());
+ }
+ }
+
+ /**
+ * Returns the default privacy list or <tt>null</tt> if none was found.
+ *
+ * @return list with {@link PrivacyItem} or <tt>null</tt> if none was found.
+ */
+ public List<PrivacyItem> getDefaultPrivacyList() {
+ // Check if we have the default list
+ if (this.getDefaultName() == null) {
+ return null;
+ } else {
+ return this.getItemLists().get(this.getDefaultName());
+ }
+ }
+
+ /**
+ * Returns a specific privacy list.
+ *
+ * @param listName the name of the list to get.
+ * @return a List with {@link PrivacyItem}
+ */
+ public List<PrivacyItem> getPrivacyList(String listName) {
+ return this.getItemLists().get(listName);
+ }
+
+ /**
+ * Returns the privacy item in the specified order.
+ *
+ * @param listName the name of the privacy list.
+ * @param order the order of the element.
+ * @return a List with {@link PrivacyItem}
+ */
+ public PrivacyItem getItem(String listName, int order) {
+ Iterator<PrivacyItem> values = getPrivacyList(listName).iterator();
+ PrivacyItem itemFound = null;
+ while (itemFound == null && values.hasNext()) {
+ PrivacyItem element = values.next();
+ if (element.getOrder() == order) {
+ itemFound = element;
+ }
+ }
+ return itemFound;
+ }
+
+ /**
+ * Sets a given privacy list as the new user default list.
+ *
+ * @param newDefault the new default privacy list.
+ * @return if the default list was changed.
+ */
+ public boolean changeDefaultList(String newDefault) {
+ if (this.getItemLists().containsKey(newDefault)) {
+ this.setDefaultName(newDefault);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Remove the list.
+ *
+ * @param listName name of the list to remove.
+ */
+ public void deleteList(String listName) {
+ this.getItemLists().remove(listName);
+ }
+
+ /**
+ * Returns the name associated with the active list set for the session. Communications
+ * will be verified against the active list.
+ *
+ * @return the name of the active list.
+ */
+ public String getActiveName() {
+ return activeName;
+ }
+
+ /**
+ * Sets the name associated with the active list set for the session. Communications
+ * will be verified against the active list.
+ *
+ * @param activeName is the name of the active list.
+ */
+ public void setActiveName(String activeName) {
+ this.activeName = activeName;
+ }
+
+ /**
+ * Returns the name of the default list that applies to the user as a whole. Default list is
+ * processed if there is no active list set for the target session/resource to which a stanza
+ * is addressed, or if there are no current sessions for the user.
+ *
+ * @return the name of the default list.
+ */
+ public String getDefaultName() {
+ return defaultName;
+ }
+
+ /**
+ * Sets the name of the default list that applies to the user as a whole. Default list is
+ * processed if there is no active list set for the target session/resource to which a stanza
+ * is addressed, or if there are no current sessions for the user.
+ *
+ * If there is no default list set, then all Privacy Items are processed.
+ *
+ * @param defaultName is the name of the default list.
+ */
+ public void setDefaultName(String defaultName) {
+ this.defaultName = defaultName;
+ }
+
+ /**
+ * Returns the collection of privacy list that the user holds. A Privacy List contains a set of
+ * rules that define if communication with the list owner is allowed or denied.
+ * Users may have zero, one or more privacy items.
+ *
+ * @return a map where the key is the name of the list and the value the
+ * collection of privacy items.
+ */
+ public Map<String, List<PrivacyItem>> getItemLists() {
+ return itemLists;
+ }
+
+ /**
+ * Returns whether the receiver allows or declines the use of an active list.
+ *
+ * @return the decline status of the list.
+ */
+ public boolean isDeclineActiveList() {
+ return declineActiveList;
+ }
+
+ /**
+ * Sets whether the receiver allows or declines the use of an active list.
+ *
+ * @param declineActiveList indicates if the receiver declines the use of an active list.
+ */
+ public void setDeclineActiveList(boolean declineActiveList) {
+ this.declineActiveList = declineActiveList;
+ }
+
+ /**
+ * Returns whether the receiver allows or declines the use of a default list.
+ *
+ * @return the decline status of the list.
+ */
+ public boolean isDeclineDefaultList() {
+ return declineDefaultList;
+ }
+
+ /**
+ * Sets whether the receiver allows or declines the use of a default list.
+ *
+ * @param declineDefaultList indicates if the receiver declines the use of a default list.
+ */
+ public void setDeclineDefaultList(boolean declineDefaultList) {
+ this.declineDefaultList = declineDefaultList;
+ }
+
+ /**
+ * Returns all the list names the user has defined to group restrictions.
+ *
+ * @return a Set with Strings containing every list names.
+ */
+ public Set<String> getPrivacyListNames() {
+ return this.itemLists.keySet();
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<query xmlns=\"jabber:iq:privacy\">");
+
+ // Add the active tag
+ if (this.isDeclineActiveList()) {
+ buf.append("<active/>");
+ } else {
+ if (this.getActiveName() != null) {
+ buf.append("<active name=\"").append(this.getActiveName()).append("\"/>");
+ }
+ }
+ // Add the default tag
+ if (this.isDeclineDefaultList()) {
+ buf.append("<default/>");
+ } else {
+ if (this.getDefaultName() != null) {
+ buf.append("<default name=\"").append(this.getDefaultName()).append("\"/>");
+ }
+ }
+
+ // Add the list with their privacy items
+ for (Map.Entry<String, List<PrivacyItem>> entry : this.getItemLists().entrySet()) {
+ String listName = entry.getKey();
+ List<PrivacyItem> items = entry.getValue();
+ // Begin the list tag
+ if (items.isEmpty()) {
+ buf.append("<list name=\"").append(listName).append("\"/>");
+ } else {
+ buf.append("<list name=\"").append(listName).append("\">");
+ }
+ for (PrivacyItem item : items) {
+ // Append the item xml representation
+ buf.append(item.toXML());
+ }
+ // Close the list tag
+ if (!items.isEmpty()) {
+ buf.append("</list>");
+ }
+ }
+
+ // Add packet extensions, if any are defined.
+ buf.append(getExtensionsXML());
+ buf.append("</query>");
+ return buf.toString();
+ }
+
+} \ No newline at end of file
diff --git a/src/org/jivesoftware/smack/packet/PrivacyItem.java b/src/org/jivesoftware/smack/packet/PrivacyItem.java
new file mode 100644
index 0000000..2e144ee
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/PrivacyItem.java
@@ -0,0 +1,462 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+/**
+ * A privacy item acts a rule that when matched defines if a packet should be blocked or not.
+ *
+ * Privacy Items can handle different kind of blocking communications based on JID, group,
+ * subscription type or globally by:<ul>
+ * <li>Allowing or blocking messages.
+ * <li>Allowing or blocking inbound presence notifications.
+ * <li>Allowing or blocking outbound presence notifications.
+ * <li>Allowing or blocking IQ stanzas.
+ * <li>Allowing or blocking all communications.
+ * </ul>
+ * @author Francisco Vives
+ */
+public class PrivacyItem {
+ /** allow is the action associated with the item, it can allow or deny the communication. */
+ private boolean allow;
+ /** order is a non-negative integer that is unique among all items in the list. */
+ private int order;
+ /** rule hold the kind of communication ([jid|group|subscription]) it will allow or block and
+ * identifier to apply the action.
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none". */
+ private PrivacyRule rule;
+
+ /** blocks incoming IQ stanzas. */
+ private boolean filterIQ = false;
+ /** filterMessage blocks incoming message stanzas. */
+ private boolean filterMessage = false;
+ /** blocks incoming presence notifications. */
+ private boolean filterPresence_in = false;
+ /** blocks outgoing presence notifications. */
+ private boolean filterPresence_out = false;
+
+ /**
+ * Creates a new privacy item.
+ *
+ * @param type the type.
+ */
+ public PrivacyItem(String type, boolean allow, int order) {
+ this.setRule(PrivacyRule.fromString(type));
+ this.setAllow(allow);
+ this.setOrder(order);
+ }
+
+ /**
+ * Returns the action associated with the item, it MUST be filled and will allow or deny
+ * the communication.
+ *
+ * @return the allow communication status.
+ */
+ public boolean isAllow() {
+ return allow;
+ }
+
+ /**
+ * Sets the action associated with the item, it can allow or deny the communication.
+ *
+ * @param allow indicates if the receiver allow or deny the communication.
+ */
+ private void setAllow(boolean allow) {
+ this.allow = allow;
+ }
+
+
+ /**
+ * Returns whether the receiver allow or deny incoming IQ stanzas or not.
+ *
+ * @return the iq filtering status.
+ */
+ public boolean isFilterIQ() {
+ return filterIQ;
+ }
+
+
+ /**
+ * Sets whether the receiver allows or denies incoming IQ stanzas or not.
+ *
+ * @param filterIQ indicates if the receiver allows or denies incoming IQ stanzas.
+ */
+ public void setFilterIQ(boolean filterIQ) {
+ this.filterIQ = filterIQ;
+ }
+
+
+ /**
+ * Returns whether the receiver allows or denies incoming messages or not.
+ *
+ * @return the message filtering status.
+ */
+ public boolean isFilterMessage() {
+ return filterMessage;
+ }
+
+
+ /**
+ * Sets wheather the receiver allows or denies incoming messages or not.
+ *
+ * @param filterMessage indicates if the receiver allows or denies incoming messages or not.
+ */
+ public void setFilterMessage(boolean filterMessage) {
+ this.filterMessage = filterMessage;
+ }
+
+
+ /**
+ * Returns whether the receiver allows or denies incoming presence or not.
+ *
+ * @return the iq filtering incoming presence status.
+ */
+ public boolean isFilterPresence_in() {
+ return filterPresence_in;
+ }
+
+
+ /**
+ * Sets whether the receiver allows or denies incoming presence or not.
+ *
+ * @param filterPresence_in indicates if the receiver allows or denies filtering incoming presence.
+ */
+ public void setFilterPresence_in(boolean filterPresence_in) {
+ this.filterPresence_in = filterPresence_in;
+ }
+
+
+ /**
+ * Returns whether the receiver allows or denies incoming presence or not.
+ *
+ * @return the iq filtering incoming presence status.
+ */
+ public boolean isFilterPresence_out() {
+ return filterPresence_out;
+ }
+
+
+ /**
+ * Sets whether the receiver allows or denies outgoing presence or not.
+ *
+ * @param filterPresence_out indicates if the receiver allows or denies filtering outgoing presence
+ */
+ public void setFilterPresence_out(boolean filterPresence_out) {
+ this.filterPresence_out = filterPresence_out;
+ }
+
+
+ /**
+ * Returns the order where the receiver is processed. List items are processed in
+ * ascending order.
+ *
+ * The order MUST be filled and its value MUST be a non-negative integer
+ * that is unique among all items in the list.
+ *
+ * @return the order number.
+ */
+ public int getOrder() {
+ return order;
+ }
+
+
+ /**
+ * Sets the order where the receiver is processed.
+ *
+ * The order MUST be filled and its value MUST be a non-negative integer
+ * that is unique among all items in the list.
+ *
+ * @param order indicates the order in the list.
+ */
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ /**
+ * Sets the element identifier to apply the action.
+ *
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none".
+ *
+ * @param value is the identifier to apply the action.
+ */
+ public void setValue(String value) {
+ if (!(this.getRule() == null && value == null)) {
+ this.getRule().setValue(value);
+ }
+ }
+
+ /**
+ * Returns the type hold the kind of communication it will allow or block.
+ * It MUST be filled with one of these values: jid, group or subscription.
+ *
+ * @return the type of communication it represent.
+ */
+ public Type getType() {
+ if (this.getRule() == null) {
+ return null;
+ } else {
+ return this.getRule().getType();
+ }
+ }
+
+ /**
+ * Returns the element identifier to apply the action.
+ *
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none".
+ *
+ * @return the identifier to apply the action.
+ */
+ public String getValue() {
+ if (this.getRule() == null) {
+ return null;
+ } else {
+ return this.getRule().getValue();
+ }
+ }
+
+
+ /**
+ * Returns whether the receiver allows or denies every kind of communication.
+ *
+ * When filterIQ, filterMessage, filterPresence_in and filterPresence_out are not set
+ * the receiver will block all communications.
+ *
+ * @return the all communications status.
+ */
+ public boolean isFilterEverything() {
+ return !(this.isFilterIQ() || this.isFilterMessage() || this.isFilterPresence_in()
+ || this.isFilterPresence_out());
+ }
+
+
+ private PrivacyRule getRule() {
+ return rule;
+ }
+
+ private void setRule(PrivacyRule rule) {
+ this.rule = rule;
+ }
+ /**
+ * Answer an xml representation of the receiver according to the RFC 3921.
+ *
+ * @return the text xml representation.
+ */
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<item");
+ if (this.isAllow()) {
+ buf.append(" action=\"allow\"");
+ } else {
+ buf.append(" action=\"deny\"");
+ }
+ buf.append(" order=\"").append(getOrder()).append("\"");
+ if (getType() != null) {
+ buf.append(" type=\"").append(getType()).append("\"");
+ }
+ if (getValue() != null) {
+ buf.append(" value=\"").append(getValue()).append("\"");
+ }
+ if (isFilterEverything()) {
+ buf.append("/>");
+ } else {
+ buf.append(">");
+ if (this.isFilterIQ()) {
+ buf.append("<iq/>");
+ }
+ if (this.isFilterMessage()) {
+ buf.append("<message/>");
+ }
+ if (this.isFilterPresence_in()) {
+ buf.append("<presence-in/>");
+ }
+ if (this.isFilterPresence_out()) {
+ buf.append("<presence-out/>");
+ }
+ buf.append("</item>");
+ }
+ return buf.toString();
+ }
+
+
+ /**
+ * Privacy Rule represents the kind of action to apply.
+ * It holds the kind of communication ([jid|group|subscription]) it will allow or block and
+ * identifier to apply the action.
+ */
+
+ public static class PrivacyRule {
+ /**
+ * Type defines if the rule is based on JIDs, roster groups or presence subscription types.
+ * Available values are: [jid|group|subscription]
+ */
+ private Type type;
+ /**
+ * The value hold the element identifier to apply the action.
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none".
+ */
+ private String value;
+
+ /**
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both",
+ * "to", "from", or "none"
+ */
+ public static final String SUBSCRIPTION_BOTH = "both";
+ public static final String SUBSCRIPTION_TO = "to";
+ public static final String SUBSCRIPTION_FROM = "from";
+ public static final String SUBSCRIPTION_NONE = "none";
+
+ /**
+ * Returns the type constant associated with the String value.
+ */
+ protected static PrivacyRule fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ PrivacyRule rule = new PrivacyRule();
+ rule.setType(Type.valueOf(value.toLowerCase()));
+ return rule;
+ }
+
+ /**
+ * Returns the type hold the kind of communication it will allow or block.
+ * It MUST be filled with one of these values: jid, group or subscription.
+ *
+ * @return the type of communication it represent.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Sets the action associated with the item, it can allow or deny the communication.
+ *
+ * @param type indicates if the receiver allows or denies the communication.
+ */
+ private void setType(Type type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the element identifier to apply the action.
+ *
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none".
+ *
+ * @return the identifier to apply the action.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the element identifier to apply the action.
+ *
+ * If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID.
+ * If the type is "group", then the 'value' attribute SHOULD contain the name of a group
+ * in the user's roster.
+ * If the type is "subscription", then the 'value' attribute MUST be one of "both", "to",
+ * "from", or "none".
+ *
+ * @param value is the identifier to apply the action.
+ */
+ protected void setValue(String value) {
+ if (this.isSuscription()) {
+ setSuscriptionValue(value);
+ } else {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Sets the element identifier to apply the action.
+ *
+ * The 'value' attribute MUST be one of "both", "to", "from", or "none".
+ *
+ * @param value is the identifier to apply the action.
+ */
+ private void setSuscriptionValue(String value) {
+ String setValue;
+ if (value == null) {
+ // Do nothing
+ }
+ if (SUBSCRIPTION_BOTH.equalsIgnoreCase(value)) {
+ setValue = SUBSCRIPTION_BOTH;
+ }
+ else if (SUBSCRIPTION_TO.equalsIgnoreCase(value)) {
+ setValue = SUBSCRIPTION_TO;
+ }
+ else if (SUBSCRIPTION_FROM.equalsIgnoreCase(value)) {
+ setValue = SUBSCRIPTION_FROM;
+ }
+ else if (SUBSCRIPTION_NONE.equalsIgnoreCase(value)) {
+ setValue = SUBSCRIPTION_NONE;
+ }
+ // Default to available.
+ else {
+ setValue = null;
+ }
+ this.value = setValue;
+ }
+
+ /**
+ * Returns if the receiver represents a subscription rule.
+ *
+ * @return if the receiver represents a subscription rule.
+ */
+ public boolean isSuscription () {
+ return this.getType() == Type.subscription;
+ }
+ }
+
+ /**
+ * Type defines if the rule is based on JIDs, roster groups or presence subscription types.
+ */
+ public static enum Type {
+ /**
+ * JID being analyzed should belong to a roster group of the list's owner.
+ */
+ group,
+ /**
+ * JID being analyzed should have a resource match, domain match or bare JID match.
+ */
+ jid,
+ /**
+ * JID being analyzed should belong to a contact present in the owner's roster with
+ * the specified subscription status.
+ */
+ subscription
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/Registration.java b/src/org/jivesoftware/smack/packet/Registration.java
new file mode 100644
index 0000000..df22e27
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Registration.java
@@ -0,0 +1,155 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents registration packets. An empty GET query will cause the server to return information
+ * about it's registration support. SET queries can be used to create accounts or update
+ * existing account information. XMPP servers may require a number of attributes to be set
+ * when creating a new account. The standard account attributes are as follows:
+ * <ul>
+ * <li>name -- the user's name.
+ * <li>first -- the user's first name.
+ * <li>last -- the user's last name.
+ * <li>email -- the user's email address.
+ * <li>city -- the user's city.
+ * <li>state -- the user's state.
+ * <li>zip -- the user's ZIP code.
+ * <li>phone -- the user's phone number.
+ * <li>url -- the user's website.
+ * <li>date -- the date the registration took place.
+ * <li>misc -- other miscellaneous information to associate with the account.
+ * <li>text -- textual information to associate with the account.
+ * <li>remove -- empty flag to remove account.
+ * </ul>
+ *
+ * @author Matt Tucker
+ */
+public class Registration extends IQ {
+
+ private String instructions = null;
+ private Map<String, String> attributes = new HashMap<String,String>();
+ private List<String> requiredFields = new ArrayList<String>();
+ private boolean registered = false;
+ private boolean remove = false;
+
+ /**
+ * Returns the registration instructions, or <tt>null</tt> if no instructions
+ * have been set. If present, instructions should be displayed to the end-user
+ * that will complete the registration process.
+ *
+ * @return the registration instructions, or <tt>null</tt> if there are none.
+ */
+ public String getInstructions() {
+ return instructions;
+ }
+
+ /**
+ * Sets the registration instructions.
+ *
+ * @param instructions the registration instructions.
+ */
+ public void setInstructions(String instructions) {
+ this.instructions = instructions;
+ }
+
+ /**
+ * Returns the map of String key/value pairs of account attributes.
+ *
+ * @return the account attributes.
+ */
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Sets the account attributes. The map must only contain String key/value pairs.
+ *
+ * @param attributes the account attributes.
+ */
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+
+ public List<String> getRequiredFields(){
+ return requiredFields;
+ }
+
+ public void addAttribute(String key, String value){
+ attributes.put(key, value);
+ }
+
+ public void setRegistered(boolean registered){
+ this.registered = registered;
+ }
+
+ public boolean isRegistered(){
+ return this.registered;
+ }
+
+ public String getField(String key){
+ return attributes.get(key);
+ }
+
+ public List<String> getFieldNames(){
+ return new ArrayList<String>(attributes.keySet());
+ }
+
+ public void setUsername(String username){
+ attributes.put("username", username);
+ }
+
+ public void setPassword(String password){
+ attributes.put("password", password);
+ }
+
+ public void setRemove(boolean remove){
+ this.remove = remove;
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<query xmlns=\"jabber:iq:register\">");
+ if (instructions != null && !remove) {
+ buf.append("<instructions>").append(instructions).append("</instructions>");
+ }
+ if (attributes != null && attributes.size() > 0 && !remove) {
+ for (String name : attributes.keySet()) {
+ String value = attributes.get(name);
+ buf.append("<").append(name).append(">");
+ buf.append(value);
+ buf.append("</").append(name).append(">");
+ }
+ }
+ else if(remove){
+ buf.append("</remove>");
+ }
+ // Add packet extensions, if any are defined.
+ buf.append(getExtensionsXML());
+ buf.append("</query>");
+ return buf.toString();
+ }
+} \ No newline at end of file
diff --git a/src/org/jivesoftware/smack/packet/RosterPacket.java b/src/org/jivesoftware/smack/packet/RosterPacket.java
new file mode 100644
index 0000000..98483c8
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/RosterPacket.java
@@ -0,0 +1,311 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Represents XMPP roster packets.
+ *
+ * @author Matt Tucker
+ */
+public class RosterPacket extends IQ {
+
+ private final List<Item> rosterItems = new ArrayList<Item>();
+ /*
+ * The ver attribute following XEP-0237
+ */
+ private String version;
+
+ /**
+ * Adds a roster item to the packet.
+ *
+ * @param item a roster item.
+ */
+ public void addRosterItem(Item item) {
+ synchronized (rosterItems) {
+ rosterItems.add(item);
+ }
+ }
+
+ public String getVersion(){
+ return version;
+ }
+
+ public void setVersion(String version){
+ this.version = version;
+ }
+
+ /**
+ * Returns the number of roster items in this roster packet.
+ *
+ * @return the number of roster items.
+ */
+ public int getRosterItemCount() {
+ synchronized (rosterItems) {
+ return rosterItems.size();
+ }
+ }
+
+ /**
+ * Returns an unmodifiable collection for the roster items in the packet.
+ *
+ * @return an unmodifiable collection for the roster items in the packet.
+ */
+ public Collection<Item> getRosterItems() {
+ synchronized (rosterItems) {
+ return Collections.unmodifiableList(new ArrayList<Item>(rosterItems));
+ }
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<query xmlns=\"jabber:iq:roster\" ");
+ if(version!=null){
+ buf.append(" ver=\""+version+"\" ");
+ }
+ buf.append(">");
+ synchronized (rosterItems) {
+ for (Item entry : rosterItems) {
+ buf.append(entry.toXML());
+ }
+ }
+ buf.append("</query>");
+ return buf.toString();
+ }
+
+ /**
+ * A roster item, which consists of a JID, their name, the type of subscription, and
+ * the groups the roster item belongs to.
+ */
+ public static class Item {
+
+ private String user;
+ private String name;
+ private ItemType itemType;
+ private ItemStatus itemStatus;
+ private final Set<String> groupNames;
+
+ /**
+ * Creates a new roster item.
+ *
+ * @param user the user.
+ * @param name the user's name.
+ */
+ public Item(String user, String name) {
+ this.user = user.toLowerCase();
+ this.name = name;
+ itemType = null;
+ itemStatus = null;
+ groupNames = new CopyOnWriteArraySet<String>();
+ }
+
+ /**
+ * Returns the user.
+ *
+ * @return the user.
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * Returns the user's name.
+ *
+ * @return the user's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user's name.
+ *
+ * @param name the user's name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the roster item type.
+ *
+ * @return the roster item type.
+ */
+ public ItemType getItemType() {
+ return itemType;
+ }
+
+ /**
+ * Sets the roster item type.
+ *
+ * @param itemType the roster item type.
+ */
+ public void setItemType(ItemType itemType) {
+ this.itemType = itemType;
+ }
+
+ /**
+ * Returns the roster item status.
+ *
+ * @return the roster item status.
+ */
+ public ItemStatus getItemStatus() {
+ return itemStatus;
+ }
+
+ /**
+ * Sets the roster item status.
+ *
+ * @param itemStatus the roster item status.
+ */
+ public void setItemStatus(ItemStatus itemStatus) {
+ this.itemStatus = itemStatus;
+ }
+
+ /**
+ * Returns an unmodifiable set of the group names that the roster item
+ * belongs to.
+ *
+ * @return an unmodifiable set of the group names.
+ */
+ public Set<String> getGroupNames() {
+ return Collections.unmodifiableSet(groupNames);
+ }
+
+ /**
+ * Adds a group name.
+ *
+ * @param groupName the group name.
+ */
+ public void addGroupName(String groupName) {
+ groupNames.add(groupName);
+ }
+
+ /**
+ * Removes a group name.
+ *
+ * @param groupName the group name.
+ */
+ public void removeGroupName(String groupName) {
+ groupNames.remove(groupName);
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<item jid=\"").append(user).append("\"");
+ if (name != null) {
+ buf.append(" name=\"").append(StringUtils.escapeForXML(name)).append("\"");
+ }
+ if (itemType != null) {
+ buf.append(" subscription=\"").append(itemType).append("\"");
+ }
+ if (itemStatus != null) {
+ buf.append(" ask=\"").append(itemStatus).append("\"");
+ }
+ buf.append(">");
+ for (String groupName : groupNames) {
+ buf.append("<group>").append(StringUtils.escapeForXML(groupName)).append("</group>");
+ }
+ buf.append("</item>");
+ return buf.toString();
+ }
+ }
+
+ /**
+ * The subscription status of a roster item. An optional element that indicates
+ * the subscription status if a change request is pending.
+ */
+ public static class ItemStatus {
+
+ /**
+ * Request to subcribe.
+ */
+ public static final ItemStatus SUBSCRIPTION_PENDING = new ItemStatus("subscribe");
+
+ /**
+ * Request to unsubscribe.
+ */
+ public static final ItemStatus UNSUBSCRIPTION_PENDING = new ItemStatus("unsubscribe");
+
+ public static ItemStatus fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ value = value.toLowerCase();
+ if ("unsubscribe".equals(value)) {
+ return UNSUBSCRIPTION_PENDING;
+ }
+ else if ("subscribe".equals(value)) {
+ return SUBSCRIPTION_PENDING;
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String value;
+
+ /**
+ * Returns the item status associated with the specified string.
+ *
+ * @param value the item status.
+ */
+ private ItemStatus(String value) {
+ this.value = value;
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+
+ public static enum ItemType {
+
+ /**
+ * The user and subscriber have no interest in each other's presence.
+ */
+ none,
+
+ /**
+ * The user is interested in receiving presence updates from the subscriber.
+ */
+ to,
+
+ /**
+ * The subscriber is interested in receiving presence updates from the user.
+ */
+ from,
+
+ /**
+ * The user and subscriber have a mutual interest in each other's presence.
+ */
+ both,
+
+ /**
+ * The user wishes to stop receiving presence updates from the subscriber.
+ */
+ remove
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/Session.java b/src/org/jivesoftware/smack/packet/Session.java
new file mode 100644
index 0000000..fd403ae
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/Session.java
@@ -0,0 +1,45 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+/**
+ * IQ packet that will be sent to the server to establish a session.<p>
+ *
+ * If a server supports sessions, it MUST include a <i>session</i> element in the
+ * stream features it advertises to a client after the completion of stream authentication.
+ * Upon being informed that session establishment is required by the server the client MUST
+ * establish a session if it desires to engage in instant messaging and presence functionality.<p>
+ *
+ * For more information refer to the following
+ * <a href=http://www.xmpp.org/specs/rfc3921.html#session>link</a>.
+ *
+ * @author Gaston Dombiak
+ */
+public class Session extends IQ {
+
+ public Session() {
+ setType(IQ.Type.SET);
+ }
+
+ public String getChildElementXML() {
+ return "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>";
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/StreamError.java b/src/org/jivesoftware/smack/packet/StreamError.java
new file mode 100644
index 0000000..8bb4c75
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/StreamError.java
@@ -0,0 +1,106 @@
+/**
+ * $Revision: 2408 $
+ * $Date: 2004-11-02 20:53:30 -0300 (Tue, 02 Nov 2004) $
+ *
+ * Copyright 2003-2005 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+/**
+ * Represents a stream error packet. Stream errors are unrecoverable errors where the server
+ * will close the unrelying TCP connection after the stream error was sent to the client.
+ * These is the list of stream errors as defined in the XMPP spec:<p>
+ *
+ * <table border=1>
+ * <tr><td><b>Code</b></td><td><b>Description</b></td></tr>
+ * <tr><td> bad-format </td><td> the entity has sent XML that cannot be processed </td></tr>
+ * <tr><td> unsupported-encoding </td><td> the entity has sent a namespace prefix that is
+ * unsupported </td></tr>
+ * <tr><td> bad-namespace-prefix </td><td> Remote Server Timeout </td></tr>
+ * <tr><td> conflict </td><td> the server is closing the active stream for this entity
+ * because a new stream has been initiated that conflicts with the existing
+ * stream. </td></tr>
+ * <tr><td> connection-timeout </td><td> the entity has not generated any traffic over
+ * the stream for some period of time. </td></tr>
+ * <tr><td> host-gone </td><td> the value of the 'to' attribute provided by the initiating
+ * entity in the stream header corresponds to a hostname that is no longer hosted by
+ * the server. </td></tr>
+ * <tr><td> host-unknown </td><td> the value of the 'to' attribute provided by the
+ * initiating entity in the stream header does not correspond to a hostname that is
+ * hosted by the server. </td></tr>
+ * <tr><td> improper-addressing </td><td> a stanza sent between two servers lacks a 'to'
+ * or 'from' attribute </td></tr>
+ * <tr><td> internal-server-error </td><td> the server has experienced a
+ * misconfiguration. </td></tr>
+ * <tr><td> invalid-from </td><td> the JID or hostname provided in a 'from' address does
+ * not match an authorized JID. </td></tr>
+ * <tr><td> invalid-id </td><td> the stream ID or dialback ID is invalid or does not match
+ * an ID previously provided. </td></tr>
+ * <tr><td> invalid-namespace </td><td> the streams namespace name is invalid. </td></tr>
+ * <tr><td> invalid-xml </td><td> the entity has sent invalid XML over the stream. </td></tr>
+ * <tr><td> not-authorized </td><td> the entity has attempted to send data before the
+ * stream has been authenticated </td></tr>
+ * <tr><td> policy-violation </td><td> the entity has violated some local service
+ * policy. </td></tr>
+ * <tr><td> remote-connection-failed </td><td> Rthe server is unable to properly connect
+ * to a remote entity. </td></tr>
+ * <tr><td> resource-constraint </td><td> Rthe server lacks the system resources necessary
+ * to service the stream. </td></tr>
+ * <tr><td> restricted-xml </td><td> the entity has attempted to send restricted XML
+ * features. </td></tr>
+ * <tr><td> see-other-host </td><td> the server will not provide service to the initiating
+ * entity but is redirecting traffic to another host. </td></tr>
+ * <tr><td> system-shutdown </td><td> the server is being shut down and all active streams
+ * are being closed. </td></tr>
+ * <tr><td> undefined-condition </td><td> the error condition is not one of those defined
+ * by the other conditions in this list. </td></tr>
+ * <tr><td> unsupported-encoding </td><td> the initiating entity has encoded the stream in
+ * an encoding that is not supported. </td></tr>
+ * <tr><td> unsupported-stanza-type </td><td> the initiating entity has sent a first-level
+ * child of the stream that is not supported. </td></tr>
+ * <tr><td> unsupported-version </td><td> the value of the 'version' attribute provided by
+ * the initiating entity in the stream header specifies a version of XMPP that is not
+ * supported. </td></tr>
+ * <tr><td> xml-not-well-formed </td><td> the initiating entity has sent XML that is
+ * not well-formed. </td></tr>
+ * </table>
+ *
+ * @author Gaston Dombiak
+ */
+public class StreamError {
+
+ private String code;
+
+ public StreamError(String code) {
+ super();
+ this.code = code;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return the error code.
+ */
+ public String getCode() {
+ return code;
+ }
+
+ public String toString() {
+ StringBuilder txt = new StringBuilder();
+ txt.append("stream:error (").append(code).append(")");
+ return txt.toString();
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/XMPPError.java b/src/org/jivesoftware/smack/packet/XMPPError.java
new file mode 100644
index 0000000..770a09c
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/XMPPError.java
@@ -0,0 +1,453 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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
+ *
+ * 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 org.jivesoftware.smack.packet;
+
+import java.util.*;
+
+/**
+ * Represents a XMPP error sub-packet. Typically, a server responds to a request that has
+ * problems by sending the packet back and including an error packet. Each error has a code, type,
+ * error condition as well as as an optional text explanation. Typical errors are:<p>
+ *
+ * <table border=1>
+ * <hr><td><b>Code</b></td><td><b>XMPP Error</b></td><td><b>Type</b></td></hr>
+ * <tr><td>500</td><td>interna-server-error</td><td>WAIT</td></tr>
+ * <tr><td>403</td><td>forbidden</td><td>AUTH</td></tr>
+ * <tr><td>400</td<td>bad-request</td><td>MODIFY</td>></tr>
+ * <tr><td>404</td><td>item-not-found</td><td>CANCEL</td></tr>
+ * <tr><td>409</td><td>conflict</td><td>CANCEL</td></tr>
+ * <tr><td>501</td><td>feature-not-implemented</td><td>CANCEL</td></tr>
+ * <tr><td>302</td><td>gone</td><td>MODIFY</td></tr>
+ * <tr><td>400</td><td>jid-malformed</td><td>MODIFY</td></tr>
+ * <tr><td>406</td><td>no-acceptable</td><td> MODIFY</td></tr>
+ * <tr><td>405</td><td>not-allowed</td><td>CANCEL</td></tr>
+ * <tr><td>401</td><td>not-authorized</td><td>AUTH</td></tr>
+ * <tr><td>402</td><td>payment-required</td><td>AUTH</td></tr>
+ * <tr><td>404</td><td>recipient-unavailable</td><td>WAIT</td></tr>
+ * <tr><td>302</td><td>redirect</td><td>MODIFY</td></tr>
+ * <tr><td>407</td><td>registration-required</td><td>AUTH</td></tr>
+ * <tr><td>404</td><td>remote-server-not-found</td><td>CANCEL</td></tr>
+ * <tr><td>504</td><td>remote-server-timeout</td><td>WAIT</td></tr>
+ * <tr><td>502</td><td>remote-server-error</td><td>CANCEL</td></tr>
+ * <tr><td>500</td><td>resource-constraint</td><td>WAIT</td></tr>
+ * <tr><td>503</td><td>service-unavailable</td><td>CANCEL</td></tr>
+ * <tr><td>407</td><td>subscription-required</td><td>AUTH</td></tr>
+ * <tr><td>500</td><td>undefined-condition</td><td>WAIT</td></tr>
+ * <tr><td>400</td><td>unexpected-condition</td><td>WAIT</td></tr>
+ * <tr><td>408</td><td>request-timeout</td><td>CANCEL</td></tr>
+ * </table>
+ *
+ * @author Matt Tucker
+ */
+public class XMPPError {
+
+ private int code;
+ private Type type;
+ private String condition;
+ private String message;
+ private List<PacketExtension> applicationExtensions = null;
+
+
+ /**
+ * Creates a new error with the specified condition infering the type and code.
+ * If the Condition is predefined, client code should be like:
+ * new XMPPError(XMPPError.Condition.remote_server_timeout);
+ * If the Condition is not predefined, invocations should be like
+ * new XMPPError(new XMPPError.Condition("my_own_error"));
+ *
+ * @param condition the error condition.
+ */
+ public XMPPError(Condition condition) {
+ this.init(condition);
+ this.message = null;
+ }
+
+ /**
+ * Creates a new error with the specified condition and message infering the type and code.
+ * If the Condition is predefined, client code should be like:
+ * new XMPPError(XMPPError.Condition.remote_server_timeout, "Error Explanation");
+ * If the Condition is not predefined, invocations should be like
+ * new XMPPError(new XMPPError.Condition("my_own_error"), "Error Explanation");
+ *
+ * @param condition the error condition.
+ * @param messageText a message describing the error.
+ */
+ public XMPPError(Condition condition, String messageText) {
+ this.init(condition);
+ this.message = messageText;
+ }
+
+ /**
+ * Creates a new error with the specified code and no message.
+ *
+ * @param code the error code.
+ * @deprecated new errors should be created using the constructor XMPPError(condition)
+ */
+ public XMPPError(int code) {
+ this.code = code;
+ this.message = null;
+ }
+
+ /**
+ * Creates a new error with the specified code and message.
+ * deprecated
+ *
+ * @param code the error code.
+ * @param message a message describing the error.
+ * @deprecated new errors should be created using the constructor XMPPError(condition, message)
+ */
+ public XMPPError(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ /**
+ * Creates a new error with the specified code, type, condition and message.
+ * This constructor is used when the condition is not recognized automatically by XMPPError
+ * i.e. there is not a defined instance of ErrorCondition or it does not applies the default
+ * specification.
+ *
+ * @param code the error code.
+ * @param type the error type.
+ * @param condition the error condition.
+ * @param message a message describing the error.
+ * @param extension list of packet extensions
+ */
+ public XMPPError(int code, Type type, String condition, String message,
+ List<PacketExtension> extension) {
+ this.code = code;
+ this.type = type;
+ this.condition = condition;
+ this.message = message;
+ this.applicationExtensions = extension;
+ }
+
+ /**
+ * Initialize the error infering the type and code for the received condition.
+ *
+ * @param condition the error condition.
+ */
+ private void init(Condition condition) {
+ // Look for the condition and its default code and type
+ ErrorSpecification defaultErrorSpecification = ErrorSpecification.specFor(condition);
+ this.condition = condition.value;
+ if (defaultErrorSpecification != null) {
+ // If there is a default error specification for the received condition,
+ // it get configured with the infered type and code.
+ this.type = defaultErrorSpecification.getType();
+ this.code = defaultErrorSpecification.getCode();
+ }
+ }
+ /**
+ * Returns the error condition.
+ *
+ * @return the error condition.
+ */
+ public String getCondition() {
+ return condition;
+ }
+
+ /**
+ * Returns the error type.
+ *
+ * @return the error type.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return the error code.
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Returns the message describing the error, or null if there is no message.
+ *
+ * @return the message describing the error, or null if there is no message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the error as XML.
+ *
+ * @return the error as XML.
+ */
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<error code=\"").append(code).append("\"");
+ if (type != null) {
+ buf.append(" type=\"");
+ buf.append(type.name());
+ buf.append("\"");
+ }
+ buf.append(">");
+ if (condition != null) {
+ buf.append("<").append(condition);
+ buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>");
+ }
+ if (message != null) {
+ buf.append("<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">");
+ buf.append(message);
+ buf.append("</text>");
+ }
+ for (PacketExtension element : this.getExtensions()) {
+ buf.append(element.toXML());
+ }
+ buf.append("</error>");
+ return buf.toString();
+ }
+
+ public String toString() {
+ StringBuilder txt = new StringBuilder();
+ if (condition != null) {
+ txt.append(condition);
+ }
+ txt.append("(").append(code).append(")");
+ if (message != null) {
+ txt.append(" ").append(message);
+ }
+ return txt.toString();
+ }
+
+ /**
+ * Returns an Iterator for the error extensions attached to the xmppError.
+ * An application MAY provide application-specific error information by including a
+ * properly-namespaced child in the error element.
+ *
+ * @return an Iterator for the error extensions.
+ */
+ public synchronized List<PacketExtension> getExtensions() {
+ if (applicationExtensions == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(applicationExtensions);
+ }
+
+ /**
+ * Returns the first patcket extension that matches the specified element name and
+ * namespace, or <tt>null</tt> if it doesn't exist.
+ *
+ * @param elementName the XML element name of the packet extension.
+ * @param namespace the XML element namespace of the packet extension.
+ * @return the extension, or <tt>null</tt> if it doesn't exist.
+ */
+ public synchronized PacketExtension getExtension(String elementName, String namespace) {
+ if (applicationExtensions == null || elementName == null || namespace == null) {
+ return null;
+ }
+ for (PacketExtension ext : applicationExtensions) {
+ if (elementName.equals(ext.getElementName()) && namespace.equals(ext.getNamespace())) {
+ return ext;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a packet extension to the error.
+ *
+ * @param extension a packet extension.
+ */
+ public synchronized void addExtension(PacketExtension extension) {
+ if (applicationExtensions == null) {
+ applicationExtensions = new ArrayList<PacketExtension>();
+ }
+ applicationExtensions.add(extension);
+ }
+
+ /**
+ * Set the packet extension to the error.
+ *
+ * @param extension a packet extension.
+ */
+ public synchronized void setExtension(List<PacketExtension> extension) {
+ applicationExtensions = extension;
+ }
+
+ /**
+ * A class to represent the type of the Error. The types are:
+ *
+ * <ul>
+ * <li>XMPPError.Type.WAIT - retry after waiting (the error is temporary)
+ * <li>XMPPError.Type.CANCEL - do not retry (the error is unrecoverable)
+ * <li>XMPPError.Type.MODIFY - retry after changing the data sent
+ * <li>XMPPError.Type.AUTH - retry after providing credentials
+ * <li>XMPPError.Type.CONTINUE - proceed (the condition was only a warning)
+ * </ul>
+ */
+ public static enum Type {
+ WAIT,
+ CANCEL,
+ MODIFY,
+ AUTH,
+ CONTINUE
+ }
+
+ /**
+ * A class to represent predefined error conditions.
+ */
+ public static class Condition {
+
+ public static final Condition interna_server_error = new Condition("internal-server-error");
+ public static final Condition forbidden = new Condition("forbidden");
+ public static final Condition bad_request = new Condition("bad-request");
+ public static final Condition conflict = new Condition("conflict");
+ public static final Condition feature_not_implemented = new Condition("feature-not-implemented");
+ public static final Condition gone = new Condition("gone");
+ public static final Condition item_not_found = new Condition("item-not-found");
+ public static final Condition jid_malformed = new Condition("jid-malformed");
+ public static final Condition no_acceptable = new Condition("not-acceptable");
+ public static final Condition not_allowed = new Condition("not-allowed");
+ public static final Condition not_authorized = new Condition("not-authorized");
+ public static final Condition payment_required = new Condition("payment-required");
+ public static final Condition recipient_unavailable = new Condition("recipient-unavailable");
+ public static final Condition redirect = new Condition("redirect");
+ public static final Condition registration_required = new Condition("registration-required");
+ public static final Condition remote_server_error = new Condition("remote-server-error");
+ public static final Condition remote_server_not_found = new Condition("remote-server-not-found");
+ public static final Condition remote_server_timeout = new Condition("remote-server-timeout");
+ public static final Condition resource_constraint = new Condition("resource-constraint");
+ public static final Condition service_unavailable = new Condition("service-unavailable");
+ public static final Condition subscription_required = new Condition("subscription-required");
+ public static final Condition undefined_condition = new Condition("undefined-condition");
+ public static final Condition unexpected_request = new Condition("unexpected-request");
+ public static final Condition request_timeout = new Condition("request-timeout");
+
+ private String value;
+
+ public Condition(String value) {
+ this.value = value;
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+
+
+ /**
+ * A class to represent the error specification used to infer common usage.
+ */
+ private static class ErrorSpecification {
+ private int code;
+ private Type type;
+ private Condition condition;
+ private static Map<Condition, ErrorSpecification> instances = errorSpecifications();
+
+ private ErrorSpecification(Condition condition, Type type, int code) {
+ this.code = code;
+ this.type = type;
+ this.condition = condition;
+ }
+
+ private static Map<Condition, ErrorSpecification> errorSpecifications() {
+ Map<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>(22);
+ instances.put(Condition.interna_server_error, new ErrorSpecification(
+ Condition.interna_server_error, Type.WAIT, 500));
+ instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden,
+ Type.AUTH, 403));
+ instances.put(Condition.bad_request, new XMPPError.ErrorSpecification(
+ Condition.bad_request, Type.MODIFY, 400));
+ instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification(
+ Condition.item_not_found, Type.CANCEL, 404));
+ instances.put(Condition.conflict, new XMPPError.ErrorSpecification(
+ Condition.conflict, Type.CANCEL, 409));
+ instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification(
+ Condition.feature_not_implemented, Type.CANCEL, 501));
+ instances.put(Condition.gone, new XMPPError.ErrorSpecification(
+ Condition.gone, Type.MODIFY, 302));
+ instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification(
+ Condition.jid_malformed, Type.MODIFY, 400));
+ instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification(
+ Condition.no_acceptable, Type.MODIFY, 406));
+ instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification(
+ Condition.not_allowed, Type.CANCEL, 405));
+ instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification(
+ Condition.not_authorized, Type.AUTH, 401));
+ instances.put(Condition.payment_required, new XMPPError.ErrorSpecification(
+ Condition.payment_required, Type.AUTH, 402));
+ instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification(
+ Condition.recipient_unavailable, Type.WAIT, 404));
+ instances.put(Condition.redirect, new XMPPError.ErrorSpecification(
+ Condition.redirect, Type.MODIFY, 302));
+ instances.put(Condition.registration_required, new XMPPError.ErrorSpecification(
+ Condition.registration_required, Type.AUTH, 407));
+ instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification(
+ Condition.remote_server_not_found, Type.CANCEL, 404));
+ instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification(
+ Condition.remote_server_timeout, Type.WAIT, 504));
+ instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification(
+ Condition.remote_server_error, Type.CANCEL, 502));
+ instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification(
+ Condition.resource_constraint, Type.WAIT, 500));
+ instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification(
+ Condition.service_unavailable, Type.CANCEL, 503));
+ instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification(
+ Condition.subscription_required, Type.AUTH, 407));
+ instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification(
+ Condition.undefined_condition, Type.WAIT, 500));
+ instances.put(Condition.unexpected_request, new XMPPError.ErrorSpecification(
+ Condition.unexpected_request, Type.WAIT, 400));
+ instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification(
+ Condition.request_timeout, Type.CANCEL, 408));
+
+ return instances;
+ }
+
+ protected static ErrorSpecification specFor(Condition condition) {
+ return instances.get(condition);
+ }
+
+ /**
+ * Returns the error condition.
+ *
+ * @return the error condition.
+ */
+ protected Condition getCondition() {
+ return condition;
+ }
+
+ /**
+ * Returns the error type.
+ *
+ * @return the error type.
+ */
+ protected Type getType() {
+ return type;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return the error code.
+ */
+ protected int getCode() {
+ return code;
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smack/packet/package.html b/src/org/jivesoftware/smack/packet/package.html
new file mode 100644
index 0000000..18a6405
--- /dev/null
+++ b/src/org/jivesoftware/smack/packet/package.html
@@ -0,0 +1 @@
+<body>XML packets that are part of the XMPP protocol.</body> \ No newline at end of file