Merge remote-tracking branch 'origin/master'

Conflicts:
	releng/org.eclipse.ecf.releng.bm/ecf.rmap
diff --git a/framework/bundles/org.eclipse.ecf.remoteservice.ui/launch/ECF Discovery and Remote Service UI.launch b/framework/bundles/org.eclipse.ecf.remoteservice.ui/launch/ECF Discovery and Remote Service UI.launch
index c6f3897..7c7c705 100644
--- a/framework/bundles/org.eclipse.ecf.remoteservice.ui/launch/ECF Discovery and Remote Service UI.launch
+++ b/framework/bundles/org.eclipse.ecf.remoteservice.ui/launch/ECF Discovery and Remote Service UI.launch
@@ -20,7 +20,7 @@
 <stringAttribute key="pde.version" value="3.3"/>
 <stringAttribute key="product" value="org.eclipse.platform.ide"/>
 <booleanAttribute key="restart" value="false"/>
-<stringAttribute key="selected_target_plugins" value="com.ibm.icu@default:default,org.apache.lucene.analysis@default:default,org.apache.lucene@default:default,org.eclipse.ant.core@default:default,org.eclipse.compare.core@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.net@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.ssl@default:false,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.concurrent@default:default,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.jarprocessor@default:default,org.eclipse.equinox.p2.metadata.repository@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.security@default:default,org.eclipse.help.base@default:default,org.eclipse.help@default:default,org.eclipse.jface.databinding@default:default,org.eclipse.jface.text@default:default,org.eclipse.jface@default:default,org.eclipse.osgi,org.eclipse.osgi.services@default:default,org.eclipse.platform@default:default,org.eclipse.swt@default:default,org.eclipse.text@default:default,org.eclipse.ui.browser@default:default,org.eclipse.ui.cheatsheets@default:default,org.eclipse.ui.forms@default:default,org.eclipse.ui.ide.application@default:default,org.eclipse.ui.ide@default:default,org.eclipse.ui.intro.universal@default:default,org.eclipse.ui.intro@default:default,org.eclipse.ui.navigator.resources@default:default,org.eclipse.ui.navigator@default:default,org.eclipse.ui.views.properties.tabbed@default:default,org.eclipse.ui.views@default:default,org.eclipse.ui.workbench.texteditor@default:default,org.eclipse.ui.workbench@default:default,org.eclipse.ui@default:default,org.objectweb.asm@default:default"/>
+<stringAttribute key="selected_target_plugins" value="com.ibm.icu@default:default,org.apache.lucene.analysis@default:default,org.eclipse.ant.core@default:default,org.eclipse.compare.core@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.net@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.core.runtime@default:true,org.eclipse.core.variables@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.ssl@default:false,org.eclipse.equinox.app@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.concurrent@default:default,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.jarprocessor@default:default,org.eclipse.equinox.p2.metadata.repository@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.security@default:default,org.eclipse.help.base@default:default,org.eclipse.help@default:default,org.eclipse.jface.databinding@default:default,org.eclipse.jface.text@default:default,org.eclipse.jface@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.platform@default:default,org.eclipse.swt@default:default,org.eclipse.text@default:default,org.eclipse.ui.browser@default:default,org.eclipse.ui.cheatsheets@default:default,org.eclipse.ui.forms@default:default,org.eclipse.ui.ide.application@default:default,org.eclipse.ui.ide@default:default,org.eclipse.ui.intro.universal@default:default,org.eclipse.ui.intro@default:default,org.eclipse.ui.navigator.resources@default:default,org.eclipse.ui.navigator@default:default,org.eclipse.ui.views.properties.tabbed@default:default,org.eclipse.ui.views@default:default,org.eclipse.ui.workbench.texteditor@default:default,org.eclipse.ui.workbench@default:default,org.eclipse.ui@default:default,org.objectweb.asm@default:default"/>
 <stringAttribute key="selected_workspace_plugins" value="ch.ethz.iks.r_osgi.remote@default:default,ch.ethz.iks.slp@default:default,org.eclipse.ecf.discovery.ui.browser@default:default,org.eclipse.ecf.discovery.ui.edit@default:default,org.eclipse.ecf.discovery.ui.model@default:default,org.eclipse.ecf.discovery.ui.properties.tabbed@default:false,org.eclipse.ecf.discovery.ui.properties@default:false,org.eclipse.ecf.discovery.ui@default:default,org.eclipse.ecf.discovery@default:default,org.eclipse.ecf.examples.remoteservices.client@default:false,org.eclipse.ecf.examples.remoteservices.common@default:default,org.eclipse.ecf.identity@default:default,org.eclipse.ecf.osgi.services.distribution@default:default,org.eclipse.ecf.provider.discovery@default:default,org.eclipse.ecf.provider.jmdns@default:default,org.eclipse.ecf.provider.jslp@default:default,org.eclipse.ecf.provider.r_osgi@default:default,org.eclipse.ecf.provider.remoteservice@default:default,org.eclipse.ecf.provider@default:default,org.eclipse.ecf.remoteservice.ui@default:default,org.eclipse.ecf.remoteservice@default:default,org.eclipse.ecf.server.generic@default:default,org.eclipse.ecf.sharedobject@default:default,org.eclipse.ecf@default:default,org.eclipse.emf.common.ui@default:default,org.eclipse.emf.common@default:default,org.eclipse.emf.ecore@default:default,org.eclipse.emf.edit.ui@default:default,org.eclipse.emf.edit@default:default"/>
 <booleanAttribute key="show_selected_only" value="false"/>
 <stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/AbstractConnectionListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/AbstractConnectionListener.java
index 69acf90..132da68 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/AbstractConnectionListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/AbstractConnectionListener.java
@@ -1,46 +1,46 @@
-/**

- * 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;

-

-/**

- * The AbstractConnectionListener class provides an empty implementation for all

- * methods defined by the {@link ConnectionListener} interface. This is a

- * convenience class which should be used in case you do not need to implement

- * all methods.

- * 

- * @author Henning Staib

- */

-public class AbstractConnectionListener implements ConnectionListener {

-

-    public void connectionClosed() {

-        // do nothing

-    }

-

-    public void connectionClosedOnError(Exception e) {

-        // do nothing

-    }

-

-    public void reconnectingIn(int seconds) {

-        // do nothing

-    }

-

-    public void reconnectionFailed(Exception e) {

-        // do nothing

-    }

-

-    public void reconnectionSuccessful() {

-        // do nothing

-    }

-

-}

+/**
+ * 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;
+
+/**
+ * The AbstractConnectionListener class provides an empty implementation for all
+ * methods defined by the {@link ConnectionListener} interface. This is a
+ * convenience class which should be used in case you do not need to implement
+ * all methods.
+ * 
+ * @author Henning Staib
+ */
+public class AbstractConnectionListener implements ConnectionListener {
+
+    public void connectionClosed() {
+        // do nothing
+    }
+
+    public void connectionClosedOnError(Exception e) {
+        // do nothing
+    }
+
+    public void reconnectingIn(int seconds) {
+        // do nothing
+    }
+
+    public void reconnectionFailed(Exception e) {
+        // do nothing
+    }
+
+    public void reconnectionSuccessful() {
+        // do nothing
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/NonSASLAuthentication.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/NonSASLAuthentication.java
index d7a479d..93511b7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/NonSASLAuthentication.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/NonSASLAuthentication.java
@@ -1,143 +1,143 @@
-/**

- * $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;

-

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.Authentication;

-import org.jivesoftware.smack.packet.IQ;

-

-import javax.security.auth.callback.CallbackHandler;

-import javax.security.auth.callback.PasswordCallback;

-import javax.security.auth.callback.Callback;

-

-/**

- * Implementation of JEP-0078: Non-SASL Authentication. Follow the following

- * <a href=http://www.jabber.org/jeps/jep-0078.html>link</a> to obtain more

- * information about the JEP.

- *

- * @author Gaston Dombiak

- */

-class NonSASLAuthentication implements UserAuthentication {

-

-    private Connection connection;

-

-    public NonSASLAuthentication(Connection connection) {

-        super();

-        this.connection = connection;

-    }

-

-    public String authenticate(String username, String resource, CallbackHandler cbh) throws XMPPException {

-        //Use the callback handler to determine the password, and continue on.

-        PasswordCallback pcb = new PasswordCallback("Password: ",false);

-        try {

-            cbh.handle(new Callback[]{pcb});

-            return authenticate(username, String.valueOf(pcb.getPassword()),resource);

-        } catch (Exception e) {

-            throw new XMPPException("Unable to determine password.",e);

-        }   

-    }

-

-    public String authenticate(String username, String password, String resource) throws

-            XMPPException {

-        // If we send an authentication packet in "get" mode with just the username,

-        // the server will return the list of authentication protocols it supports.

-        Authentication discoveryAuth = new Authentication();

-        discoveryAuth.setType(IQ.Type.GET);

-        discoveryAuth.setUsername(username);

-

-        PacketCollector collector =

-            connection.createPacketCollector(new PacketIDFilter(discoveryAuth.getPacketID()));

-        // Send the packet

-        connection.sendPacket(discoveryAuth);

-        // Wait up to a certain number of seconds for a response from the server.

-        IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        if (response == null) {

-            throw new XMPPException("No response from the server.");

-        }

-        // If the server replied with an error, throw an exception.

-        else if (response.getType() == IQ.Type.ERROR) {

-            throw new XMPPException(response.getError());

-        }

-        // Otherwise, no error so continue processing.

-        Authentication authTypes = (Authentication) response;

-        collector.cancel();

-

-        // Now, create the authentication packet we'll send to the server.

-        Authentication auth = new Authentication();

-        auth.setUsername(username);

-

-        // Figure out if we should use digest or plain text authentication.

-        if (authTypes.getDigest() != null) {

-            auth.setDigest(connection.getConnectionID(), password);

-        }

-        else if (authTypes.getPassword() != null) {

-            auth.setPassword(password);

-        }

-        else {

-            throw new XMPPException("Server does not support compatible authentication mechanism.");

-        }

-

-        auth.setResource(resource);

-

-        collector = connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));

-        // Send the packet.

-        connection.sendPacket(auth);

-        // Wait up to a certain number of seconds for a response from the server.

-        response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        if (response == null) {

-            throw new XMPPException("Authentication failed.");

-        }

-        else if (response.getType() == IQ.Type.ERROR) {

-            throw new XMPPException(response.getError());

-        }

-        // We're done with the collector, so explicitly cancel it.

-        collector.cancel();

-

-        return response.getTo();

-    }

-

-    public String authenticateAnonymously() throws XMPPException {

-        // Create the authentication packet we'll send to the server.

-        Authentication auth = new Authentication();

-

-        PacketCollector collector =

-            connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));

-        // Send the packet.

-        connection.sendPacket(auth);

-        // Wait up to a certain number of seconds for a response from the server.

-        IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        if (response == null) {

-            throw new XMPPException("Anonymous login failed.");

-        }

-        else if (response.getType() == IQ.Type.ERROR) {

-            throw new XMPPException(response.getError());

-        }

-        // We're done with the collector, so explicitly cancel it.

-        collector.cancel();

-

-        if (response.getTo() != null) {

-            return response.getTo();

-        }

-        else {

-            return connection.getServiceName() + "/" + ((Authentication) response).getResource();

-        }

-    }

-}

+/**
+ * $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;
+
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.Authentication;
+import org.jivesoftware.smack.packet.IQ;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.Callback;
+
+/**
+ * Implementation of JEP-0078: Non-SASL Authentication. Follow the following
+ * <a href=http://www.jabber.org/jeps/jep-0078.html>link</a> to obtain more
+ * information about the JEP.
+ *
+ * @author Gaston Dombiak
+ */
+class NonSASLAuthentication implements UserAuthentication {
+
+    private Connection connection;
+
+    public NonSASLAuthentication(Connection connection) {
+        super();
+        this.connection = connection;
+    }
+
+    public String authenticate(String username, String resource, CallbackHandler cbh) throws XMPPException {
+        //Use the callback handler to determine the password, and continue on.
+        PasswordCallback pcb = new PasswordCallback("Password: ",false);
+        try {
+            cbh.handle(new Callback[]{pcb});
+            return authenticate(username, String.valueOf(pcb.getPassword()),resource);
+        } catch (Exception e) {
+            throw new XMPPException("Unable to determine password.",e);
+        }   
+    }
+
+    public String authenticate(String username, String password, String resource) throws
+            XMPPException {
+        // If we send an authentication packet in "get" mode with just the username,
+        // the server will return the list of authentication protocols it supports.
+        Authentication discoveryAuth = new Authentication();
+        discoveryAuth.setType(IQ.Type.GET);
+        discoveryAuth.setUsername(username);
+
+        PacketCollector collector =
+            connection.createPacketCollector(new PacketIDFilter(discoveryAuth.getPacketID()));
+        // Send the packet
+        connection.sendPacket(discoveryAuth);
+        // Wait up to a certain number of seconds for a response from the server.
+        IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        if (response == null) {
+            throw new XMPPException("No response from the server.");
+        }
+        // If the server replied with an error, throw an exception.
+        else if (response.getType() == IQ.Type.ERROR) {
+            throw new XMPPException(response.getError());
+        }
+        // Otherwise, no error so continue processing.
+        Authentication authTypes = (Authentication) response;
+        collector.cancel();
+
+        // Now, create the authentication packet we'll send to the server.
+        Authentication auth = new Authentication();
+        auth.setUsername(username);
+
+        // Figure out if we should use digest or plain text authentication.
+        if (authTypes.getDigest() != null) {
+            auth.setDigest(connection.getConnectionID(), password);
+        }
+        else if (authTypes.getPassword() != null) {
+            auth.setPassword(password);
+        }
+        else {
+            throw new XMPPException("Server does not support compatible authentication mechanism.");
+        }
+
+        auth.setResource(resource);
+
+        collector = connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
+        // Send the packet.
+        connection.sendPacket(auth);
+        // Wait up to a certain number of seconds for a response from the server.
+        response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        if (response == null) {
+            throw new XMPPException("Authentication failed.");
+        }
+        else if (response.getType() == IQ.Type.ERROR) {
+            throw new XMPPException(response.getError());
+        }
+        // We're done with the collector, so explicitly cancel it.
+        collector.cancel();
+
+        return response.getTo();
+    }
+
+    public String authenticateAnonymously() throws XMPPException {
+        // Create the authentication packet we'll send to the server.
+        Authentication auth = new Authentication();
+
+        PacketCollector collector =
+            connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
+        // Send the packet.
+        connection.sendPacket(auth);
+        // Wait up to a certain number of seconds for a response from the server.
+        IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        if (response == null) {
+            throw new XMPPException("Anonymous login failed.");
+        }
+        else if (response.getType() == IQ.Type.ERROR) {
+            throw new XMPPException(response.getError());
+        }
+        // We're done with the collector, so explicitly cancel it.
+        collector.cancel();
+
+        if (response.getTo() != null) {
+            return response.getTo();
+        }
+        else {
+            return connection.getServiceName() + "/" + ((Authentication) response).getResource();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/OpenTrustManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/OpenTrustManager.java
index 61ed8c6..2e47b1f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/OpenTrustManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/OpenTrustManager.java
@@ -1,49 +1,49 @@
-/**

- * $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;

-

-import javax.net.ssl.X509TrustManager;

-import java.security.cert.CertificateException;

-import java.security.cert.X509Certificate;

-

-/**

- * Dummy trust manager that trust all certificates presented by the server. This class

- * is used during old SSL connections.

- *

- * @author Gaston Dombiak

- */

-class OpenTrustManager implements X509TrustManager {

-

-    public OpenTrustManager() {

-    }

-

-    public X509Certificate[] getAcceptedIssuers() {

-        return new X509Certificate[0];

-    }

-

-    public void checkClientTrusted(X509Certificate[] arg0, String arg1)

-            throws CertificateException {

-    }

-

-    public void checkServerTrusted(X509Certificate[] arg0, String arg1)

-            throws CertificateException {

-    }

+/**
+ * $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;
+
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Dummy trust manager that trust all certificates presented by the server. This class
+ * is used during old SSL connections.
+ *
+ * @author Gaston Dombiak
+ */
+class OpenTrustManager implements X509TrustManager {
+
+    public OpenTrustManager() {
+    }
+
+    public X509Certificate[] getAcceptedIssuers() {
+        return new X509Certificate[0];
+    }
+
+    public void checkClientTrusted(X509Certificate[] arg0, String arg1)
+            throws CertificateException {
+    }
+
+    public void checkServerTrusted(X509Certificate[] arg0, String arg1)
+            throws CertificateException {
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PacketInterceptor.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PacketInterceptor.java
index bd89031..af56326 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PacketInterceptor.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PacketInterceptor.java
@@ -1,49 +1,49 @@
-/**

- * $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;

-

-import org.jivesoftware.smack.packet.Packet;

-

-/**

- * Provides a mechanism to intercept and modify packets that are going to be

- * sent to the server. PacketInterceptors are added to the {@link Connection}

- * together with a {@link org.jivesoftware.smack.filter.PacketFilter} so that only

- * certain packets are intercepted and processed by the interceptor.<p>

- *

- * This allows event-style programming -- every time a new packet is found,

- * the {@link #interceptPacket(Packet)} method will be called.

- *

- * @see Connection#addPacketInterceptor(PacketInterceptor, org.jivesoftware.smack.filter.PacketFilter)

- * @author Gaston Dombiak

- */

-public interface PacketInterceptor {

-

-    /**

-     * Process the packet that is about to be sent to the server. The intercepted

-     * packet can be modified by the interceptor.<p>

-     *

-     * Interceptors are invoked using the same thread that requested the packet

-     * to be sent, so it's very important that implementations of this method

-     * not block for any extended period of time.

-     *

-     * @param packet the packet to is going to be sent to the server.

-     */

-    public void interceptPacket(Packet packet);

-}

+/**
+ * $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;
+
+import org.jivesoftware.smack.packet.Packet;
+
+/**
+ * Provides a mechanism to intercept and modify packets that are going to be
+ * sent to the server. PacketInterceptors are added to the {@link Connection}
+ * together with a {@link org.jivesoftware.smack.filter.PacketFilter} so that only
+ * certain packets are intercepted and processed by the interceptor.<p>
+ *
+ * This allows event-style programming -- every time a new packet is found,
+ * the {@link #interceptPacket(Packet)} method will be called.
+ *
+ * @see Connection#addPacketInterceptor(PacketInterceptor, org.jivesoftware.smack.filter.PacketFilter)
+ * @author Gaston Dombiak
+ */
+public interface PacketInterceptor {
+
+    /**
+     * Process the packet that is about to be sent to the server. The intercepted
+     * packet can be modified by the interceptor.<p>
+     *
+     * Interceptors are invoked using the same thread that requested the packet
+     * to be sent, so it's very important that implementations of this method
+     * not block for any extended period of time.
+     *
+     * @param packet the packet to is going to be sent to the server.
+     */
+    public void interceptPacket(Packet packet);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyList.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyList.java
index 67d731d..e2d9f5e 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyList.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyList.java
@@ -15,60 +15,60 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smack;

-

-import org.jivesoftware.smack.packet.PrivacyItem;

-

-import java.util.List;

-

-/**

- * A privacy list represents a list of contacts that is a read only class used to represent a set of allowed or blocked communications. 

- * Basically it can:<ul>

- *

- *      <li>Handle many {@link org.jivesoftware.smack.packet.PrivacyItem}.</li>

- *      <li>Answer if it is the default list.</li>

- *      <li>Answer if it is the active list.</li>

- * </ul>

- *

- * {@link PrivacyItem Privacy Items} can handle different kind of blocking communications based on JID, group,

- * subscription type or globally.

- * 

- * @author Francisco Vives

- */

-public class PrivacyList {

-

-    /** Holds if it is an active list or not **/

-    private boolean isActiveList;

-    /** Holds if it is an default list or not **/

-    private boolean isDefaultList;

-    /** Holds the list name used to print **/

-    private String listName;

-    /** Holds the list of {@see PrivacyItem} **/

-    private List<PrivacyItem> items;

-    

-    protected PrivacyList(boolean isActiveList, boolean isDefaultList,

-            String listName, List<PrivacyItem> privacyItems) {

-        super();

-        this.isActiveList = isActiveList;

-        this.isDefaultList = isDefaultList;

-        this.listName = listName;

-        this.items = privacyItems;

-    }

-

-    public boolean isActiveList() {

-        return isActiveList;

-    }

-

-    public boolean isDefaultList() {

-        return isDefaultList;

-    }

-

-    public List<PrivacyItem> getItems() {

-        return items;

-    }

-

-    public String toString() {

-        return listName;

-    }

-

-}

+package org.jivesoftware.smack;
+
+import org.jivesoftware.smack.packet.PrivacyItem;
+
+import java.util.List;
+
+/**
+ * A privacy list represents a list of contacts that is a read only class used to represent a set of allowed or blocked communications. 
+ * Basically it can:<ul>
+ *
+ *      <li>Handle many {@link org.jivesoftware.smack.packet.PrivacyItem}.</li>
+ *      <li>Answer if it is the default list.</li>
+ *      <li>Answer if it is the active list.</li>
+ * </ul>
+ *
+ * {@link PrivacyItem Privacy Items} can handle different kind of blocking communications based on JID, group,
+ * subscription type or globally.
+ * 
+ * @author Francisco Vives
+ */
+public class PrivacyList {
+
+    /** Holds if it is an active list or not **/
+    private boolean isActiveList;
+    /** Holds if it is an default list or not **/
+    private boolean isDefaultList;
+    /** Holds the list name used to print **/
+    private String listName;
+    /** Holds the list of {@see PrivacyItem} **/
+    private List<PrivacyItem> items;
+    
+    protected PrivacyList(boolean isActiveList, boolean isDefaultList,
+            String listName, List<PrivacyItem> privacyItems) {
+        super();
+        this.isActiveList = isActiveList;
+        this.isDefaultList = isDefaultList;
+        this.listName = listName;
+        this.items = privacyItems;
+    }
+
+    public boolean isActiveList() {
+        return isActiveList;
+    }
+
+    public boolean isDefaultList() {
+        return isDefaultList;
+    }
+
+    public List<PrivacyItem> getItems() {
+        return items;
+    }
+
+    public String toString() {
+        return listName;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListListener.java
index 5644ed7..20683f8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListListener.java
@@ -1,51 +1,51 @@
-/**

- * $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;

-

-import org.jivesoftware.smack.packet.PrivacyItem;

-

-import java.util.List;

-

-/**

- * Interface to implement classes to listen for server events about privacy communication. 

- * Listeners are registered with the {@link PrivacyListManager}.

- *

- * @see PrivacyListManager#addListener

- * 

- * @author Francisco Vives

- */

-public interface PrivacyListListener {

-

-    /**

-     * Set or update a privacy list with PrivacyItem.

-     *

-     * @param listName the name of the new or updated privacy list.

-     * @param listItem the PrivacyItems that rules the list.

-     */

-    public void setPrivacyList(String listName, List<PrivacyItem> listItem);

-

-    /**

-     * A privacy list has been modified by another. It gets notified.

-     *

-     * @param listName the name of the updated privacy list.

-     */

-    public void updatedPrivacyList(String listName);

-

+/**
+ * $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;
+
+import org.jivesoftware.smack.packet.PrivacyItem;
+
+import java.util.List;
+
+/**
+ * Interface to implement classes to listen for server events about privacy communication. 
+ * Listeners are registered with the {@link PrivacyListManager}.
+ *
+ * @see PrivacyListManager#addListener
+ * 
+ * @author Francisco Vives
+ */
+public interface PrivacyListListener {
+
+    /**
+     * Set or update a privacy list with PrivacyItem.
+     *
+     * @param listName the name of the new or updated privacy list.
+     * @param listItem the PrivacyItems that rules the list.
+     */
+    public void setPrivacyList(String listName, List<PrivacyItem> listItem);
+
+    /**
+     * A privacy list has been modified by another. It gets notified.
+     *
+     * @param listName the name of the updated privacy list.
+     */
+    public void updatedPrivacyList(String listName);
+
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListManager.java
index eb1d231..77b066a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/PrivacyListManager.java
@@ -1,466 +1,466 @@
-/**

- * $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;

-

-import org.jivesoftware.smack.filter.*;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.Privacy;

-import org.jivesoftware.smack.packet.PrivacyItem;

-

-import java.util.*;

-

-/**

- * A PrivacyListManager is used by XMPP clients to block or allow communications from other

- * users. Use the manager to: <ul>

- *      <li>Retrieve privacy lists.

- *      <li>Add, remove, and edit privacy lists.

- *      <li>Set, change, or decline active lists.

- *      <li>Set, change, or decline the default list (i.e., the list that is active by default).

- * </ul>

- * Privacy Items can handle different kind of permission communications based on JID, group, 

- * subscription type or globally (@see PrivacyItem).

- * 

- * @author Francisco Vives

- */

-public class PrivacyListManager {

-

-    // Keep the list of instances of this class.

-	private static Map<Connection, PrivacyListManager> instances = new Hashtable<Connection, PrivacyListManager>();

-

-	private Connection connection;

-	private final List<PrivacyListListener> listeners = new ArrayList<PrivacyListListener>();

-	PacketFilter packetFilter = new AndFilter(new IQTypeFilter(IQ.Type.SET),

-    		new PacketExtensionFilter("query", "jabber:iq:privacy"));

-

-    static {

-        // Create a new PrivacyListManager on every established connection. In the init()

-        // method of PrivacyListManager, we'll add a listener that will delete the

-        // instance when the connection is closed.

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-            public void connectionCreated(Connection connection) {

-                new PrivacyListManager(connection);

-            }

-        });

-    }

-    /**

-     * Creates a new privacy manager to maintain the communication privacy. Note: no

-     * information is sent to or received from the server until you attempt to 

-     * get or set the privacy communication.<p>

-     *

-     * @param connection the XMPP connection.

-     */

-	private PrivacyListManager(Connection connection) {

-        this.connection = connection;

-        this.init();

-    }

-

-	/** Answer the connection userJID that owns the privacy.

-	 * @return the userJID that owns the privacy

-	 */

-	private String getUser() {

-		return connection.getUser();

-	}

-

-    /**

-     * Initializes the packet listeners of the connection that will notify for any set privacy 

-     * package. 

-     */

-    private void init() {

-        // Register the new instance and associate it with the connection 

-        instances.put(connection, this);

-        // Add a listener to the connection that removes the registered instance when

-        // the connection is closed

-        connection.addConnectionListener(new ConnectionListener() {

-            public void connectionClosed() {

-                // Unregister this instance since the connection has been closed

-                instances.remove(connection);

-            }

-

-            public void connectionClosedOnError(Exception e) {

-                // ignore

-            }

-

-            public void reconnectionFailed(Exception e) {

-                // ignore

-            }

-

-            public void reconnectingIn(int seconds) {

-                // ignore

-            }

-

-            public void reconnectionSuccessful() {

-                // ignore

-            }

-        });

-

-        connection.addPacketListener(new PacketListener() {

-            public void processPacket(Packet packet) {

-

-                if (packet == null || packet.getError() != null) {

-                    return;

-                }

-                // The packet is correct.

-                Privacy privacy = (Privacy) packet;

-                

-                // Notifies the event to the listeners.

-                synchronized (listeners) {

-                    for (PrivacyListListener listener : listeners) {

-                        // Notifies the created or updated privacy lists

-                        for (Map.Entry<String,List<PrivacyItem>> entry : privacy.getItemLists().entrySet()) {

-                            String listName = entry.getKey();

-                            List<PrivacyItem> items = entry.getValue();

-                            if (items.isEmpty()) {

-                                listener.updatedPrivacyList(listName);

-                            } else {

-                                listener.setPrivacyList(listName, items);

-                            }

-                        }

-                    }

-                }

-                

-                // Send a result package acknowledging the reception of a privacy package.

-                

-                // Prepare the IQ packet to send

-                IQ iq = new IQ() {

-                    public String getChildElementXML() {

-                        return "";

-                    }

-                };

-                iq.setType(IQ.Type.RESULT);

-                iq.setFrom(packet.getFrom());

-                iq.setPacketID(packet.getPacketID());

-

-                // Send create & join packet.

-                connection.sendPacket(iq);

-            }

-        }, packetFilter);

-    }

-

-    /**

-     * Returns the PrivacyListManager instance associated with a given Connection.

-     * 

-     * @param connection the connection used to look for the proper PrivacyListManager.

-     * @return the PrivacyListManager associated with a given Connection.

-     */

-    public static PrivacyListManager getInstanceFor(Connection connection) {

-        return instances.get(connection);

-    }

-    

-	/**

-	 * Send the {@link Privacy} packet to the server in order to know some privacy content and then 

-	 * waits for the answer.

-	 * 

-	 * @param requestPrivacy is the {@link Privacy} packet configured properly whose XML

-     *      will be sent to the server.

-	 * @return a new {@link Privacy} with the data received from the server.

-	 * @exception XMPPException if the request or the answer failed, it raises an exception.

-	 */ 

-	private Privacy getRequest(Privacy requestPrivacy) throws XMPPException {

-		// The request is a get iq type

-		requestPrivacy.setType(Privacy.Type.GET);

-		requestPrivacy.setFrom(this.getUser());

-		

-		// Filter packets looking for an answer from the server.

-		PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());

-        PacketCollector response = connection.createPacketCollector(responseFilter);

-        

-        // Send create & join packet.

-        connection.sendPacket(requestPrivacy);

-        

-        // Wait up to a certain number of seconds for a reply.

-        Privacy privacyAnswer =

-            (Privacy) response.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        

-        // Stop queuing results

-        response.cancel();

-

-        // Interprete the result and answer the privacy only if it is valid

-        if (privacyAnswer == null) {

-            throw new XMPPException("No response from server.");

-        }

-        else if (privacyAnswer.getError() != null) {

-            throw new XMPPException(privacyAnswer.getError());

-        }

-        return privacyAnswer;

-	}

-	

-	/**

-	 * Send the {@link Privacy} packet to the server in order to modify the server privacy and 

-	 * waits for the answer.

-	 * 

-	 * @param requestPrivacy is the {@link Privacy} packet configured properly whose xml will be sent

-	 * to the server.

-	 * @return a new {@link Privacy} with the data received from the server.

-	 * @exception XMPPException if the request or the answer failed, it raises an exception.

-	 */ 

-	private Packet setRequest(Privacy requestPrivacy) throws XMPPException {

-		

-		// The request is a get iq type

-		requestPrivacy.setType(Privacy.Type.SET);

-		requestPrivacy.setFrom(this.getUser());

-		

-		// Filter packets looking for an answer from the server.

-		PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());

-        PacketCollector response = connection.createPacketCollector(responseFilter);

-        

-        // Send create & join packet.

-        connection.sendPacket(requestPrivacy);

-        

-        // Wait up to a certain number of seconds for a reply.

-        Packet privacyAnswer = response.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        

-        // Stop queuing results

-        response.cancel();

-

-        // Interprete the result and answer the privacy only if it is valid

-        if (privacyAnswer == null) {

-            throw new XMPPException("No response from server.");

-        } else if (privacyAnswer.getError() != null) {

-            throw new XMPPException(privacyAnswer.getError());

-        }

-        return privacyAnswer;

-	}

-

-	/**

-	 * Answer a privacy containing the list structre without {@link PrivacyItem}.

-	 * 

-	 * @return a Privacy with the list names.

-     * @throws XMPPException if an error occurs.

-	 */ 

-	private Privacy getPrivacyWithListNames() throws XMPPException {

-		

-		// The request of the list is an empty privacy message

-		Privacy request = new Privacy();

-		

-		// Send the package to the server and get the answer

-		return getRequest(request);

-	}

-	

-    /**

-     * Answer the active privacy list.

-     * 

-     * @return the privacy list of the active list.

-     * @throws XMPPException if an error occurs.

-     */ 

-    public PrivacyList getActiveList() throws XMPPException {

-        Privacy privacyAnswer = this.getPrivacyWithListNames();

-        String listName = privacyAnswer.getActiveName();

-        boolean isDefaultAndActive = privacyAnswer.getActiveName() != null

-                && privacyAnswer.getDefaultName() != null

-                && privacyAnswer.getActiveName().equals(

-                privacyAnswer.getDefaultName());

-        return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName));

-    }

-    

-    /**

-     * Answer the default privacy list.

-     * 

-     * @return the privacy list of the default list.

-     * @throws XMPPException if an error occurs.

-     */ 

-    public PrivacyList getDefaultList() throws XMPPException {

-        Privacy privacyAnswer = this.getPrivacyWithListNames();

-        String listName = privacyAnswer.getDefaultName();

-        boolean isDefaultAndActive = privacyAnswer.getActiveName() != null

-                && privacyAnswer.getDefaultName() != null

-                && privacyAnswer.getActiveName().equals(

-                privacyAnswer.getDefaultName());

-        return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName));

-    }

-    

-    /**

-     * Answer the privacy list items under listName with the allowed and blocked permissions.

-     * 

-     * @param listName the name of the list to get the allowed and blocked permissions.

-     * @return a list of privacy items under the list listName.

-     * @throws XMPPException if an error occurs.

-     */ 

-    private List<PrivacyItem> getPrivacyListItems(String listName) throws XMPPException {

-        

-        // The request of the list is an privacy message with an empty list

-        Privacy request = new Privacy();

-        request.setPrivacyList(listName, new ArrayList<PrivacyItem>());

-        

-        // Send the package to the server and get the answer

-        Privacy privacyAnswer = getRequest(request);

-        

-        return privacyAnswer.getPrivacyList(listName);

-    }

-    

-	/**

-	 * Answer the privacy list items under listName with the allowed and blocked permissions.

-	 * 

-	 * @param listName the name of the list to get the allowed and blocked permissions.

-	 * @return a privacy list under the list listName.

-     * @throws XMPPException if an error occurs.

-	 */ 

-	public PrivacyList getPrivacyList(String listName) throws XMPPException {

-		

-        return new PrivacyList(false, false, listName, getPrivacyListItems(listName));

-	}

-	

-    /**

-     * Answer every privacy list with the allowed and blocked permissions.

-     * 

-     * @return an array of privacy lists.

-     * @throws XMPPException if an error occurs.

-     */ 

-    public PrivacyList[] getPrivacyLists() throws XMPPException {

-        Privacy privacyAnswer = this.getPrivacyWithListNames();

-        Set<String> names = privacyAnswer.getPrivacyListNames();

-        PrivacyList[] lists = new PrivacyList[names.size()];

-        boolean isActiveList;

-        boolean isDefaultList;

-        int index=0;

-        for (String listName : names) {

-            isActiveList = listName.equals(privacyAnswer.getActiveName());

-            isDefaultList = listName.equals(privacyAnswer.getDefaultName());

-            lists[index] = new PrivacyList(isActiveList, isDefaultList,

-                    listName, getPrivacyListItems(listName));

-            index = index + 1;

-        }

-        return lists;

-    }

-

-    

-	/**

-	 * Set or change the active list to listName.

-	 * 

-	 * @param listName the list name to set as the active one.

-	 * @exception XMPPException if the request or the answer failed, it raises an exception.

-	 */ 

-	public void setActiveListName(String listName) throws XMPPException {

-		

-		// The request of the list is an privacy message with an empty list

-		Privacy request = new Privacy();

-		request.setActiveName(listName);

-		

-		// Send the package to the server

-		setRequest(request);

-	}

-

-	/**

-	 * Client declines the use of active lists.

-     *

-     * @throws XMPPException if an error occurs.

-	 */ 

-	public void declineActiveList() throws XMPPException {

-		

-		// The request of the list is an privacy message with an empty list

-		Privacy request = new Privacy();

-		request.setDeclineActiveList(true);

-		

-		// Send the package to the server

-		setRequest(request);

-	}

-

-	/**

-	 * Set or change the default list to listName.

-	 * 

-	 * @param listName the list name to set as the default one.

-	 * @exception XMPPException if the request or the answer failed, it raises an exception.

-	 */ 

-	public void setDefaultListName(String listName) throws XMPPException {

-		

-		// The request of the list is an privacy message with an empty list

-		Privacy request = new Privacy();

-		request.setDefaultName(listName);

-		

-		// Send the package to the server

-		setRequest(request);

-	}

-	

-	/**

-	 * Client declines the use of default lists.

-     *

-     * @throws XMPPException if an error occurs.

-	 */ 

-	public void declineDefaultList() throws XMPPException {

-		

-		// The request of the list is an privacy message with an empty list

-		Privacy request = new Privacy();

-		request.setDeclineDefaultList(true);

-		

-		// Send the package to the server

-		setRequest(request);

-	}

-	

-	/**

-	 * The client has created a new list. It send the new one to the server.

-	 * 

-     * @param listName the list that has changed its content.

-     * @param privacyItems a List with every privacy item in the list.

-     * @throws XMPPException if an error occurs.

-	 */ 

-	public void createPrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {

-

-		this.updatePrivacyList(listName, privacyItems);

-	}

-

-    /**

-     * The client has edited an existing list. It updates the server content with the resulting 

-     * list of privacy items. The {@link PrivacyItem} list MUST contain all elements in the 

-     * list (not the "delta").

-     * 

-     * @param listName the list that has changed its content.

-     * @param privacyItems a List with every privacy item in the list.

-     * @throws XMPPException if an error occurs.

-     */ 

-    public void updatePrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {

-

-        // Build the privacy package to add or update the new list

-        Privacy request = new Privacy();

-        request.setPrivacyList(listName, privacyItems);

-

-        // Send the package to the server

-        setRequest(request);

-    }

-    

-	/**

-	 * Remove a privacy list.

-	 * 

-     * @param listName the list that has changed its content.

-     * @throws XMPPException if an error occurs.

-	 */ 

-	public void deletePrivacyList(String listName) throws XMPPException {

-		

-		// The request of the list is an privacy message with an empty list

-		Privacy request = new Privacy();

-		request.setPrivacyList(listName, new ArrayList<PrivacyItem>());

-

-		// Send the package to the server

-		setRequest(request);

-	}

-	

-    /**

-     * Adds a packet listener that will be notified of any new update in the user

-     * privacy communication.

-     *

-     * @param listener a packet listener.

-     */

-    public void addListener(PrivacyListListener listener) {

-        // Keep track of the listener so that we can manually deliver extra

-        // messages to it later if needed.

-        synchronized (listeners) {

-            listeners.add(listener);

-        }

-    }    

-}

+/**
+ * $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;
+
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.Privacy;
+import org.jivesoftware.smack.packet.PrivacyItem;
+
+import java.util.*;
+
+/**
+ * A PrivacyListManager is used by XMPP clients to block or allow communications from other
+ * users. Use the manager to: <ul>
+ *      <li>Retrieve privacy lists.
+ *      <li>Add, remove, and edit privacy lists.
+ *      <li>Set, change, or decline active lists.
+ *      <li>Set, change, or decline the default list (i.e., the list that is active by default).
+ * </ul>
+ * Privacy Items can handle different kind of permission communications based on JID, group, 
+ * subscription type or globally (@see PrivacyItem).
+ * 
+ * @author Francisco Vives
+ */
+public class PrivacyListManager {
+
+    // Keep the list of instances of this class.
+	private static Map<Connection, PrivacyListManager> instances = new Hashtable<Connection, PrivacyListManager>();
+
+	private Connection connection;
+	private final List<PrivacyListListener> listeners = new ArrayList<PrivacyListListener>();
+	PacketFilter packetFilter = new AndFilter(new IQTypeFilter(IQ.Type.SET),
+    		new PacketExtensionFilter("query", "jabber:iq:privacy"));
+
+    static {
+        // Create a new PrivacyListManager on every established connection. In the init()
+        // method of PrivacyListManager, we'll add a listener that will delete the
+        // instance when the connection is closed.
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+            public void connectionCreated(Connection connection) {
+                new PrivacyListManager(connection);
+            }
+        });
+    }
+    /**
+     * Creates a new privacy manager to maintain the communication privacy. Note: no
+     * information is sent to or received from the server until you attempt to 
+     * get or set the privacy communication.<p>
+     *
+     * @param connection the XMPP connection.
+     */
+	private PrivacyListManager(Connection connection) {
+        this.connection = connection;
+        this.init();
+    }
+
+	/** Answer the connection userJID that owns the privacy.
+	 * @return the userJID that owns the privacy
+	 */
+	private String getUser() {
+		return connection.getUser();
+	}
+
+    /**
+     * Initializes the packet listeners of the connection that will notify for any set privacy 
+     * package. 
+     */
+    private void init() {
+        // Register the new instance and associate it with the connection 
+        instances.put(connection, this);
+        // Add a listener to the connection that removes the registered instance when
+        // the connection is closed
+        connection.addConnectionListener(new ConnectionListener() {
+            public void connectionClosed() {
+                // Unregister this instance since the connection has been closed
+                instances.remove(connection);
+            }
+
+            public void connectionClosedOnError(Exception e) {
+                // ignore
+            }
+
+            public void reconnectionFailed(Exception e) {
+                // ignore
+            }
+
+            public void reconnectingIn(int seconds) {
+                // ignore
+            }
+
+            public void reconnectionSuccessful() {
+                // ignore
+            }
+        });
+
+        connection.addPacketListener(new PacketListener() {
+            public void processPacket(Packet packet) {
+
+                if (packet == null || packet.getError() != null) {
+                    return;
+                }
+                // The packet is correct.
+                Privacy privacy = (Privacy) packet;
+                
+                // Notifies the event to the listeners.
+                synchronized (listeners) {
+                    for (PrivacyListListener listener : listeners) {
+                        // Notifies the created or updated privacy lists
+                        for (Map.Entry<String,List<PrivacyItem>> entry : privacy.getItemLists().entrySet()) {
+                            String listName = entry.getKey();
+                            List<PrivacyItem> items = entry.getValue();
+                            if (items.isEmpty()) {
+                                listener.updatedPrivacyList(listName);
+                            } else {
+                                listener.setPrivacyList(listName, items);
+                            }
+                        }
+                    }
+                }
+                
+                // Send a result package acknowledging the reception of a privacy package.
+                
+                // Prepare the IQ packet to send
+                IQ iq = new IQ() {
+                    public String getChildElementXML() {
+                        return "";
+                    }
+                };
+                iq.setType(IQ.Type.RESULT);
+                iq.setFrom(packet.getFrom());
+                iq.setPacketID(packet.getPacketID());
+
+                // Send create & join packet.
+                connection.sendPacket(iq);
+            }
+        }, packetFilter);
+    }
+
+    /**
+     * Returns the PrivacyListManager instance associated with a given Connection.
+     * 
+     * @param connection the connection used to look for the proper PrivacyListManager.
+     * @return the PrivacyListManager associated with a given Connection.
+     */
+    public static PrivacyListManager getInstanceFor(Connection connection) {
+        return instances.get(connection);
+    }
+    
+	/**
+	 * Send the {@link Privacy} packet to the server in order to know some privacy content and then 
+	 * waits for the answer.
+	 * 
+	 * @param requestPrivacy is the {@link Privacy} packet configured properly whose XML
+     *      will be sent to the server.
+	 * @return a new {@link Privacy} with the data received from the server.
+	 * @exception XMPPException if the request or the answer failed, it raises an exception.
+	 */ 
+	private Privacy getRequest(Privacy requestPrivacy) throws XMPPException {
+		// The request is a get iq type
+		requestPrivacy.setType(Privacy.Type.GET);
+		requestPrivacy.setFrom(this.getUser());
+		
+		// Filter packets looking for an answer from the server.
+		PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());
+        PacketCollector response = connection.createPacketCollector(responseFilter);
+        
+        // Send create & join packet.
+        connection.sendPacket(requestPrivacy);
+        
+        // Wait up to a certain number of seconds for a reply.
+        Privacy privacyAnswer =
+            (Privacy) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        
+        // Stop queuing results
+        response.cancel();
+
+        // Interprete the result and answer the privacy only if it is valid
+        if (privacyAnswer == null) {
+            throw new XMPPException("No response from server.");
+        }
+        else if (privacyAnswer.getError() != null) {
+            throw new XMPPException(privacyAnswer.getError());
+        }
+        return privacyAnswer;
+	}
+	
+	/**
+	 * Send the {@link Privacy} packet to the server in order to modify the server privacy and 
+	 * waits for the answer.
+	 * 
+	 * @param requestPrivacy is the {@link Privacy} packet configured properly whose xml will be sent
+	 * to the server.
+	 * @return a new {@link Privacy} with the data received from the server.
+	 * @exception XMPPException if the request or the answer failed, it raises an exception.
+	 */ 
+	private Packet setRequest(Privacy requestPrivacy) throws XMPPException {
+		
+		// The request is a get iq type
+		requestPrivacy.setType(Privacy.Type.SET);
+		requestPrivacy.setFrom(this.getUser());
+		
+		// Filter packets looking for an answer from the server.
+		PacketFilter responseFilter = new PacketIDFilter(requestPrivacy.getPacketID());
+        PacketCollector response = connection.createPacketCollector(responseFilter);
+        
+        // Send create & join packet.
+        connection.sendPacket(requestPrivacy);
+        
+        // Wait up to a certain number of seconds for a reply.
+        Packet privacyAnswer = response.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        
+        // Stop queuing results
+        response.cancel();
+
+        // Interprete the result and answer the privacy only if it is valid
+        if (privacyAnswer == null) {
+            throw new XMPPException("No response from server.");
+        } else if (privacyAnswer.getError() != null) {
+            throw new XMPPException(privacyAnswer.getError());
+        }
+        return privacyAnswer;
+	}
+
+	/**
+	 * Answer a privacy containing the list structre without {@link PrivacyItem}.
+	 * 
+	 * @return a Privacy with the list names.
+     * @throws XMPPException if an error occurs.
+	 */ 
+	private Privacy getPrivacyWithListNames() throws XMPPException {
+		
+		// The request of the list is an empty privacy message
+		Privacy request = new Privacy();
+		
+		// Send the package to the server and get the answer
+		return getRequest(request);
+	}
+	
+    /**
+     * Answer the active privacy list.
+     * 
+     * @return the privacy list of the active list.
+     * @throws XMPPException if an error occurs.
+     */ 
+    public PrivacyList getActiveList() throws XMPPException {
+        Privacy privacyAnswer = this.getPrivacyWithListNames();
+        String listName = privacyAnswer.getActiveName();
+        boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
+                && privacyAnswer.getDefaultName() != null
+                && privacyAnswer.getActiveName().equals(
+                privacyAnswer.getDefaultName());
+        return new PrivacyList(true, isDefaultAndActive, listName, getPrivacyListItems(listName));
+    }
+    
+    /**
+     * Answer the default privacy list.
+     * 
+     * @return the privacy list of the default list.
+     * @throws XMPPException if an error occurs.
+     */ 
+    public PrivacyList getDefaultList() throws XMPPException {
+        Privacy privacyAnswer = this.getPrivacyWithListNames();
+        String listName = privacyAnswer.getDefaultName();
+        boolean isDefaultAndActive = privacyAnswer.getActiveName() != null
+                && privacyAnswer.getDefaultName() != null
+                && privacyAnswer.getActiveName().equals(
+                privacyAnswer.getDefaultName());
+        return new PrivacyList(isDefaultAndActive, true, listName, getPrivacyListItems(listName));
+    }
+    
+    /**
+     * Answer the privacy list items under listName with the allowed and blocked permissions.
+     * 
+     * @param listName the name of the list to get the allowed and blocked permissions.
+     * @return a list of privacy items under the list listName.
+     * @throws XMPPException if an error occurs.
+     */ 
+    private List<PrivacyItem> getPrivacyListItems(String listName) throws XMPPException {
+        
+        // The request of the list is an privacy message with an empty list
+        Privacy request = new Privacy();
+        request.setPrivacyList(listName, new ArrayList<PrivacyItem>());
+        
+        // Send the package to the server and get the answer
+        Privacy privacyAnswer = getRequest(request);
+        
+        return privacyAnswer.getPrivacyList(listName);
+    }
+    
+	/**
+	 * Answer the privacy list items under listName with the allowed and blocked permissions.
+	 * 
+	 * @param listName the name of the list to get the allowed and blocked permissions.
+	 * @return a privacy list under the list listName.
+     * @throws XMPPException if an error occurs.
+	 */ 
+	public PrivacyList getPrivacyList(String listName) throws XMPPException {
+		
+        return new PrivacyList(false, false, listName, getPrivacyListItems(listName));
+	}
+	
+    /**
+     * Answer every privacy list with the allowed and blocked permissions.
+     * 
+     * @return an array of privacy lists.
+     * @throws XMPPException if an error occurs.
+     */ 
+    public PrivacyList[] getPrivacyLists() throws XMPPException {
+        Privacy privacyAnswer = this.getPrivacyWithListNames();
+        Set<String> names = privacyAnswer.getPrivacyListNames();
+        PrivacyList[] lists = new PrivacyList[names.size()];
+        boolean isActiveList;
+        boolean isDefaultList;
+        int index=0;
+        for (String listName : names) {
+            isActiveList = listName.equals(privacyAnswer.getActiveName());
+            isDefaultList = listName.equals(privacyAnswer.getDefaultName());
+            lists[index] = new PrivacyList(isActiveList, isDefaultList,
+                    listName, getPrivacyListItems(listName));
+            index = index + 1;
+        }
+        return lists;
+    }
+
+    
+	/**
+	 * Set or change the active list to listName.
+	 * 
+	 * @param listName the list name to set as the active one.
+	 * @exception XMPPException if the request or the answer failed, it raises an exception.
+	 */ 
+	public void setActiveListName(String listName) throws XMPPException {
+		
+		// The request of the list is an privacy message with an empty list
+		Privacy request = new Privacy();
+		request.setActiveName(listName);
+		
+		// Send the package to the server
+		setRequest(request);
+	}
+
+	/**
+	 * Client declines the use of active lists.
+     *
+     * @throws XMPPException if an error occurs.
+	 */ 
+	public void declineActiveList() throws XMPPException {
+		
+		// The request of the list is an privacy message with an empty list
+		Privacy request = new Privacy();
+		request.setDeclineActiveList(true);
+		
+		// Send the package to the server
+		setRequest(request);
+	}
+
+	/**
+	 * Set or change the default list to listName.
+	 * 
+	 * @param listName the list name to set as the default one.
+	 * @exception XMPPException if the request or the answer failed, it raises an exception.
+	 */ 
+	public void setDefaultListName(String listName) throws XMPPException {
+		
+		// The request of the list is an privacy message with an empty list
+		Privacy request = new Privacy();
+		request.setDefaultName(listName);
+		
+		// Send the package to the server
+		setRequest(request);
+	}
+	
+	/**
+	 * Client declines the use of default lists.
+     *
+     * @throws XMPPException if an error occurs.
+	 */ 
+	public void declineDefaultList() throws XMPPException {
+		
+		// The request of the list is an privacy message with an empty list
+		Privacy request = new Privacy();
+		request.setDeclineDefaultList(true);
+		
+		// Send the package to the server
+		setRequest(request);
+	}
+	
+	/**
+	 * The client has created a new list. It send the new one to the server.
+	 * 
+     * @param listName the list that has changed its content.
+     * @param privacyItems a List with every privacy item in the list.
+     * @throws XMPPException if an error occurs.
+	 */ 
+	public void createPrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {
+
+		this.updatePrivacyList(listName, privacyItems);
+	}
+
+    /**
+     * The client has edited an existing list. It updates the server content with the resulting 
+     * list of privacy items. The {@link PrivacyItem} list MUST contain all elements in the 
+     * list (not the "delta").
+     * 
+     * @param listName the list that has changed its content.
+     * @param privacyItems a List with every privacy item in the list.
+     * @throws XMPPException if an error occurs.
+     */ 
+    public void updatePrivacyList(String listName, List<PrivacyItem> privacyItems) throws XMPPException {
+
+        // Build the privacy package to add or update the new list
+        Privacy request = new Privacy();
+        request.setPrivacyList(listName, privacyItems);
+
+        // Send the package to the server
+        setRequest(request);
+    }
+    
+	/**
+	 * Remove a privacy list.
+	 * 
+     * @param listName the list that has changed its content.
+     * @throws XMPPException if an error occurs.
+	 */ 
+	public void deletePrivacyList(String listName) throws XMPPException {
+		
+		// The request of the list is an privacy message with an empty list
+		Privacy request = new Privacy();
+		request.setPrivacyList(listName, new ArrayList<PrivacyItem>());
+
+		// Send the package to the server
+		setRequest(request);
+	}
+	
+    /**
+     * Adds a packet listener that will be notified of any new update in the user
+     * privacy communication.
+     *
+     * @param listener a packet listener.
+     */
+    public void addListener(PrivacyListListener listener) {
+        // Keep track of the listener so that we can manually deliver extra
+        // messages to it later if needed.
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+    }    
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/ReconnectionManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/ReconnectionManager.java
index cc3e3af..7e3c1de 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/ReconnectionManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/ReconnectionManager.java
@@ -15,213 +15,213 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smack;

-

-import org.jivesoftware.smack.packet.StreamError;

-import java.util.Random;

-/**

- * Handles the automatic reconnection process. Every time a connection is dropped without

- * the application explictly closing it, the manager automatically tries to reconnect to

- * the server.<p>

- *

- * The reconnection mechanism will try to reconnect periodically:

- * <ol>

- *  <li>For the first minute it will attempt to connect once every ten seconds.

- *  <li>For the next five minutes it will attempt to connect once a minute.

- *  <li>If that fails it will indefinitely try to connect once every five minutes.

- * </ol>

- *

- * @author Francisco Vives

- */

-public class ReconnectionManager implements ConnectionListener {

-

-    // Holds the connection to the server

-    private Connection connection;

-    private Thread reconnectionThread;

-    private int randomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds

-    

-    // Holds the state of the reconnection

-    boolean done = false;

-

-    static {

-        // Create a new PrivacyListManager on every established connection. In the init()

-        // method of PrivacyListManager, we'll add a listener that will delete the

-        // instance when the connection is closed.

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-            public void connectionCreated(Connection connection) {

-                connection.addConnectionListener(new ReconnectionManager(connection));

-            }

-        });

-    }

-

-    private ReconnectionManager(Connection connection) {

-        this.connection = connection;

-    }

-

-

-    /**

-     * Returns true if the reconnection mechanism is enabled.

-     *

-     * @return true if automatic reconnections are allowed.

-     */

-    private boolean isReconnectionAllowed() {

+package org.jivesoftware.smack;
+
+import org.jivesoftware.smack.packet.StreamError;
+import java.util.Random;
+/**
+ * Handles the automatic reconnection process. Every time a connection is dropped without
+ * the application explictly closing it, the manager automatically tries to reconnect to
+ * the server.<p>
+ *
+ * The reconnection mechanism will try to reconnect periodically:
+ * <ol>
+ *  <li>For the first minute it will attempt to connect once every ten seconds.
+ *  <li>For the next five minutes it will attempt to connect once a minute.
+ *  <li>If that fails it will indefinitely try to connect once every five minutes.
+ * </ol>
+ *
+ * @author Francisco Vives
+ */
+public class ReconnectionManager implements ConnectionListener {
+
+    // Holds the connection to the server
+    private Connection connection;
+    private Thread reconnectionThread;
+    private int randomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds
+    
+    // Holds the state of the reconnection
+    boolean done = false;
+
+    static {
+        // Create a new PrivacyListManager on every established connection. In the init()
+        // method of PrivacyListManager, we'll add a listener that will delete the
+        // instance when the connection is closed.
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+            public void connectionCreated(Connection connection) {
+                connection.addConnectionListener(new ReconnectionManager(connection));
+            }
+        });
+    }
+
+    private ReconnectionManager(Connection connection) {
+        this.connection = connection;
+    }
+
+
+    /**
+     * Returns true if the reconnection mechanism is enabled.
+     *
+     * @return true if automatic reconnections are allowed.
+     */
+    private boolean isReconnectionAllowed() {
         return !done && !connection.isConnected()
                 && connection.isReconnectionAllowed();
-    }

-

-    /**

-     * Starts a reconnection mechanism if it was configured to do that.

-     * The algorithm is been executed when the first connection error is detected.

-     * <p/>

-     * The reconnection mechanism will try to reconnect periodically in this way:

-     * <ol>

-     * <li>First it will try 6 times every 10 seconds.

-     * <li>Then it will try 10 times every 1 minute.

-     * <li>Finally it will try indefinitely every 5 minutes.

-     * </ol>

-     */

-    synchronized protected void reconnect() {

-        if (this.isReconnectionAllowed()) {

-            // Since there is no thread running, creates a new one to attempt

-            // the reconnection.

-            // avoid to run duplicated reconnectionThread -- fd: 16/09/2010

-            if (reconnectionThread!=null && reconnectionThread.isAlive()) return;

-            

-            reconnectionThread = new Thread() {

-             			

-                /**

-                 * Holds the current number of reconnection attempts

-                 */

-                private int attempts = 0;

-

-                /**

-                 * Returns the number of seconds until the next reconnection attempt.

-                 *

-                 * @return the number of seconds until the next reconnection attempt.

-                 */

-                private int timeDelay() {

-                    attempts++;

-                    if (attempts > 13) {

-                	return randomBase*6*5;      // between 2.5 and 7.5 minutes (~5 minutes)

-                    }

-                    if (attempts > 7) {

-                	return randomBase*6;       // between 30 and 90 seconds (~1 minutes)

-                    }

-                    return randomBase;       // 10 seconds

-                }

-

-                /**

-                 * The process will try the reconnection until the connection succeed or the user

-                 * cancell it

-                 */

-                public void run() {

-                    // The process will try to reconnect until the connection is established or

-                    // the user cancel the reconnection process {@link Connection#disconnect()}

-                    while (ReconnectionManager.this.isReconnectionAllowed()) {

-                        // Find how much time we should wait until the next reconnection

-                        int remainingSeconds = timeDelay();

-                        // Sleep until we're ready for the next reconnection attempt. Notify

-                        // listeners once per second about how much time remains before the next

-                        // reconnection attempt.

-                        while (ReconnectionManager.this.isReconnectionAllowed() &&

-                                remainingSeconds > 0)

-                        {

-                            try {

-                                Thread.sleep(1000);

-                                remainingSeconds--;

-                                ReconnectionManager.this

-                                        .notifyAttemptToReconnectIn(remainingSeconds);

-                            }

-                            catch (InterruptedException e1) {

-                                e1.printStackTrace();

-                                // Notify the reconnection has failed

-                                ReconnectionManager.this.notifyReconnectionFailed(e1);

-                            }

-                        }

-

-                        // Makes a reconnection attempt

-                        try {

-                            if (ReconnectionManager.this.isReconnectionAllowed()) {

-                                connection.connect();

-                            }

-                        }

-                        catch (XMPPException e) {

-                            // Fires the failed reconnection notification

-                            ReconnectionManager.this.notifyReconnectionFailed(e);

-                        }

-                    }

-                }

-            };

-            reconnectionThread.setName("Smack Reconnection Manager");

-            reconnectionThread.setDaemon(true);

-            reconnectionThread.start();

-        }

-    }

-

-    /**

-     * Fires listeners when a reconnection attempt has failed.

-     *

-     * @param exception the exception that occured.

-     */

-    protected void notifyReconnectionFailed(Exception exception) {

+    }
+
+    /**
+     * Starts a reconnection mechanism if it was configured to do that.
+     * The algorithm is been executed when the first connection error is detected.
+     * <p/>
+     * The reconnection mechanism will try to reconnect periodically in this way:
+     * <ol>
+     * <li>First it will try 6 times every 10 seconds.
+     * <li>Then it will try 10 times every 1 minute.
+     * <li>Finally it will try indefinitely every 5 minutes.
+     * </ol>
+     */
+    synchronized protected void reconnect() {
+        if (this.isReconnectionAllowed()) {
+            // Since there is no thread running, creates a new one to attempt
+            // the reconnection.
+            // avoid to run duplicated reconnectionThread -- fd: 16/09/2010
+            if (reconnectionThread!=null && reconnectionThread.isAlive()) return;
+            
+            reconnectionThread = new Thread() {
+             			
+                /**
+                 * Holds the current number of reconnection attempts
+                 */
+                private int attempts = 0;
+
+                /**
+                 * Returns the number of seconds until the next reconnection attempt.
+                 *
+                 * @return the number of seconds until the next reconnection attempt.
+                 */
+                private int timeDelay() {
+                    attempts++;
+                    if (attempts > 13) {
+                	return randomBase*6*5;      // between 2.5 and 7.5 minutes (~5 minutes)
+                    }
+                    if (attempts > 7) {
+                	return randomBase*6;       // between 30 and 90 seconds (~1 minutes)
+                    }
+                    return randomBase;       // 10 seconds
+                }
+
+                /**
+                 * The process will try the reconnection until the connection succeed or the user
+                 * cancell it
+                 */
+                public void run() {
+                    // The process will try to reconnect until the connection is established or
+                    // the user cancel the reconnection process {@link Connection#disconnect()}
+                    while (ReconnectionManager.this.isReconnectionAllowed()) {
+                        // Find how much time we should wait until the next reconnection
+                        int remainingSeconds = timeDelay();
+                        // Sleep until we're ready for the next reconnection attempt. Notify
+                        // listeners once per second about how much time remains before the next
+                        // reconnection attempt.
+                        while (ReconnectionManager.this.isReconnectionAllowed() &&
+                                remainingSeconds > 0)
+                        {
+                            try {
+                                Thread.sleep(1000);
+                                remainingSeconds--;
+                                ReconnectionManager.this
+                                        .notifyAttemptToReconnectIn(remainingSeconds);
+                            }
+                            catch (InterruptedException e1) {
+                                e1.printStackTrace();
+                                // Notify the reconnection has failed
+                                ReconnectionManager.this.notifyReconnectionFailed(e1);
+                            }
+                        }
+
+                        // Makes a reconnection attempt
+                        try {
+                            if (ReconnectionManager.this.isReconnectionAllowed()) {
+                                connection.connect();
+                            }
+                        }
+                        catch (XMPPException e) {
+                            // Fires the failed reconnection notification
+                            ReconnectionManager.this.notifyReconnectionFailed(e);
+                        }
+                    }
+                }
+            };
+            reconnectionThread.setName("Smack Reconnection Manager");
+            reconnectionThread.setDaemon(true);
+            reconnectionThread.start();
+        }
+    }
+
+    /**
+     * Fires listeners when a reconnection attempt has failed.
+     *
+     * @param exception the exception that occured.
+     */
+    protected void notifyReconnectionFailed(Exception exception) {
         if (isReconnectionAllowed()) {
             for (ConnectionListener listener : connection.connectionListeners) {
-                listener.reconnectionFailed(exception);

-            }

-        }

-    }

-

-    /**

-     * Fires listeners when The Connection will retry a reconnection. Expressed in seconds.

-     *

-     * @param seconds the number of seconds that a reconnection will be attempted in.

-     */

-    protected void notifyAttemptToReconnectIn(int seconds) {

+                listener.reconnectionFailed(exception);
+            }
+        }
+    }
+
+    /**
+     * Fires listeners when The Connection will retry a reconnection. Expressed in seconds.
+     *
+     * @param seconds the number of seconds that a reconnection will be attempted in.
+     */
+    protected void notifyAttemptToReconnectIn(int seconds) {
         if (isReconnectionAllowed()) {
             for (ConnectionListener listener : connection.connectionListeners) {
-                listener.reconnectingIn(seconds);

-            }

-        }

-    }

-

-    public void connectionClosed() {

-        done = true;

-    }

-

-    public void connectionClosedOnError(Exception e) {

-        done = false;

-        if (e instanceof XMPPException) {

-            XMPPException xmppEx = (XMPPException) e;

-            StreamError error = xmppEx.getStreamError();

-

-            // Make sure the error is not null

-            if (error != null) {

-                String reason = error.getCode();

-

-                if ("conflict".equals(reason)) {

-                    return;

-                }

-            }

-        }

-

-        if (this.isReconnectionAllowed()) {

-            this.reconnect();

-        }

-    }

-

-    public void reconnectingIn(int seconds) {

-        // ignore

-    }

-

-    public void reconnectionFailed(Exception e) {

-        // ignore

-    }

-

-    /**

-     * The connection has successfull gotten connected.

-     */

-    public void reconnectionSuccessful() {

-        // ignore

-    }

-

+                listener.reconnectingIn(seconds);
+            }
+        }
+    }
+
+    public void connectionClosed() {
+        done = true;
+    }
+
+    public void connectionClosedOnError(Exception e) {
+        done = false;
+        if (e instanceof XMPPException) {
+            XMPPException xmppEx = (XMPPException) e;
+            StreamError error = xmppEx.getStreamError();
+
+            // Make sure the error is not null
+            if (error != null) {
+                String reason = error.getCode();
+
+                if ("conflict".equals(reason)) {
+                    return;
+                }
+            }
+        }
+
+        if (this.isReconnectionAllowed()) {
+            this.reconnect();
+        }
+    }
+
+    public void reconnectingIn(int seconds) {
+        // ignore
+    }
+
+    public void reconnectionFailed(Exception e) {
+        // ignore
+    }
+
+    /**
+     * The connection has successfull gotten connected.
+     */
+    public void reconnectionSuccessful() {
+        // ignore
+    }
+
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SASLAuthentication.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SASLAuthentication.java
index 6f8b5bf..abf08c8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SASLAuthentication.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SASLAuthentication.java
@@ -1,591 +1,591 @@
-/**

- * $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;

-

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.Bind;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.Session;

-import org.jivesoftware.smack.sasl.*;

-

-import javax.security.auth.callback.CallbackHandler;

-import java.io.IOException;

-import java.lang.reflect.Constructor;

-import java.util.*;

-

-/**

- * <p>This class is responsible authenticating the user using SASL, binding the resource

- * to the connection and establishing a session with the server.</p>

- *

- * <p>Once TLS has been negotiated (i.e. the connection has been secured) it is possible to

- * register with the server, authenticate using Non-SASL or authenticate using SASL. If the

- * server supports SASL then Smack will first try to authenticate using SASL. But if that

- * fails then Non-SASL will be tried.</p>

- *

- * <p>The server may support many SASL mechanisms to use for authenticating. Out of the box

- * Smack provides several SASL mechanisms, but it is possible to register new SASL Mechanisms. Use

- * {@link #registerSASLMechanism(String, Class)} to register a new mechanisms. A registered

- * mechanism wont be used until {@link #supportSASLMechanism(String, int)} is called. By default,

- * the list of supported SASL mechanisms is determined from the {@link SmackConfiguration}. </p>

- *

- * <p>Once the user has been authenticated with SASL, it is necessary to bind a resource for

- * the connection. If no resource is passed in {@link #authenticate(String, String, String)}

- * then the server will assign a resource for the connection. In case a resource is passed

- * then the server will receive the desired resource but may assign a modified resource for

- * the connection.</p>

- *

- * <p>Once a resource has been binded and if the server supports sessions then Smack will establish

- * a session so that instant messaging and presence functionalities may be used.</p>

- *

- * @see org.jivesoftware.smack.sasl.SASLMechanism

- *

- * @author Gaston Dombiak

- * @author Jay Kline

- */

-public class SASLAuthentication implements UserAuthentication {

-

-    private static Map<String, Class<? extends SASLMechanism>> implementedMechanisms = new HashMap<String, Class<? extends SASLMechanism>>();

-    private static List<String> mechanismsPreferences = new ArrayList<String>();

-

-    private Connection connection;

-    private Collection<String> serverMechanisms = new ArrayList<String>();

-    private SASLMechanism currentMechanism = null;

-    /**

-     * Boolean indicating if SASL negotiation has finished and was successful.

-     */

-    private boolean saslNegotiated;

-    /**

-     * Boolean indication if SASL authentication has failed. When failed the server may end

-     * the connection.

-     */

-    private boolean saslFailed;

-    private boolean resourceBinded;

-    private boolean sessionSupported;

-    /**

-     * The SASL related error condition if there was one provided by the server.

-     */

-    private String errorCondition;

-

-    static {

-

-        // Register SASL mechanisms supported by Smack

-        registerSASLMechanism("EXTERNAL", SASLExternalMechanism.class);

-        registerSASLMechanism("GSSAPI", SASLGSSAPIMechanism.class);

-        registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class);

-        registerSASLMechanism("CRAM-MD5", SASLCramMD5Mechanism.class);

-        registerSASLMechanism("PLAIN", SASLPlainMechanism.class);

-        registerSASLMechanism("ANONYMOUS", SASLAnonymous.class);

-

-        supportSASLMechanism("GSSAPI",0);

-        supportSASLMechanism("DIGEST-MD5",1);

-        supportSASLMechanism("CRAM-MD5",2);

-        supportSASLMechanism("PLAIN",3);

-        supportSASLMechanism("ANONYMOUS",4);

-

-    }

-

-    /**

-     * Registers a new SASL mechanism

-     *

-     * @param name   common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.

-     * @param mClass a SASLMechanism subclass.

-     */

-    public static void registerSASLMechanism(String name, Class<? extends SASLMechanism> mClass) {

-        implementedMechanisms.put(name, mClass);

-    }

-

-    /**

-     * Unregisters an existing SASL mechanism. Once the mechanism has been unregistered it won't

-     * be possible to authenticate users using the removed SASL mechanism. It also removes the

-     * mechanism from the supported list.

-     *

-     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.

-     */

-    public static void unregisterSASLMechanism(String name) {

-        implementedMechanisms.remove(name);

-        mechanismsPreferences.remove(name);

-    }

-

-

-    /**

-     * Registers a new SASL mechanism in the specified preference position. The client will try

-     * to authenticate using the most prefered SASL mechanism that is also supported by the server.

-     * The SASL mechanism must be registered via {@link #registerSASLMechanism(String, Class)}

-     *

-     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.

-     */

-    public static void supportSASLMechanism(String name) {

-        mechanismsPreferences.add(0, name);

-    }

-

-    /**

-     * Registers a new SASL mechanism in the specified preference position. The client will try

-     * to authenticate using the most prefered SASL mechanism that is also supported by the server.

-     * Use the <tt>index</tt> parameter to set the level of preference of the new SASL mechanism.

-     * A value of 0 means that the mechanism is the most prefered one. The SASL mechanism must be

-     * registered via {@link #registerSASLMechanism(String, Class)}

-     *

-     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.

-     * @param index preference position amongst all the implemented SASL mechanism. Starts with 0.

-     */

-    public static void supportSASLMechanism(String name, int index) {

-        mechanismsPreferences.add(index, name);

-    }

-

-    /**

-     * Un-supports an existing SASL mechanism. Once the mechanism has been unregistered it won't

-     * be possible to authenticate users using the removed SASL mechanism. Note that the mechanism

-     * is still registered, but will just not be used.

-     *

-     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.

-     */

-    public static void unsupportSASLMechanism(String name) {

-        mechanismsPreferences.remove(name);

-    }

-

-    /**

-     * Returns the registerd SASLMechanism classes sorted by the level of preference.

-     *

-     * @return the registerd SASLMechanism classes sorted by the level of preference.

-     */

-    public static List<Class<? extends SASLMechanism>> getRegisterSASLMechanisms() {

-        List<Class<? extends SASLMechanism>> answer = new ArrayList<Class<? extends SASLMechanism>>();

-        for (String mechanismsPreference : mechanismsPreferences) {

-            answer.add(implementedMechanisms.get(mechanismsPreference));

-        }

-        return answer;

-    }

-

-    SASLAuthentication(Connection connection) {

-        super();

-        this.connection = connection;

-        this.init();

-    }

-

-    /**

-     * Returns true if the server offered ANONYMOUS SASL as a way to authenticate users.

-     *

-     * @return true if the server offered ANONYMOUS SASL as a way to authenticate users.

-     */

-    public boolean hasAnonymousAuthentication() {

-        return serverMechanisms.contains("ANONYMOUS");

-    }

-

-    /**

-     * Returns true if the server offered SASL authentication besides ANONYMOUS SASL.

-     *

-     * @return true if the server offered SASL authentication besides ANONYMOUS SASL.

-     */

-    public boolean hasNonAnonymousAuthentication() {

-        return !serverMechanisms.isEmpty() && (serverMechanisms.size() != 1 || !hasAnonymousAuthentication());

-    }

-

-    /**

-     * Performs SASL authentication of the specified user. If SASL authentication was successful

-     * then resource binding and session establishment will be performed. This method will return

-     * the full JID provided by the server while binding a resource to the connection.<p>

-     *

-     * The server may assign a full JID with a username or resource different than the requested

-     * by this method.

-     *

-     * @param username the username that is authenticating with the server.

-     * @param resource the desired resource.

-     * @param cbh the CallbackHandler used to get information from the user

-     * @return the full JID provided by the server while binding a resource to the connection.

-     * @throws XMPPException if an error occures while authenticating.

-     */

-    public String authenticate(String username, String resource, CallbackHandler cbh) 

-            throws XMPPException {

-        // Locate the SASLMechanism to use

-        String selectedMechanism = null;

-        for (String mechanism : mechanismsPreferences) {

-            if (implementedMechanisms.containsKey(mechanism) &&

-                    serverMechanisms.contains(mechanism)) {

-                selectedMechanism = mechanism;

-                break;

-            }

-        }

-        if (selectedMechanism != null) {

-            // A SASL mechanism was found. Authenticate using the selected mechanism and then

-            // proceed to bind a resource

-            try {

-                Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);

-                Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);

-                currentMechanism = constructor.newInstance(this);

-                // Trigger SASL authentication with the selected mechanism. We use

-                // connection.getHost() since GSAPI requires the FQDN of the server, which

-                // may not match the XMPP domain.

-                currentMechanism.authenticate(username, connection.getHost(), cbh);

-

-                // Wait until SASL negotiation finishes

-                synchronized (this) {

-                    if (!saslNegotiated && !saslFailed) {

-                        try {

-                            wait(30000);

-                        }

-                        catch (InterruptedException e) {

-                            // Ignore

-                        }

-                    }

-                }

-

-                if (saslFailed) {

-                    // SASL authentication failed and the server may have closed the connection

-                    // so throw an exception

-                    if (errorCondition != null) {

-                        throw new XMPPException("SASL authentication " +

-                                selectedMechanism + " failed: " + errorCondition);

-                    }

-                    else {

-                        throw new XMPPException("SASL authentication failed using mechanism " +

-                                selectedMechanism);

-                    }

-                }

-

-                if (saslNegotiated) {

-                    // Bind a resource for this connection and

-                    return bindResourceAndEstablishSession(resource);

-                } else {

-                    // SASL authentication failed

-                }

-            }

-            catch (XMPPException e) {

-                throw e;

-            }

-            catch (Exception e) {

-                e.printStackTrace();

-            }

-        }

-        else {

-            throw new XMPPException("SASL Authentication failed. No known authentication mechanisims.");

-        }

-        throw new XMPPException("SASL authentication failed");

-    }

-

-    /**

-     * Performs SASL authentication of the specified user. If SASL authentication was successful

-     * then resource binding and session establishment will be performed. This method will return

-     * the full JID provided by the server while binding a resource to the connection.<p>

-     *

-     * The server may assign a full JID with a username or resource different than the requested

-     * by this method.

-     *

-     * @param username the username that is authenticating with the server.

-     * @param password the password to send to the server.

-     * @param resource the desired resource.

-     * @return the full JID provided by the server while binding a resource to the connection.

-     * @throws XMPPException if an error occures while authenticating.

-     */

-    public String authenticate(String username, String password, String resource)

-            throws XMPPException {

-        // Locate the SASLMechanism to use

-        String selectedMechanism = null;

-        for (String mechanism : mechanismsPreferences) {

-            if (implementedMechanisms.containsKey(mechanism) &&

-                    serverMechanisms.contains(mechanism)) {

-                selectedMechanism = mechanism;

-                break;

-            }

-        }

-        if (selectedMechanism != null) {

-            // A SASL mechanism was found. Authenticate using the selected mechanism and then

-            // proceed to bind a resource

-            try {

-                Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);

-                Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);

-                currentMechanism = constructor.newInstance(this);

-                // Trigger SASL authentication with the selected mechanism. We use

-                // connection.getHost() since GSAPI requires the FQDN of the server, which

-                // may not match the XMPP domain.    

-                

-                //The serviceName is basically the value that XMPP server sends to the client as being the location

-                //of the XMPP service we are trying to connect to. This should have the format: host [ "/" serv-name ]

-                //as per RFC-2831 guidelines

-                String serviceName = connection.getServiceName();               

-                currentMechanism.authenticate(username, connection.getHost(), serviceName, password);

-

-                // Wait until SASL negotiation finishes

-                synchronized (this) {

-                    if (!saslNegotiated && !saslFailed) {

-                        try {

-                            wait(30000);

-                        }

-                        catch (InterruptedException e) {

-                            // Ignore

-                        }

-                    }

-                }

-

-                if (saslFailed) {

-                    // SASL authentication failed and the server may have closed the connection

-                    // so throw an exception

-                    if (errorCondition != null) {

-                        throw new XMPPException("SASL authentication " +

-                                selectedMechanism + " failed: " + errorCondition);

-                    }

-                    else {

-                        throw new XMPPException("SASL authentication failed using mechanism " +

-                                selectedMechanism);

-                    }

-                }

-

-                if (saslNegotiated) {

-                    // Bind a resource for this connection and

-                    return bindResourceAndEstablishSession(resource);

-                }

-                else {

-                    // SASL authentication failed so try a Non-SASL authentication

-                    return new NonSASLAuthentication(connection)

-                            .authenticate(username, password, resource);

-                }

-            }

-            catch (XMPPException e) {

-                throw e;

-            }

-            catch (Exception e) {

-                e.printStackTrace();

-                // SASL authentication failed so try a Non-SASL authentication

-                return new NonSASLAuthentication(connection)

-                        .authenticate(username, password, resource);

-            }

-        }

-        else {

-            // No SASL method was found so try a Non-SASL authentication

-            return new NonSASLAuthentication(connection).authenticate(username, password, resource);

-        }

-    }

-

-    /**

-     * Performs ANONYMOUS SASL authentication. If SASL authentication was successful

-     * then resource binding and session establishment will be performed. This method will return

-     * the full JID provided by the server while binding a resource to the connection.<p>

-     *

-     * The server will assign a full JID with a randomly generated resource and possibly with

-     * no username.

-     *

-     * @return the full JID provided by the server while binding a resource to the connection.

-     * @throws XMPPException if an error occures while authenticating.

-     */

-    public String authenticateAnonymously() throws XMPPException {

-        try {

-            currentMechanism = new SASLAnonymous(this);

-            currentMechanism.authenticate(null,null,null,"");

-

-            // Wait until SASL negotiation finishes

-            synchronized (this) {

-                if (!saslNegotiated && !saslFailed) {

-                    try {

-                        wait(5000);

-                    }

-                    catch (InterruptedException e) {

-                        // Ignore

-                    }

-                }

-            }

-

-            if (saslFailed) {

-                // SASL authentication failed and the server may have closed the connection

-                // so throw an exception

-                if (errorCondition != null) {

-                    throw new XMPPException("SASL authentication failed: " + errorCondition);

-                }

-                else {

-                    throw new XMPPException("SASL authentication failed");

-                }

-            }

-

-            if (saslNegotiated) {

-                // Bind a resource for this connection and

-                return bindResourceAndEstablishSession(null);

-            }

-            else {

-                return new NonSASLAuthentication(connection).authenticateAnonymously();

-            }

-        } catch (IOException e) {

-            return new NonSASLAuthentication(connection).authenticateAnonymously();

-        }

-    }

-

-    private String bindResourceAndEstablishSession(String resource) throws XMPPException {

-        // Wait until server sends response containing the <bind> element

-        synchronized (this) {

-            if (!resourceBinded) {

-                try {

-                    wait(30000);

-                }

-                catch (InterruptedException e) {

-                    // Ignore

-                }

-            }

-        }

-

-        if (!resourceBinded) {

-            // Server never offered resource binding

-            throw new XMPPException("Resource binding not offered by server");

-        }

-

-        Bind bindResource = new Bind();

-        bindResource.setResource(resource);

-

-        PacketCollector collector = connection

-                .createPacketCollector(new PacketIDFilter(bindResource.getPacketID()));

-        // Send the packet

-        connection.sendPacket(bindResource);

-        // Wait up to a certain number of seconds for a response from the server.

-        Bind response = (Bind) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from the server.");

-        }

-        // If the server replied with an error, throw an exception.

-        else if (response.getType() == IQ.Type.ERROR) {

-            throw new XMPPException(response.getError());

-        }

-        String userJID = response.getJid();

-

-        if (sessionSupported) {

-            Session session = new Session();

-            collector = connection.createPacketCollector(new PacketIDFilter(session.getPacketID()));

-            // Send the packet

-            connection.sendPacket(session);

-            // Wait up to a certain number of seconds for a response from the server.

-            IQ ack = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-            collector.cancel();

-            if (ack == null) {

-                throw new XMPPException("No response from the server.");

-            }

-            // If the server replied with an error, throw an exception.

-            else if (ack.getType() == IQ.Type.ERROR) {

-                throw new XMPPException(ack.getError());

-            }

-        }

-        return userJID;

-    }

-

-    /**

-     * Sets the available SASL mechanism reported by the server. The server will report the

-     * available SASL mechanism once the TLS negotiation was successful. This information is

-     * stored and will be used when doing the authentication for logging in the user.

-     *

-     * @param mechanisms collection of strings with the available SASL mechanism reported

-     *                   by the server.

-     */

-    void setAvailableSASLMethods(Collection<String> mechanisms) {

-        this.serverMechanisms = mechanisms;

-    }

-

-    /**

-     * Returns true if the user was able to authenticate with the server usins SASL.

-     *

-     * @return true if the user was able to authenticate with the server usins SASL.

-     */

-    public boolean isAuthenticated() {

-        return saslNegotiated;

-    }

-

-    /**

-     * The server is challenging the SASL authentication we just sent. Forward the challenge

-     * to the current SASLMechanism we are using. The SASLMechanism will send a response to

-     * the server. The length of the challenge-response sequence varies according to the

-     * SASLMechanism in use.

-     *

-     * @param challenge a base64 encoded string representing the challenge.

-     * @throws IOException If a network error occures while authenticating.

-     */

-    void challengeReceived(String challenge) throws IOException {

-        currentMechanism.challengeReceived(challenge);

-    }

-

-    /**

-     * Notification message saying that SASL authentication was successful. The next step

-     * would be to bind the resource.

-     */

-    void authenticated() {

-        synchronized (this) {

-            saslNegotiated = true;

-            // Wake up the thread that is waiting in the #authenticate method

-            notify();

-        }

-    }

-

-    /**

-     * Notification message saying that SASL authentication has failed. The server may have

-     * closed the connection depending on the number of possible retries.

-     * 

-     * @deprecated replaced by {@see #authenticationFailed(String)}.

-     */

-    void authenticationFailed() {

-        authenticationFailed(null);

-    }

-

-    /**

-     * Notification message saying that SASL authentication has failed. The server may have

-     * closed the connection depending on the number of possible retries.

-     * 

-     * @param condition the error condition provided by the server.

-     */

-    void authenticationFailed(String condition) {

-        synchronized (this) {

-            saslFailed = true;

-            errorCondition = condition;

-            // Wake up the thread that is waiting in the #authenticate method

-            notify();

-        }

-    }

-

-    /**

-     * Notification message saying that the server requires the client to bind a

-     * resource to the stream.

-     */

-    void bindingRequired() {

-        synchronized (this) {

-            resourceBinded = true;

-            // Wake up the thread that is waiting in the #authenticate method

-            notify();

-        }

-    }

-

-    public void send(Packet stanza) {

-        connection.sendPacket(stanza);

-    }

-

-    /**

-     * Notification message saying that the server supports sessions. When a server supports

-     * sessions the client needs to send a Session packet after successfully binding a resource

-     * for the session.

-     */

-    void sessionsSupported() {

-        sessionSupported = true;

-    }

-    

-    /**

-     * Initializes the internal state in order to be able to be reused. The authentication

-     * is used by the connection at the first login and then reused after the connection

-     * is disconnected and then reconnected.

-     */

-    protected void init() {

-        saslNegotiated = false;

-        saslFailed = false;

-        resourceBinded = false;

-        sessionSupported = false;

-    }

+/**
+ * $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;
+
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.Bind;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.Session;
+import org.jivesoftware.smack.sasl.*;
+
+import javax.security.auth.callback.CallbackHandler;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.*;
+
+/**
+ * <p>This class is responsible authenticating the user using SASL, binding the resource
+ * to the connection and establishing a session with the server.</p>
+ *
+ * <p>Once TLS has been negotiated (i.e. the connection has been secured) it is possible to
+ * register with the server, authenticate using Non-SASL or authenticate using SASL. If the
+ * server supports SASL then Smack will first try to authenticate using SASL. But if that
+ * fails then Non-SASL will be tried.</p>
+ *
+ * <p>The server may support many SASL mechanisms to use for authenticating. Out of the box
+ * Smack provides several SASL mechanisms, but it is possible to register new SASL Mechanisms. Use
+ * {@link #registerSASLMechanism(String, Class)} to register a new mechanisms. A registered
+ * mechanism wont be used until {@link #supportSASLMechanism(String, int)} is called. By default,
+ * the list of supported SASL mechanisms is determined from the {@link SmackConfiguration}. </p>
+ *
+ * <p>Once the user has been authenticated with SASL, it is necessary to bind a resource for
+ * the connection. If no resource is passed in {@link #authenticate(String, String, String)}
+ * then the server will assign a resource for the connection. In case a resource is passed
+ * then the server will receive the desired resource but may assign a modified resource for
+ * the connection.</p>
+ *
+ * <p>Once a resource has been binded and if the server supports sessions then Smack will establish
+ * a session so that instant messaging and presence functionalities may be used.</p>
+ *
+ * @see org.jivesoftware.smack.sasl.SASLMechanism
+ *
+ * @author Gaston Dombiak
+ * @author Jay Kline
+ */
+public class SASLAuthentication implements UserAuthentication {
+
+    private static Map<String, Class<? extends SASLMechanism>> implementedMechanisms = new HashMap<String, Class<? extends SASLMechanism>>();
+    private static List<String> mechanismsPreferences = new ArrayList<String>();
+
+    private Connection connection;
+    private Collection<String> serverMechanisms = new ArrayList<String>();
+    private SASLMechanism currentMechanism = null;
+    /**
+     * Boolean indicating if SASL negotiation has finished and was successful.
+     */
+    private boolean saslNegotiated;
+    /**
+     * Boolean indication if SASL authentication has failed. When failed the server may end
+     * the connection.
+     */
+    private boolean saslFailed;
+    private boolean resourceBinded;
+    private boolean sessionSupported;
+    /**
+     * The SASL related error condition if there was one provided by the server.
+     */
+    private String errorCondition;
+
+    static {
+
+        // Register SASL mechanisms supported by Smack
+        registerSASLMechanism("EXTERNAL", SASLExternalMechanism.class);
+        registerSASLMechanism("GSSAPI", SASLGSSAPIMechanism.class);
+        registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class);
+        registerSASLMechanism("CRAM-MD5", SASLCramMD5Mechanism.class);
+        registerSASLMechanism("PLAIN", SASLPlainMechanism.class);
+        registerSASLMechanism("ANONYMOUS", SASLAnonymous.class);
+
+        supportSASLMechanism("GSSAPI",0);
+        supportSASLMechanism("DIGEST-MD5",1);
+        supportSASLMechanism("CRAM-MD5",2);
+        supportSASLMechanism("PLAIN",3);
+        supportSASLMechanism("ANONYMOUS",4);
+
+    }
+
+    /**
+     * Registers a new SASL mechanism
+     *
+     * @param name   common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.
+     * @param mClass a SASLMechanism subclass.
+     */
+    public static void registerSASLMechanism(String name, Class<? extends SASLMechanism> mClass) {
+        implementedMechanisms.put(name, mClass);
+    }
+
+    /**
+     * Unregisters an existing SASL mechanism. Once the mechanism has been unregistered it won't
+     * be possible to authenticate users using the removed SASL mechanism. It also removes the
+     * mechanism from the supported list.
+     *
+     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.
+     */
+    public static void unregisterSASLMechanism(String name) {
+        implementedMechanisms.remove(name);
+        mechanismsPreferences.remove(name);
+    }
+
+
+    /**
+     * Registers a new SASL mechanism in the specified preference position. The client will try
+     * to authenticate using the most prefered SASL mechanism that is also supported by the server.
+     * The SASL mechanism must be registered via {@link #registerSASLMechanism(String, Class)}
+     *
+     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.
+     */
+    public static void supportSASLMechanism(String name) {
+        mechanismsPreferences.add(0, name);
+    }
+
+    /**
+     * Registers a new SASL mechanism in the specified preference position. The client will try
+     * to authenticate using the most prefered SASL mechanism that is also supported by the server.
+     * Use the <tt>index</tt> parameter to set the level of preference of the new SASL mechanism.
+     * A value of 0 means that the mechanism is the most prefered one. The SASL mechanism must be
+     * registered via {@link #registerSASLMechanism(String, Class)}
+     *
+     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.
+     * @param index preference position amongst all the implemented SASL mechanism. Starts with 0.
+     */
+    public static void supportSASLMechanism(String name, int index) {
+        mechanismsPreferences.add(index, name);
+    }
+
+    /**
+     * Un-supports an existing SASL mechanism. Once the mechanism has been unregistered it won't
+     * be possible to authenticate users using the removed SASL mechanism. Note that the mechanism
+     * is still registered, but will just not be used.
+     *
+     * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4.
+     */
+    public static void unsupportSASLMechanism(String name) {
+        mechanismsPreferences.remove(name);
+    }
+
+    /**
+     * Returns the registerd SASLMechanism classes sorted by the level of preference.
+     *
+     * @return the registerd SASLMechanism classes sorted by the level of preference.
+     */
+    public static List<Class<? extends SASLMechanism>> getRegisterSASLMechanisms() {
+        List<Class<? extends SASLMechanism>> answer = new ArrayList<Class<? extends SASLMechanism>>();
+        for (String mechanismsPreference : mechanismsPreferences) {
+            answer.add(implementedMechanisms.get(mechanismsPreference));
+        }
+        return answer;
+    }
+
+    SASLAuthentication(Connection connection) {
+        super();
+        this.connection = connection;
+        this.init();
+    }
+
+    /**
+     * Returns true if the server offered ANONYMOUS SASL as a way to authenticate users.
+     *
+     * @return true if the server offered ANONYMOUS SASL as a way to authenticate users.
+     */
+    public boolean hasAnonymousAuthentication() {
+        return serverMechanisms.contains("ANONYMOUS");
+    }
+
+    /**
+     * Returns true if the server offered SASL authentication besides ANONYMOUS SASL.
+     *
+     * @return true if the server offered SASL authentication besides ANONYMOUS SASL.
+     */
+    public boolean hasNonAnonymousAuthentication() {
+        return !serverMechanisms.isEmpty() && (serverMechanisms.size() != 1 || !hasAnonymousAuthentication());
+    }
+
+    /**
+     * Performs SASL authentication of the specified user. If SASL authentication was successful
+     * then resource binding and session establishment will be performed. This method will return
+     * the full JID provided by the server while binding a resource to the connection.<p>
+     *
+     * The server may assign a full JID with a username or resource different than the requested
+     * by this method.
+     *
+     * @param username the username that is authenticating with the server.
+     * @param resource the desired resource.
+     * @param cbh the CallbackHandler used to get information from the user
+     * @return the full JID provided by the server while binding a resource to the connection.
+     * @throws XMPPException if an error occures while authenticating.
+     */
+    public String authenticate(String username, String resource, CallbackHandler cbh) 
+            throws XMPPException {
+        // Locate the SASLMechanism to use
+        String selectedMechanism = null;
+        for (String mechanism : mechanismsPreferences) {
+            if (implementedMechanisms.containsKey(mechanism) &&
+                    serverMechanisms.contains(mechanism)) {
+                selectedMechanism = mechanism;
+                break;
+            }
+        }
+        if (selectedMechanism != null) {
+            // A SASL mechanism was found. Authenticate using the selected mechanism and then
+            // proceed to bind a resource
+            try {
+                Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
+                Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);
+                currentMechanism = constructor.newInstance(this);
+                // Trigger SASL authentication with the selected mechanism. We use
+                // connection.getHost() since GSAPI requires the FQDN of the server, which
+                // may not match the XMPP domain.
+                currentMechanism.authenticate(username, connection.getHost(), cbh);
+
+                // Wait until SASL negotiation finishes
+                synchronized (this) {
+                    if (!saslNegotiated && !saslFailed) {
+                        try {
+                            wait(30000);
+                        }
+                        catch (InterruptedException e) {
+                            // Ignore
+                        }
+                    }
+                }
+
+                if (saslFailed) {
+                    // SASL authentication failed and the server may have closed the connection
+                    // so throw an exception
+                    if (errorCondition != null) {
+                        throw new XMPPException("SASL authentication " +
+                                selectedMechanism + " failed: " + errorCondition);
+                    }
+                    else {
+                        throw new XMPPException("SASL authentication failed using mechanism " +
+                                selectedMechanism);
+                    }
+                }
+
+                if (saslNegotiated) {
+                    // Bind a resource for this connection and
+                    return bindResourceAndEstablishSession(resource);
+                } else {
+                    // SASL authentication failed
+                }
+            }
+            catch (XMPPException e) {
+                throw e;
+            }
+            catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        else {
+            throw new XMPPException("SASL Authentication failed. No known authentication mechanisims.");
+        }
+        throw new XMPPException("SASL authentication failed");
+    }
+
+    /**
+     * Performs SASL authentication of the specified user. If SASL authentication was successful
+     * then resource binding and session establishment will be performed. This method will return
+     * the full JID provided by the server while binding a resource to the connection.<p>
+     *
+     * The server may assign a full JID with a username or resource different than the requested
+     * by this method.
+     *
+     * @param username the username that is authenticating with the server.
+     * @param password the password to send to the server.
+     * @param resource the desired resource.
+     * @return the full JID provided by the server while binding a resource to the connection.
+     * @throws XMPPException if an error occures while authenticating.
+     */
+    public String authenticate(String username, String password, String resource)
+            throws XMPPException {
+        // Locate the SASLMechanism to use
+        String selectedMechanism = null;
+        for (String mechanism : mechanismsPreferences) {
+            if (implementedMechanisms.containsKey(mechanism) &&
+                    serverMechanisms.contains(mechanism)) {
+                selectedMechanism = mechanism;
+                break;
+            }
+        }
+        if (selectedMechanism != null) {
+            // A SASL mechanism was found. Authenticate using the selected mechanism and then
+            // proceed to bind a resource
+            try {
+                Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
+                Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);
+                currentMechanism = constructor.newInstance(this);
+                // Trigger SASL authentication with the selected mechanism. We use
+                // connection.getHost() since GSAPI requires the FQDN of the server, which
+                // may not match the XMPP domain.    
+                
+                //The serviceName is basically the value that XMPP server sends to the client as being the location
+                //of the XMPP service we are trying to connect to. This should have the format: host [ "/" serv-name ]
+                //as per RFC-2831 guidelines
+                String serviceName = connection.getServiceName();               
+                currentMechanism.authenticate(username, connection.getHost(), serviceName, password);
+
+                // Wait until SASL negotiation finishes
+                synchronized (this) {
+                    if (!saslNegotiated && !saslFailed) {
+                        try {
+                            wait(30000);
+                        }
+                        catch (InterruptedException e) {
+                            // Ignore
+                        }
+                    }
+                }
+
+                if (saslFailed) {
+                    // SASL authentication failed and the server may have closed the connection
+                    // so throw an exception
+                    if (errorCondition != null) {
+                        throw new XMPPException("SASL authentication " +
+                                selectedMechanism + " failed: " + errorCondition);
+                    }
+                    else {
+                        throw new XMPPException("SASL authentication failed using mechanism " +
+                                selectedMechanism);
+                    }
+                }
+
+                if (saslNegotiated) {
+                    // Bind a resource for this connection and
+                    return bindResourceAndEstablishSession(resource);
+                }
+                else {
+                    // SASL authentication failed so try a Non-SASL authentication
+                    return new NonSASLAuthentication(connection)
+                            .authenticate(username, password, resource);
+                }
+            }
+            catch (XMPPException e) {
+                throw e;
+            }
+            catch (Exception e) {
+                e.printStackTrace();
+                // SASL authentication failed so try a Non-SASL authentication
+                return new NonSASLAuthentication(connection)
+                        .authenticate(username, password, resource);
+            }
+        }
+        else {
+            // No SASL method was found so try a Non-SASL authentication
+            return new NonSASLAuthentication(connection).authenticate(username, password, resource);
+        }
+    }
+
+    /**
+     * Performs ANONYMOUS SASL authentication. If SASL authentication was successful
+     * then resource binding and session establishment will be performed. This method will return
+     * the full JID provided by the server while binding a resource to the connection.<p>
+     *
+     * The server will assign a full JID with a randomly generated resource and possibly with
+     * no username.
+     *
+     * @return the full JID provided by the server while binding a resource to the connection.
+     * @throws XMPPException if an error occures while authenticating.
+     */
+    public String authenticateAnonymously() throws XMPPException {
+        try {
+            currentMechanism = new SASLAnonymous(this);
+            currentMechanism.authenticate(null,null,null,"");
+
+            // Wait until SASL negotiation finishes
+            synchronized (this) {
+                if (!saslNegotiated && !saslFailed) {
+                    try {
+                        wait(5000);
+                    }
+                    catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+            }
+
+            if (saslFailed) {
+                // SASL authentication failed and the server may have closed the connection
+                // so throw an exception
+                if (errorCondition != null) {
+                    throw new XMPPException("SASL authentication failed: " + errorCondition);
+                }
+                else {
+                    throw new XMPPException("SASL authentication failed");
+                }
+            }
+
+            if (saslNegotiated) {
+                // Bind a resource for this connection and
+                return bindResourceAndEstablishSession(null);
+            }
+            else {
+                return new NonSASLAuthentication(connection).authenticateAnonymously();
+            }
+        } catch (IOException e) {
+            return new NonSASLAuthentication(connection).authenticateAnonymously();
+        }
+    }
+
+    private String bindResourceAndEstablishSession(String resource) throws XMPPException {
+        // Wait until server sends response containing the <bind> element
+        synchronized (this) {
+            if (!resourceBinded) {
+                try {
+                    wait(30000);
+                }
+                catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        if (!resourceBinded) {
+            // Server never offered resource binding
+            throw new XMPPException("Resource binding not offered by server");
+        }
+
+        Bind bindResource = new Bind();
+        bindResource.setResource(resource);
+
+        PacketCollector collector = connection
+                .createPacketCollector(new PacketIDFilter(bindResource.getPacketID()));
+        // Send the packet
+        connection.sendPacket(bindResource);
+        // Wait up to a certain number of seconds for a response from the server.
+        Bind response = (Bind) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from the server.");
+        }
+        // If the server replied with an error, throw an exception.
+        else if (response.getType() == IQ.Type.ERROR) {
+            throw new XMPPException(response.getError());
+        }
+        String userJID = response.getJid();
+
+        if (sessionSupported) {
+            Session session = new Session();
+            collector = connection.createPacketCollector(new PacketIDFilter(session.getPacketID()));
+            // Send the packet
+            connection.sendPacket(session);
+            // Wait up to a certain number of seconds for a response from the server.
+            IQ ack = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+            collector.cancel();
+            if (ack == null) {
+                throw new XMPPException("No response from the server.");
+            }
+            // If the server replied with an error, throw an exception.
+            else if (ack.getType() == IQ.Type.ERROR) {
+                throw new XMPPException(ack.getError());
+            }
+        }
+        return userJID;
+    }
+
+    /**
+     * Sets the available SASL mechanism reported by the server. The server will report the
+     * available SASL mechanism once the TLS negotiation was successful. This information is
+     * stored and will be used when doing the authentication for logging in the user.
+     *
+     * @param mechanisms collection of strings with the available SASL mechanism reported
+     *                   by the server.
+     */
+    void setAvailableSASLMethods(Collection<String> mechanisms) {
+        this.serverMechanisms = mechanisms;
+    }
+
+    /**
+     * Returns true if the user was able to authenticate with the server usins SASL.
+     *
+     * @return true if the user was able to authenticate with the server usins SASL.
+     */
+    public boolean isAuthenticated() {
+        return saslNegotiated;
+    }
+
+    /**
+     * The server is challenging the SASL authentication we just sent. Forward the challenge
+     * to the current SASLMechanism we are using. The SASLMechanism will send a response to
+     * the server. The length of the challenge-response sequence varies according to the
+     * SASLMechanism in use.
+     *
+     * @param challenge a base64 encoded string representing the challenge.
+     * @throws IOException If a network error occures while authenticating.
+     */
+    void challengeReceived(String challenge) throws IOException {
+        currentMechanism.challengeReceived(challenge);
+    }
+
+    /**
+     * Notification message saying that SASL authentication was successful. The next step
+     * would be to bind the resource.
+     */
+    void authenticated() {
+        synchronized (this) {
+            saslNegotiated = true;
+            // Wake up the thread that is waiting in the #authenticate method
+            notify();
+        }
+    }
+
+    /**
+     * Notification message saying that SASL authentication has failed. The server may have
+     * closed the connection depending on the number of possible retries.
+     * 
+     * @deprecated replaced by {@see #authenticationFailed(String)}.
+     */
+    void authenticationFailed() {
+        authenticationFailed(null);
+    }
+
+    /**
+     * Notification message saying that SASL authentication has failed. The server may have
+     * closed the connection depending on the number of possible retries.
+     * 
+     * @param condition the error condition provided by the server.
+     */
+    void authenticationFailed(String condition) {
+        synchronized (this) {
+            saslFailed = true;
+            errorCondition = condition;
+            // Wake up the thread that is waiting in the #authenticate method
+            notify();
+        }
+    }
+
+    /**
+     * Notification message saying that the server requires the client to bind a
+     * resource to the stream.
+     */
+    void bindingRequired() {
+        synchronized (this) {
+            resourceBinded = true;
+            // Wake up the thread that is waiting in the #authenticate method
+            notify();
+        }
+    }
+
+    public void send(Packet stanza) {
+        connection.sendPacket(stanza);
+    }
+
+    /**
+     * Notification message saying that the server supports sessions. When a server supports
+     * sessions the client needs to send a Session packet after successfully binding a resource
+     * for the session.
+     */
+    void sessionsSupported() {
+        sessionSupported = true;
+    }
+    
+    /**
+     * Initializes the internal state in order to be able to be reused. The authentication
+     * is used by the connection at the first login and then reused after the connection
+     * is disconnected and then reconnected.
+     */
+    protected void init() {
+        saslNegotiated = false;
+        saslFailed = false;
+        resourceBinded = false;
+        sessionSupported = false;
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SmackError.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SmackError.java
index af22e7c..8527876 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SmackError.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/SmackError.java
@@ -1,24 +1,24 @@
-package org.jivesoftware.smack;

-

-public enum SmackError {

-    NO_RESPONSE_FROM_SERVER("No response from server.");

-    

-    private String message;

-    

-    private SmackError(String errMessage) {

-        message = errMessage;

-    }

-    

-    public String getErrorMessage() {

-        return message;

-    }

-    

-    public static SmackError getErrorCode(String message) {

-        for (SmackError code : values()) {

-            if (code.message.equals(message)) {

-                return code;

-            }

-        }

-        return null;

-    }

-}

+package org.jivesoftware.smack;
+
+public enum SmackError {
+    NO_RESPONSE_FROM_SERVER("No response from server.");
+    
+    private String message;
+    
+    private SmackError(String errMessage) {
+        message = errMessage;
+    }
+    
+    public String getErrorMessage() {
+        return message;
+    }
+    
+    public static SmackError getErrorCode(String message) {
+        for (SmackError code : values()) {
+            if (code.message.equals(message)) {
+                return code;
+            }
+        }
+        return null;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/UserAuthentication.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/UserAuthentication.java
index 8f0dd50..728428b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/UserAuthentication.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/UserAuthentication.java
@@ -1,79 +1,79 @@
-/**

- * $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;

-

-import javax.security.auth.callback.CallbackHandler;

-

-/**

- * There are two ways to authenticate a user with a server. Using SASL or Non-SASL

- * authentication. This interface makes {@link SASLAuthentication} and

- * {@link NonSASLAuthentication} polyphormic.

- *

- * @author Gaston Dombiak

- * @author Jay Kline

- */

-interface UserAuthentication {

-

-    /**

-     * Authenticates the user with the server.  This method will return the full JID provided by

-     * the server.  The server may assign a full JID with a username and resource different than

-     * requested by this method.

-     *

-     * Note that using callbacks is the prefered method of authenticating users since it allows

-     * more flexability in the mechanisms used.

-     *

-     * @param username the requested username (authorization ID) for authenticating to the server

-     * @param resource the requested resource.

-     * @param cbh the CallbackHandler used to obtain authentication ID, password, or other

-     * information

-     * @return the full JID provided by the server while binding a resource for the connection.

-     * @throws XMPPException if an error occurs while authenticating.

-     */

-    String authenticate(String username, String resource, CallbackHandler cbh) throws

-            XMPPException;

-

-    /**

-     * Authenticates the user with the server. This method will return the full JID provided by

-     * the server. The server may assign a full JID with a username and resource different than

-     * the requested by this method.

-     *

-     * It is recommended that @{link #authenticate(String, String, CallbackHandler)} be used instead

-     * since it provides greater flexability in authenticaiton and authorization.

-     *

-     * @param username the username that is authenticating with the server.

-     * @param password the password to send to the server.

-     * @param resource the desired resource.

-     * @return the full JID provided by the server while binding a resource for the connection.

-     * @throws XMPPException if an error occures while authenticating.

-     */

-    String authenticate(String username, String password, String resource) throws

-            XMPPException;

-

-    /**

-     * Performs an anonymous authentication with the server. The server will created a new full JID

-     * for this connection. An exception will be thrown if the server does not support anonymous

-     * authentication.

-     *

-     * @return the full JID provided by the server while binding a resource for the connection.

-     * @throws XMPPException if an error occures while authenticating.

-     */

-    String authenticateAnonymously() throws XMPPException;

-}

+/**
+ * $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;
+
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * There are two ways to authenticate a user with a server. Using SASL or Non-SASL
+ * authentication. This interface makes {@link SASLAuthentication} and
+ * {@link NonSASLAuthentication} polyphormic.
+ *
+ * @author Gaston Dombiak
+ * @author Jay Kline
+ */
+interface UserAuthentication {
+
+    /**
+     * Authenticates the user with the server.  This method will return the full JID provided by
+     * the server.  The server may assign a full JID with a username and resource different than
+     * requested by this method.
+     *
+     * Note that using callbacks is the prefered method of authenticating users since it allows
+     * more flexability in the mechanisms used.
+     *
+     * @param username the requested username (authorization ID) for authenticating to the server
+     * @param resource the requested resource.
+     * @param cbh the CallbackHandler used to obtain authentication ID, password, or other
+     * information
+     * @return the full JID provided by the server while binding a resource for the connection.
+     * @throws XMPPException if an error occurs while authenticating.
+     */
+    String authenticate(String username, String resource, CallbackHandler cbh) throws
+            XMPPException;
+
+    /**
+     * Authenticates the user with the server. This method will return the full JID provided by
+     * the server. The server may assign a full JID with a username and resource different than
+     * the requested by this method.
+     *
+     * It is recommended that @{link #authenticate(String, String, CallbackHandler)} be used instead
+     * since it provides greater flexability in authenticaiton and authorization.
+     *
+     * @param username the username that is authenticating with the server.
+     * @param password the password to send to the server.
+     * @param resource the desired resource.
+     * @return the full JID provided by the server while binding a resource for the connection.
+     * @throws XMPPException if an error occures while authenticating.
+     */
+    String authenticate(String username, String password, String resource) throws
+            XMPPException;
+
+    /**
+     * Performs an anonymous authentication with the server. The server will created a new full JID
+     * for this connection. An exception will be thrown if the server does not support anonymous
+     * authentication.
+     *
+     * @return the full JID provided by the server while binding a resource for the connection.
+     * @throws XMPPException if an error occures while authenticating.
+     */
+    String authenticateAnonymously() throws XMPPException;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/filter/IQTypeFilter.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/filter/IQTypeFilter.java
index dbab1c3..efe6003 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/filter/IQTypeFilter.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/filter/IQTypeFilter.java
@@ -1,48 +1,48 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.filter;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-

-/**

- * A filter for IQ packet types. Returns true only if the packet is an IQ packet

- * and it matches the type provided in the constructor.

- * 

- * @author Alexander Wenckus

- * 

- */

-public class IQTypeFilter implements PacketFilter {

-

-	private IQ.Type type;

-

-	public IQTypeFilter(IQ.Type type) {

-		this.type = type;

-	}

-

-	/*

-	 * (non-Javadoc)

-	 * 

-	 * @see org.jivesoftware.smack.filter.PacketFilter#accept(org.jivesoftware.smack.packet.Packet)

-	 */

-	public boolean accept(Packet packet) {

-		return (packet instanceof IQ && ((IQ) packet).getType().equals(type));

-	}

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.filter;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+
+/**
+ * A filter for IQ packet types. Returns true only if the packet is an IQ packet
+ * and it matches the type provided in the constructor.
+ * 
+ * @author Alexander Wenckus
+ * 
+ */
+public class IQTypeFilter implements PacketFilter {
+
+	private IQ.Type type;
+
+	public IQTypeFilter(IQ.Type type) {
+		this.type = type;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jivesoftware.smack.filter.PacketFilter#accept(org.jivesoftware.smack.packet.Packet)
+	 */
+	public boolean accept(Packet packet) {
+		return (packet instanceof IQ && ((IQ) packet).getType().equals(type));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Bind.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Bind.java
index 07cd193..fa15014 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Bind.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Bind.java
@@ -1,71 +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();

-    }

-}

+/**
+ * $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/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Privacy.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Privacy.java
index a62d578..3af6c4b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Privacy.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Privacy.java
@@ -1,323 +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();

-    }

-    

+/**
+ * $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/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/PrivacyItem.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/PrivacyItem.java
index 2e144ee..e108f68 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/PrivacyItem.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/PrivacyItem.java
@@ -15,448 +15,448 @@
  * 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

-    }

-}

+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/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Session.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Session.java
index fd403ae..427c01a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Session.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/Session.java
@@ -1,45 +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\"/>";

-    }

-}

+/**
+ * $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/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/StreamError.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/StreamError.java
index 8bb4c75..72679a8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/StreamError.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/packet/StreamError.java
@@ -1,106 +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();

-    }

-}

+/**
+ * $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/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java
index e7b4b93..84b5079 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java
@@ -1,109 +1,109 @@
-/**

- * 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.provider;

-

-import java.util.ArrayList;

-import java.util.HashMap;

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.jivesoftware.smackx.pubsub.provider.ItemProvider;

-import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * 

- * This class simplifies parsing of embedded elements by using the 

- * <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template Method Pattern</a>.  

- * After extracting the current element attributes and content of any child elements, the template method 

- * ({@link #createReturnExtension(String, String, Map, List)} is called.  Subclasses

- * then override this method to create the specific return type.

- * 

- * <p>To use this class, you simply register your subclasses as extension providers in the 

- * <b>smack.properties</b> file.  Then they will be automatically picked up and used to parse

- * any child elements.  

- * 

- * <pre>

- * For example, given the following message

- * 

- * &lt;message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo&gt;

- *    &lt;event xmlns='http://jabber.org/protocol/pubsub#event&gt;

- *       &lt;items node='princely_musings'&gt;

- *          &lt;item id='asdjkwei3i34234n356'&gt;

- *             &lt;entry xmlns='http://www.w3.org/2005/Atom'&gt;

- *                &lt;title&gt;Soliloquy&lt;/title&gt;

- *                &lt;link rel='alternative' type='text/html'/&gt;

- *                &lt;id>tag:denmark.lit,2003:entry-32397&lt;/id&gt;

- *             &lt;/entry&gt;

- *          &lt;/item&gt;

- *       &lt;/items&gt;

- *    &lt;/event&gt;

- * &lt;/message&gt;

- * 

- * I would have a classes

- * {@link ItemsProvider} extends {@link EmbeddedExtensionProvider}

- * {@link ItemProvider} extends {@link EmbeddedExtensionProvider}

- * and

- * AtomProvider extends {@link PacketExtensionProvider}

- * 

- * These classes are then registered in the meta-inf/smack.providers file

- * as follows.

- * 

- *   &lt;extensionProvider&gt;

- *      &lt;elementName&gt;items&lt;/elementName&gt;

- *      &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;

- *      &lt;className&gt;org.jivesoftware.smackx.provider.ItemsEventProvider&lt;/className&gt;

- *   &lt;/extensionProvider&gt;

- *   &lt;extensionProvider&gt;

- *       &lt;elementName&gt;item&lt;/elementName&gt;

- *       &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;

- *       &lt;className&gt;org.jivesoftware.smackx.provider.ItemProvider&lt;/className&gt;

- *   &lt;/extensionProvider&gt;

- * 

- * </pre>

- * 

- * @author Robin Collier

- */

-abstract public class EmbeddedExtensionProvider implements PacketExtensionProvider

-{

-

-	final public PacketExtension parseExtension(XmlPullParser parser) throws Exception

-	{

-        String namespace = parser.getNamespace();

-        String name = parser.getName();

-        Map<String, String> attMap = new HashMap<String, String>();

-        

-        for(int i=0; i<parser.getAttributeCount(); i++)

-        {

-        	attMap.put(parser.getAttributeName(i), parser.getAttributeValue(i));

-        }

-        List<PacketExtension> extensions = new ArrayList<PacketExtension>();

-        

-        do

-        {

-            int tag = parser.next();

-

-            if (tag == XmlPullParser.START_TAG) 

-            	extensions.add(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));

-        } while (!name.equals(parser.getName()));

-

-		return createReturnExtension(name, namespace, attMap, extensions);

-	}

-

-	abstract protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content);

-}

+/**
+ * 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.provider;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
+import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * 
+ * This class simplifies parsing of embedded elements by using the 
+ * <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template Method Pattern</a>.  
+ * After extracting the current element attributes and content of any child elements, the template method 
+ * ({@link #createReturnExtension(String, String, Map, List)} is called.  Subclasses
+ * then override this method to create the specific return type.
+ * 
+ * <p>To use this class, you simply register your subclasses as extension providers in the 
+ * <b>smack.properties</b> file.  Then they will be automatically picked up and used to parse
+ * any child elements.  
+ * 
+ * <pre>
+ * For example, given the following message
+ * 
+ * &lt;message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo&gt;
+ *    &lt;event xmlns='http://jabber.org/protocol/pubsub#event&gt;
+ *       &lt;items node='princely_musings'&gt;
+ *          &lt;item id='asdjkwei3i34234n356'&gt;
+ *             &lt;entry xmlns='http://www.w3.org/2005/Atom'&gt;
+ *                &lt;title&gt;Soliloquy&lt;/title&gt;
+ *                &lt;link rel='alternative' type='text/html'/&gt;
+ *                &lt;id>tag:denmark.lit,2003:entry-32397&lt;/id&gt;
+ *             &lt;/entry&gt;
+ *          &lt;/item&gt;
+ *       &lt;/items&gt;
+ *    &lt;/event&gt;
+ * &lt;/message&gt;
+ * 
+ * I would have a classes
+ * {@link ItemsProvider} extends {@link EmbeddedExtensionProvider}
+ * {@link ItemProvider} extends {@link EmbeddedExtensionProvider}
+ * and
+ * AtomProvider extends {@link PacketExtensionProvider}
+ * 
+ * These classes are then registered in the meta-inf/smack.providers file
+ * as follows.
+ * 
+ *   &lt;extensionProvider&gt;
+ *      &lt;elementName&gt;items&lt;/elementName&gt;
+ *      &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;
+ *      &lt;className&gt;org.jivesoftware.smackx.provider.ItemsEventProvider&lt;/className&gt;
+ *   &lt;/extensionProvider&gt;
+ *   &lt;extensionProvider&gt;
+ *       &lt;elementName&gt;item&lt;/elementName&gt;
+ *       &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;
+ *       &lt;className&gt;org.jivesoftware.smackx.provider.ItemProvider&lt;/className&gt;
+ *   &lt;/extensionProvider&gt;
+ * 
+ * </pre>
+ * 
+ * @author Robin Collier
+ */
+abstract public class EmbeddedExtensionProvider implements PacketExtensionProvider
+{
+
+	final public PacketExtension parseExtension(XmlPullParser parser) throws Exception
+	{
+        String namespace = parser.getNamespace();
+        String name = parser.getName();
+        Map<String, String> attMap = new HashMap<String, String>();
+        
+        for(int i=0; i<parser.getAttributeCount(); i++)
+        {
+        	attMap.put(parser.getAttributeName(i), parser.getAttributeValue(i));
+        }
+        List<PacketExtension> extensions = new ArrayList<PacketExtension>();
+        
+        do
+        {
+            int tag = parser.next();
+
+            if (tag == XmlPullParser.START_TAG) 
+            	extensions.add(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));
+        } while (!name.equals(parser.getName()));
+
+		return createReturnExtension(name, namespace, attMap, extensions);
+	}
+
+	abstract protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/PrivacyProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/PrivacyProvider.java
index 62b3120..4ce9ea6 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/PrivacyProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/provider/PrivacyProvider.java
@@ -15,137 +15,137 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smack.provider;

-

-import org.jivesoftware.smack.packet.DefaultPacketExtension;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Privacy;

-import org.jivesoftware.smack.packet.PrivacyItem;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-

-/**

- * The PrivacyProvider parses {@link Privacy} packets. {@link Privacy}

- * Parses the <tt>query</tt> sub-document and creates an instance of {@link Privacy}.

- * For each <tt>item</tt> in the <tt>list</tt> element, it creates an instance 

- * of {@link PrivacyItem} and {@link org.jivesoftware.smack.packet.PrivacyItem.PrivacyRule}.

- * 

- * @author Francisco Vives

- */

-public class PrivacyProvider implements IQProvider {

-

-	public PrivacyProvider() {

-	}

-

-	public IQ parseIQ(XmlPullParser parser) throws Exception {

-        Privacy privacy = new Privacy();

-        /* privacy.addExtension(PacketParserUtils.parsePacketExtension(parser

-                .getName(), parser.getNamespace(), parser)); */

-        privacy.addExtension(new DefaultPacketExtension(parser.getName(), parser.getNamespace()));

-        boolean done = false;

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("active")) {

-                	String activeName = parser.getAttributeValue("", "name");

-                	if (activeName == null) {

-                		privacy.setDeclineActiveList(true);

-                	} else {

-                		privacy.setActiveName(activeName);

-                	}

-                }

-                else if (parser.getName().equals("default")) {

-                	String defaultName = parser.getAttributeValue("", "name");

-                	if (defaultName == null) {

-                		privacy.setDeclineDefaultList(true);

-                	} else {

-                		privacy.setDefaultName(defaultName);

-                	}

-                }

-                else if (parser.getName().equals("list")) {

-                    parseList(parser, privacy);

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("query")) {

-                    done = true;

-                }

-            }

-        }

-

-        return privacy;

-	}

-	

-	// Parse the list complex type

-	public void parseList(XmlPullParser parser, Privacy privacy) throws Exception {

-        boolean done = false;

-        String listName = parser.getAttributeValue("", "name");

-        ArrayList<PrivacyItem> items = new ArrayList<PrivacyItem>();

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("item")) {

-                	items.add(parseItem(parser));

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("list")) {

-                    done = true;

-                }

-            }

-        }

-

-        privacy.setPrivacyList(listName, items);

-	}

-	

-	// Parse the list complex type

-	public PrivacyItem parseItem(XmlPullParser parser) throws Exception {

-        boolean done = false;

-        // Retrieves the required attributes

-        String actionValue = parser.getAttributeValue("", "action");

-        String orderValue = parser.getAttributeValue("", "order");

-        String type = parser.getAttributeValue("", "type");

-        

-        /* 

-         * According the action value it sets the allow status. The fall-through action is assumed 

-         * to be "allow"

-         */

-        boolean allow = true;

-        if ("allow".equalsIgnoreCase(actionValue)) {

-        	allow = true;

-        } else if ("deny".equalsIgnoreCase(actionValue)) {

-        	allow = false;

-        }

-        // Set the order number

-        int order = Integer.parseInt(orderValue);

-

-        // Create the privacy item

-        PrivacyItem item = new PrivacyItem(type, allow, order);

-        item.setValue(parser.getAttributeValue("", "value"));

-

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("iq")) {

-                	item.setFilterIQ(true);

-                }

-                if (parser.getName().equals("message")) {

-                	item.setFilterMessage(true);

-                }

-                if (parser.getName().equals("presence-in")) {

-                	item.setFilterPresence_in(true);

-                }

-                if (parser.getName().equals("presence-out")) {

-                	item.setFilterPresence_out(true);

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("item")) {

-                    done = true;

-                }

-            }

-        }

-        return item;

-	}

-}

+package org.jivesoftware.smack.provider;
+
+import org.jivesoftware.smack.packet.DefaultPacketExtension;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Privacy;
+import org.jivesoftware.smack.packet.PrivacyItem;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+
+/**
+ * The PrivacyProvider parses {@link Privacy} packets. {@link Privacy}
+ * Parses the <tt>query</tt> sub-document and creates an instance of {@link Privacy}.
+ * For each <tt>item</tt> in the <tt>list</tt> element, it creates an instance 
+ * of {@link PrivacyItem} and {@link org.jivesoftware.smack.packet.PrivacyItem.PrivacyRule}.
+ * 
+ * @author Francisco Vives
+ */
+public class PrivacyProvider implements IQProvider {
+
+	public PrivacyProvider() {
+	}
+
+	public IQ parseIQ(XmlPullParser parser) throws Exception {
+        Privacy privacy = new Privacy();
+        /* privacy.addExtension(PacketParserUtils.parsePacketExtension(parser
+                .getName(), parser.getNamespace(), parser)); */
+        privacy.addExtension(new DefaultPacketExtension(parser.getName(), parser.getNamespace()));
+        boolean done = false;
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("active")) {
+                	String activeName = parser.getAttributeValue("", "name");
+                	if (activeName == null) {
+                		privacy.setDeclineActiveList(true);
+                	} else {
+                		privacy.setActiveName(activeName);
+                	}
+                }
+                else if (parser.getName().equals("default")) {
+                	String defaultName = parser.getAttributeValue("", "name");
+                	if (defaultName == null) {
+                		privacy.setDeclineDefaultList(true);
+                	} else {
+                		privacy.setDefaultName(defaultName);
+                	}
+                }
+                else if (parser.getName().equals("list")) {
+                    parseList(parser, privacy);
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("query")) {
+                    done = true;
+                }
+            }
+        }
+
+        return privacy;
+	}
+	
+	// Parse the list complex type
+	public void parseList(XmlPullParser parser, Privacy privacy) throws Exception {
+        boolean done = false;
+        String listName = parser.getAttributeValue("", "name");
+        ArrayList<PrivacyItem> items = new ArrayList<PrivacyItem>();
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("item")) {
+                	items.add(parseItem(parser));
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("list")) {
+                    done = true;
+                }
+            }
+        }
+
+        privacy.setPrivacyList(listName, items);
+	}
+	
+	// Parse the list complex type
+	public PrivacyItem parseItem(XmlPullParser parser) throws Exception {
+        boolean done = false;
+        // Retrieves the required attributes
+        String actionValue = parser.getAttributeValue("", "action");
+        String orderValue = parser.getAttributeValue("", "order");
+        String type = parser.getAttributeValue("", "type");
+        
+        /* 
+         * According the action value it sets the allow status. The fall-through action is assumed 
+         * to be "allow"
+         */
+        boolean allow = true;
+        if ("allow".equalsIgnoreCase(actionValue)) {
+        	allow = true;
+        } else if ("deny".equalsIgnoreCase(actionValue)) {
+        	allow = false;
+        }
+        // Set the order number
+        int order = Integer.parseInt(orderValue);
+
+        // Create the privacy item
+        PrivacyItem item = new PrivacyItem(type, allow, order);
+        item.setValue(parser.getAttributeValue("", "value"));
+
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("iq")) {
+                	item.setFilterIQ(true);
+                }
+                if (parser.getName().equals("message")) {
+                	item.setFilterMessage(true);
+                }
+                if (parser.getName().equals("presence-in")) {
+                	item.setFilterPresence_in(true);
+                }
+                if (parser.getName().equals("presence-out")) {
+                	item.setFilterPresence_out(true);
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("item")) {
+                    done = true;
+                }
+            }
+        }
+        return item;
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLAnonymous.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLAnonymous.java
index 8ef9d18..b959e96 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLAnonymous.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLAnonymous.java
@@ -1,62 +1,62 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-

-import java.io.IOException;

-import javax.security.auth.callback.CallbackHandler;

-

-/**

- * Implementation of the SASL ANONYMOUS mechanism

- *

- * @author Jay Kline

- */

-public class SASLAnonymous extends SASLMechanism {

-

-    public SASLAnonymous(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-    }

-

-    protected String getName() {

-        return "ANONYMOUS";

-    }

-

-    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException {

-        authenticate();

-    }

-

-    public void authenticate(String username, String host, String password) throws IOException {

-        authenticate();

-    }

-

-    protected void authenticate() throws IOException {

-        // Send the authentication to the server

-        getSASLAuthentication().send(new AuthMechanism(getName(), null));

-    }

-

-    public void challengeReceived(String challenge) throws IOException {

-        // Build the challenge response stanza encoding the response text

-        // and send the authentication to the server

-        getSASLAuthentication().send(new Response());

-    }

-

-

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+
+import java.io.IOException;
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * Implementation of the SASL ANONYMOUS mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLAnonymous extends SASLMechanism {
+
+    public SASLAnonymous(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+    }
+
+    protected String getName() {
+        return "ANONYMOUS";
+    }
+
+    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException {
+        authenticate();
+    }
+
+    public void authenticate(String username, String host, String password) throws IOException {
+        authenticate();
+    }
+
+    protected void authenticate() throws IOException {
+        // Send the authentication to the server
+        getSASLAuthentication().send(new AuthMechanism(getName(), null));
+    }
+
+    public void challengeReceived(String challenge) throws IOException {
+        // Build the challenge response stanza encoding the response text
+        // and send the authentication to the server
+        getSASLAuthentication().send(new Response());
+    }
+
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java
index 82d218f..f2be038 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java
@@ -1,38 +1,38 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-

-/**

- * Implementation of the SASL CRAM-MD5 mechanism

- *

- * @author Jay Kline

- */

-public class SASLCramMD5Mechanism extends SASLMechanism {

-

-    public SASLCramMD5Mechanism(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-    }

-

-    protected String getName() {

-        return "CRAM-MD5";

-    }

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+
+/**
+ * Implementation of the SASL CRAM-MD5 mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLCramMD5Mechanism extends SASLMechanism {
+
+    public SASLCramMD5Mechanism(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+    }
+
+    protected String getName() {
+        return "CRAM-MD5";
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java
index 7af65fb..bfaec0a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java
@@ -1,38 +1,38 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-

-/**

- * Implementation of the SASL DIGEST-MD5 mechanism

- *

- * @author Jay Kline

- */

-public class SASLDigestMD5Mechanism extends SASLMechanism {

-

-    public SASLDigestMD5Mechanism(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-    }

-

-    protected String getName() {

-        return "DIGEST-MD5";

-    }

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+
+/**
+ * Implementation of the SASL DIGEST-MD5 mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLDigestMD5Mechanism extends SASLMechanism {
+
+    public SASLDigestMD5Mechanism(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+    }
+
+    protected String getName() {
+        return "DIGEST-MD5";
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java
index dff18fb..248a650 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java
@@ -1,59 +1,59 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-

-/**

- * Implementation of the SASL EXTERNAL mechanism.

- *

- * To effectively use this mechanism, Java must be configured to properly 

- * supply a client SSL certificate (of some sort) to the server. It is up

- * to the implementer to determine how to do this.  Here is one method:

- *

- * Create a java keystore with your SSL certificate in it:

- * keytool -genkey -alias username -dname "cn=username,ou=organizationalUnit,o=organizationaName,l=locality,s=state,c=country"

- *

- * Next, set the System Properties:

- *  <ul>

- *  <li>javax.net.ssl.keyStore to the location of the keyStore

- *  <li>javax.net.ssl.keyStorePassword to the password of the keyStore

- *  <li>javax.net.ssl.trustStore to the location of the trustStore

- *  <li>javax.net.ssl.trustStorePassword to the the password of the trustStore

- *  </ul>

- *

- * Then, when the server requests or requires the client certificate, java will

- * simply provide the one in the keyStore.

- *

- * Also worth noting is the EXTERNAL mechanism in Smack is not enabled by default.

- * To enable it, the implementer will need to call SASLAuthentication.supportSASLMechamism("EXTERNAL");

- *

- * @author Jay Kline

- */

-public class SASLExternalMechanism extends SASLMechanism  {

-

-    public SASLExternalMechanism(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-    }

-

-    protected String getName() {

-        return "EXTERNAL";

-    }

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+
+/**
+ * Implementation of the SASL EXTERNAL mechanism.
+ *
+ * To effectively use this mechanism, Java must be configured to properly 
+ * supply a client SSL certificate (of some sort) to the server. It is up
+ * to the implementer to determine how to do this.  Here is one method:
+ *
+ * Create a java keystore with your SSL certificate in it:
+ * keytool -genkey -alias username -dname "cn=username,ou=organizationalUnit,o=organizationaName,l=locality,s=state,c=country"
+ *
+ * Next, set the System Properties:
+ *  <ul>
+ *  <li>javax.net.ssl.keyStore to the location of the keyStore
+ *  <li>javax.net.ssl.keyStorePassword to the password of the keyStore
+ *  <li>javax.net.ssl.trustStore to the location of the trustStore
+ *  <li>javax.net.ssl.trustStorePassword to the the password of the trustStore
+ *  </ul>
+ *
+ * Then, when the server requests or requires the client certificate, java will
+ * simply provide the one in the keyStore.
+ *
+ * Also worth noting is the EXTERNAL mechanism in Smack is not enabled by default.
+ * To enable it, the implementer will need to call SASLAuthentication.supportSASLMechamism("EXTERNAL");
+ *
+ * @author Jay Kline
+ */
+public class SASLExternalMechanism extends SASLMechanism  {
+
+    public SASLExternalMechanism(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+    }
+
+    protected String getName() {
+        return "EXTERNAL";
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java
index 3f0b7d8..8101ca5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java
@@ -1,89 +1,89 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-import org.jivesoftware.smack.XMPPException;

-

-import java.io.IOException;

-import java.util.Map;

-import java.util.HashMap;

-import javax.security.sasl.Sasl;

-import javax.security.auth.callback.CallbackHandler;

-

-/**

- * Implementation of the SASL GSSAPI mechanism

- *

- * @author Jay Kline

- */

-public class SASLGSSAPIMechanism extends SASLMechanism {

-

-    public SASLGSSAPIMechanism(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-

-        System.setProperty("javax.security.auth.useSubjectCredsOnly","false");

-        System.setProperty("java.security.auth.login.config","gss.conf");

-

-    }

-

-    protected String getName() {

-        return "GSSAPI";

-    }

-

-    /**

-     * Builds and sends the <tt>auth</tt> stanza to the server.

-     * This overrides from the abstract class because the initial token

-     * needed for GSSAPI is binary, and not safe to put in a string, thus

-     * getAuthenticationText() cannot be used.

-     *

-     * @param username the username of the user being authenticated.

-     * @param host     the hostname where the user account resides.

-     * @param cbh      the CallbackHandler (not used with GSSAPI)

-     * @throws IOException If a network error occures while authenticating.

-     */

-    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {

-        String[] mechanisms = { getName() };

-        Map<String,String> props = new HashMap<String,String>();

-        props.put(Sasl.SERVER_AUTH,"TRUE");

-        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);

-        authenticate();

-    }

-

-    /**

-     * Builds and sends the <tt>auth</tt> stanza to the server.

-     * This overrides from the abstract class because the initial token

-     * needed for GSSAPI is binary, and not safe to put in a string, thus

-     * getAuthenticationText() cannot be used.

-     *

-     * @param username the username of the user being authenticated.

-     * @param host     the hostname where the user account resides.

-     * @param password the password of the user (ignored for GSSAPI)

-     * @throws IOException If a network error occures while authenticating.

-     */

-    public void authenticate(String username, String host, String password) throws IOException, XMPPException {

-        String[] mechanisms = { getName() };

-        Map<String,String> props = new HashMap<String, String>();

-        props.put(Sasl.SERVER_AUTH,"TRUE");

-        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);

-        authenticate();

-    }

-

-

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+import org.jivesoftware.smack.XMPPException;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import javax.security.sasl.Sasl;
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * Implementation of the SASL GSSAPI mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLGSSAPIMechanism extends SASLMechanism {
+
+    public SASLGSSAPIMechanism(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+
+        System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
+        System.setProperty("java.security.auth.login.config","gss.conf");
+
+    }
+
+    protected String getName() {
+        return "GSSAPI";
+    }
+
+    /**
+     * Builds and sends the <tt>auth</tt> stanza to the server.
+     * This overrides from the abstract class because the initial token
+     * needed for GSSAPI is binary, and not safe to put in a string, thus
+     * getAuthenticationText() cannot be used.
+     *
+     * @param username the username of the user being authenticated.
+     * @param host     the hostname where the user account resides.
+     * @param cbh      the CallbackHandler (not used with GSSAPI)
+     * @throws IOException If a network error occures while authenticating.
+     */
+    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
+        String[] mechanisms = { getName() };
+        Map<String,String> props = new HashMap<String,String>();
+        props.put(Sasl.SERVER_AUTH,"TRUE");
+        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
+        authenticate();
+    }
+
+    /**
+     * Builds and sends the <tt>auth</tt> stanza to the server.
+     * This overrides from the abstract class because the initial token
+     * needed for GSSAPI is binary, and not safe to put in a string, thus
+     * getAuthenticationText() cannot be used.
+     *
+     * @param username the username of the user being authenticated.
+     * @param host     the hostname where the user account resides.
+     * @param password the password of the user (ignored for GSSAPI)
+     * @throws IOException If a network error occures while authenticating.
+     */
+    public void authenticate(String username, String host, String password) throws IOException, XMPPException {
+        String[] mechanisms = { getName() };
+        Map<String,String> props = new HashMap<String, String>();
+        props.put(Sasl.SERVER_AUTH,"TRUE");
+        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
+        authenticate();
+    }
+
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLMechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLMechanism.java
index 0d096f2..45212e4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLMechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLMechanism.java
@@ -1,404 +1,404 @@
-/**

- * $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.sasl;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.SASLAuthentication;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.util.StringUtils;

-

-import java.io.IOException;

-import java.util.Map;

-import java.util.HashMap;

-import javax.security.auth.callback.CallbackHandler;

-import javax.security.auth.callback.UnsupportedCallbackException;

-import javax.security.auth.callback.Callback;

-import javax.security.auth.callback.NameCallback;

-import javax.security.auth.callback.PasswordCallback;

-import javax.security.sasl.RealmCallback;

-import javax.security.sasl.RealmChoiceCallback;

-import javax.security.sasl.Sasl;

-import javax.security.sasl.SaslClient;

-import javax.security.sasl.SaslException;

-

-/**

- * Base class for SASL mechanisms. Subclasses must implement these methods:

- * <ul>

- *  <li>{@link #getName()} -- returns the common name of the SASL mechanism.</li>

- * </ul>

- * Subclasses will likely want to implement their own versions of these mthods:

- *  <li>{@link #authenticate(String, String, String)} -- Initiate authentication stanza using the

- *  deprecated method.</li>

- *  <li>{@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza

- *  using the CallbackHandler method.</li>

- *  <li>{@link #challengeReceived(String)} -- Handle a challenge from the server.</li>

- * </ul>

- * 

- * Basic XMPP SASL authentication steps:

- * 1. Client authentication initialization, stanza sent to the server (Base64 encoded): 

- *    <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>

- * 2. Server sends back to the client the challenge response (Base64 encoded)

- *    sample: 

- *    realm=<sasl server realm>,nonce="OA6MG9tEQGm2hh",qop="auth",charset=utf-8,algorithm=md5-sess

- * 3. The client responds back to the server (Base 64 encoded):

- *    sample:

- *    username=<userid>,realm=<sasl server realm from above>,nonce="OA6MG9tEQGm2hh",

- *    cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth,

- *    digest-uri=<digesturi>,

- *    response=d388dad90d4bbd760a152321f2143af7,

- *    charset=utf-8,

- *    authzid=<id>

- * 4. The server evaluates if the user is present and contained in the REALM

- *    if successful it sends: <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> (Base64 encoded)

- *    if not successful it sends:

- *    sample:

- *    <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>

- *        cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==

- *    </challenge>       

- * 

-

- *

- * @author Jay Kline

- */

-public abstract class SASLMechanism implements CallbackHandler {

-

-    private SASLAuthentication saslAuthentication;

-    protected SaslClient sc;

-    protected String authenticationId;

-    protected String password;

-    protected String hostname;

-

-    public SASLMechanism(SASLAuthentication saslAuthentication) {

-        this.saslAuthentication = saslAuthentication;

-    }

-

-    /**

-     * Builds and sends the <tt>auth</tt> stanza to the server. Note that this method of

-     * authentication is not recommended, since it is very inflexable. Use

-     * {@link #authenticate(String, String, CallbackHandler)} whenever possible.

-     * 

-     * Explanation of auth stanza:

-     * 

-     * The client authentication stanza needs to include the digest-uri of the form: xmpp/serverName 

-     * From RFC-2831: 

-     * digest-uri = "digest-uri" "=" digest-uri-value

-     * digest-uri-value = serv-type "/" host [ "/" serv-name ]

-     * 

-     * digest-uri: 

-     * Indicates the principal name of the service with which the client 

-     * wishes to connect, formed from the serv-type, host, and serv-name. 

-     * For example, the FTP service

-     * on "ftp.example.com" would have a "digest-uri" value of "ftp/ftp.example.com"; the SMTP

-     * server from the example above would have a "digest-uri" value of

-     * "smtp/mail3.example.com/example.com".

-     * 

-     * host:

-     * The DNS host name or IP address for the service requested. The DNS host name

-     * must be the fully-qualified canonical name of the host. The DNS host name is the

-     * preferred form; see notes on server processing of the digest-uri.

-     * 

-     * serv-name: 

-     * Indicates the name of the service if it is replicated. The service is

-     * considered to be replicated if the client's service-location process involves resolution

-     * using standard DNS lookup operations, and if these operations involve DNS records (such

-     * as SRV, or MX) which resolve one DNS name into a set of other DNS names. In this case,

-     * the initial name used by the client is the "serv-name", and the final name is the "host"

-     * component. For example, the incoming mail service for "example.com" may be replicated

-     * through the use of MX records stored in the DNS, one of which points at an SMTP server

-     * called "mail3.example.com"; it's "serv-name" would be "example.com", it's "host" would be

-     * "mail3.example.com". If the service is not replicated, or the serv-name is identical to

-     * the host, then the serv-name component MUST be omitted

-     * 

-     * digest-uri verification is needed for ejabberd 2.0.3 and higher   

-     * 

-     * @param username the username of the user being authenticated.

-     * @param host the hostname where the user account resides.

-     * @param serviceName the xmpp service location - used by the SASL client in digest-uri creation

-     * serviceName format is: host [ "/" serv-name ] as per RFC-2831

-     * @param password the password for this account.

-     * @throws IOException If a network error occurs while authenticating.

-     * @throws XMPPException If a protocol error occurs or the user is not authenticated.

-     */

-    public void authenticate(String username, String host, String serviceName, String password) throws IOException, XMPPException {

-        //Since we were not provided with a CallbackHandler, we will use our own with the given

-        //information

-

-        //Set the authenticationID as the username, since they must be the same in this case.

-        this.authenticationId = username;

-        this.password = password;

-        this.hostname = host;        

-

-        String[] mechanisms = { getName() };

-        Map<String,String> props = new HashMap<String,String>();        

-        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", serviceName, props, this);

-        authenticate();

-    }

-

-    /**

-     * Same as {@link #authenticate(String, String, String, String)}, but with the hostname used as the serviceName.

-     * <p>

-     * Kept for backward compatibility only.

-     * 

-     * @param username the username of the user being authenticated.

-     * @param host the hostname where the user account resides.

-     * @param password the password for this account.

-     * @throws IOException If a network error occurs while authenticating.

-     * @throws XMPPException If a protocol error occurs or the user is not authenticated.

-     * @deprecated Please use {@link #authenticate(String, String, String, String)} instead.

-     */

-    public void authenticate(String username, String host, String password) throws IOException, XMPPException {

-        authenticate(username, host, host, password);

-    }

-    

-    /**

-     * Builds and sends the <tt>auth</tt> stanza to the server. The callback handler will handle

-     * any additional information, such as the authentication ID or realm, if it is needed.

-     *

-     * @param username the username of the user being authenticated.

-     * @param host     the hostname where the user account resides.

-     * @param cbh      the CallbackHandler to obtain user information.

-     * @throws IOException If a network error occures while authenticating.

-     * @throws XMPPException If a protocol error occurs or the user is not authenticated.

-     */

-    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {

-        String[] mechanisms = { getName() };

-        Map<String,String> props = new HashMap<String,String>();

-        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);

-        authenticate();

-    }

-

-    protected void authenticate() throws IOException, XMPPException {

-        String authenticationText = null;

-        try {

-            if(sc.hasInitialResponse()) {

-                byte[] response = sc.evaluateChallenge(new byte[0]);

-                authenticationText = StringUtils.encodeBase64(response, false);

-            }

-        } catch (SaslException e) {

-            throw new XMPPException("SASL authentication failed", e);

-        }

-

-        // Send the authentication to the server

-        getSASLAuthentication().send(new AuthMechanism(getName(), authenticationText));

-    }

-

-

-    /**

-     * The server is challenging the SASL mechanism for the stanza he just sent. Send a

-     * response to the server's challenge.

-     *

-     * @param challenge a base64 encoded string representing the challenge.

-     * @throws IOException if an exception sending the response occurs.

-     */

-    public void challengeReceived(String challenge) throws IOException {

-        byte response[];

-        if(challenge != null) {

-            response = sc.evaluateChallenge(StringUtils.decodeBase64(challenge));

-        } else {

-            response = sc.evaluateChallenge(new byte[0]);

-        }

-

-        Packet responseStanza;

-        if (response == null) {

-            responseStanza = new Response();

-        }

-        else {

-            responseStanza = new Response(StringUtils.encodeBase64(response, false));

-        }

-

-        // Send the authentication to the server

-        getSASLAuthentication().send(responseStanza);

-    }

-

-    /**

-     * Returns the common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or GSSAPI.

-     *

-     * @return the common name of the SASL mechanism.

-     */

-    protected abstract String getName();

-

-

-    protected SASLAuthentication getSASLAuthentication() {

-        return saslAuthentication;

-    }

-

-    /**

-     * 

-     */

-    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

-        for (int i = 0; i < callbacks.length; i++) {

-            if (callbacks[i] instanceof NameCallback) {

-                NameCallback ncb = (NameCallback)callbacks[i];

-                ncb.setName(authenticationId);

-            } else if(callbacks[i] instanceof PasswordCallback) {

-                PasswordCallback pcb = (PasswordCallback)callbacks[i];

-                pcb.setPassword(password.toCharArray());

-            } else if(callbacks[i] instanceof RealmCallback) {

-                RealmCallback rcb = (RealmCallback)callbacks[i];

-                //Retrieve the REALM from the challenge response that the server returned when the client initiated the authentication 

-                //exchange. If this value is not null or empty, *this value* has to be sent back to the server in the client's response 

-                //to the server's challenge

-                String text = rcb.getDefaultText();

-                //The SASL client (sc) created in smack  uses rcb.getText when creating the negotiatedRealm to send it back to the server

-                //Make sure that this value matches the server's realm

-                rcb.setText(text);

-            } else if(callbacks[i] instanceof RealmChoiceCallback){

-                //unused

-                //RealmChoiceCallback rccb = (RealmChoiceCallback)callbacks[i];

-            } else {

-               throw new UnsupportedCallbackException(callbacks[i]);

-            }

-         }

-    }

-

-    /**

-     * Initiating SASL authentication by select a mechanism.

-     */

-    public class AuthMechanism extends Packet {

-        final private String name;

-        final private String authenticationText;

-

-        public AuthMechanism(String name, String authenticationText) {

-            if (name == null) {

-                throw new NullPointerException("SASL mechanism name shouldn't be null.");

-            }

-            this.name = name;

-            this.authenticationText = authenticationText;

-        }

-

-        public String toXML() {

-            StringBuilder stanza = new StringBuilder();

-            stanza.append("<auth mechanism=\"").append(name);

-            stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");

-            if (authenticationText != null &&

-                    authenticationText.trim().length() > 0) {

-                stanza.append(authenticationText);

-            }

-            stanza.append("</auth>");

-            return stanza.toString();

-        }

-    }

-

-    /**

-     * A SASL challenge stanza.

-     */

-    public static class Challenge extends Packet {

-        final private String data;

-

-        public Challenge(String data) {

-            this.data = data;

-        }

-

-        public String toXML() {

-            StringBuilder stanza = new StringBuilder();

-            stanza.append("<challenge xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");

-            if (data != null &&

-                    data.trim().length() > 0) {

-                stanza.append(data);

-            }

-            stanza.append("</challenge>");

-            return stanza.toString();

-        }

-    }

-

-    /**

-     * A SASL response stanza.

-     */

-    public class Response extends Packet {

-        final private String authenticationText;

-

-        public Response() {

-            authenticationText = null;

-        }

-

-        public Response(String authenticationText) {

-            if (authenticationText == null || authenticationText.trim().length() == 0) {

-                this.authenticationText = null;

-            }

-            else {

-                this.authenticationText = authenticationText;

-            }

-        }

-

-        public String toXML() {

-            StringBuilder stanza = new StringBuilder();

-            stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");

-            if (authenticationText != null) {

-                stanza.append(authenticationText);

-            }

-            stanza.append("</response>");

-            return stanza.toString();

-        }

-    }

-

-    /**

-     * A SASL success stanza.

-     */

-    public static class Success extends Packet {

-        final private String data;

-

-        public Success(String data) {

-            this.data = data;

-        }

-

-        public String toXML() {

-            StringBuilder stanza = new StringBuilder();

-            stanza.append("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");

-            if (data != null &&

-                    data.trim().length() > 0) {

-                stanza.append(data);

-            }

-            stanza.append("</success>");

-            return stanza.toString();

-        }

-    }

-

-    /**

-     * A SASL failure stanza.

-     */

-    public static class Failure extends Packet {

-        final private String condition;

-

-        public Failure(String condition) {

-            this.condition = condition;

-        }

-

-        /**

-         * Get the SASL related error condition.

-         * 

-         * @return the SASL related error condition.

-         */

-        public String getCondition() {

-            return condition;

-        }

-

-        public String toXML() {

-            StringBuilder stanza = new StringBuilder();

-            stanza.append("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");

-            if (condition != null &&

-                    condition.trim().length() > 0) {

-                stanza.append("<").append(condition).append("/>");

-            }

-            stanza.append("</failure>");

-            return stanza.toString();

-        }

-    }        

-}

+/**
+ * $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.sasl;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.SASLAuthentication;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.util.StringUtils;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.sasl.RealmCallback;
+import javax.security.sasl.RealmChoiceCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+/**
+ * Base class for SASL mechanisms. Subclasses must implement these methods:
+ * <ul>
+ *  <li>{@link #getName()} -- returns the common name of the SASL mechanism.</li>
+ * </ul>
+ * Subclasses will likely want to implement their own versions of these mthods:
+ *  <li>{@link #authenticate(String, String, String)} -- Initiate authentication stanza using the
+ *  deprecated method.</li>
+ *  <li>{@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza
+ *  using the CallbackHandler method.</li>
+ *  <li>{@link #challengeReceived(String)} -- Handle a challenge from the server.</li>
+ * </ul>
+ * 
+ * Basic XMPP SASL authentication steps:
+ * 1. Client authentication initialization, stanza sent to the server (Base64 encoded): 
+ *    <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>
+ * 2. Server sends back to the client the challenge response (Base64 encoded)
+ *    sample: 
+ *    realm=<sasl server realm>,nonce="OA6MG9tEQGm2hh",qop="auth",charset=utf-8,algorithm=md5-sess
+ * 3. The client responds back to the server (Base 64 encoded):
+ *    sample:
+ *    username=<userid>,realm=<sasl server realm from above>,nonce="OA6MG9tEQGm2hh",
+ *    cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth,
+ *    digest-uri=<digesturi>,
+ *    response=d388dad90d4bbd760a152321f2143af7,
+ *    charset=utf-8,
+ *    authzid=<id>
+ * 4. The server evaluates if the user is present and contained in the REALM
+ *    if successful it sends: <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> (Base64 encoded)
+ *    if not successful it sends:
+ *    sample:
+ *    <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
+ *        cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==
+ *    </challenge>       
+ * 
+
+ *
+ * @author Jay Kline
+ */
+public abstract class SASLMechanism implements CallbackHandler {
+
+    private SASLAuthentication saslAuthentication;
+    protected SaslClient sc;
+    protected String authenticationId;
+    protected String password;
+    protected String hostname;
+
+    public SASLMechanism(SASLAuthentication saslAuthentication) {
+        this.saslAuthentication = saslAuthentication;
+    }
+
+    /**
+     * Builds and sends the <tt>auth</tt> stanza to the server. Note that this method of
+     * authentication is not recommended, since it is very inflexable. Use
+     * {@link #authenticate(String, String, CallbackHandler)} whenever possible.
+     * 
+     * Explanation of auth stanza:
+     * 
+     * The client authentication stanza needs to include the digest-uri of the form: xmpp/serverName 
+     * From RFC-2831: 
+     * digest-uri = "digest-uri" "=" digest-uri-value
+     * digest-uri-value = serv-type "/" host [ "/" serv-name ]
+     * 
+     * digest-uri: 
+     * Indicates the principal name of the service with which the client 
+     * wishes to connect, formed from the serv-type, host, and serv-name. 
+     * For example, the FTP service
+     * on "ftp.example.com" would have a "digest-uri" value of "ftp/ftp.example.com"; the SMTP
+     * server from the example above would have a "digest-uri" value of
+     * "smtp/mail3.example.com/example.com".
+     * 
+     * host:
+     * The DNS host name or IP address for the service requested. The DNS host name
+     * must be the fully-qualified canonical name of the host. The DNS host name is the
+     * preferred form; see notes on server processing of the digest-uri.
+     * 
+     * serv-name: 
+     * Indicates the name of the service if it is replicated. The service is
+     * considered to be replicated if the client's service-location process involves resolution
+     * using standard DNS lookup operations, and if these operations involve DNS records (such
+     * as SRV, or MX) which resolve one DNS name into a set of other DNS names. In this case,
+     * the initial name used by the client is the "serv-name", and the final name is the "host"
+     * component. For example, the incoming mail service for "example.com" may be replicated
+     * through the use of MX records stored in the DNS, one of which points at an SMTP server
+     * called "mail3.example.com"; it's "serv-name" would be "example.com", it's "host" would be
+     * "mail3.example.com". If the service is not replicated, or the serv-name is identical to
+     * the host, then the serv-name component MUST be omitted
+     * 
+     * digest-uri verification is needed for ejabberd 2.0.3 and higher   
+     * 
+     * @param username the username of the user being authenticated.
+     * @param host the hostname where the user account resides.
+     * @param serviceName the xmpp service location - used by the SASL client in digest-uri creation
+     * serviceName format is: host [ "/" serv-name ] as per RFC-2831
+     * @param password the password for this account.
+     * @throws IOException If a network error occurs while authenticating.
+     * @throws XMPPException If a protocol error occurs or the user is not authenticated.
+     */
+    public void authenticate(String username, String host, String serviceName, String password) throws IOException, XMPPException {
+        //Since we were not provided with a CallbackHandler, we will use our own with the given
+        //information
+
+        //Set the authenticationID as the username, since they must be the same in this case.
+        this.authenticationId = username;
+        this.password = password;
+        this.hostname = host;        
+
+        String[] mechanisms = { getName() };
+        Map<String,String> props = new HashMap<String,String>();        
+        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", serviceName, props, this);
+        authenticate();
+    }
+
+    /**
+     * Same as {@link #authenticate(String, String, String, String)}, but with the hostname used as the serviceName.
+     * <p>
+     * Kept for backward compatibility only.
+     * 
+     * @param username the username of the user being authenticated.
+     * @param host the hostname where the user account resides.
+     * @param password the password for this account.
+     * @throws IOException If a network error occurs while authenticating.
+     * @throws XMPPException If a protocol error occurs or the user is not authenticated.
+     * @deprecated Please use {@link #authenticate(String, String, String, String)} instead.
+     */
+    public void authenticate(String username, String host, String password) throws IOException, XMPPException {
+        authenticate(username, host, host, password);
+    }
+    
+    /**
+     * Builds and sends the <tt>auth</tt> stanza to the server. The callback handler will handle
+     * any additional information, such as the authentication ID or realm, if it is needed.
+     *
+     * @param username the username of the user being authenticated.
+     * @param host     the hostname where the user account resides.
+     * @param cbh      the CallbackHandler to obtain user information.
+     * @throws IOException If a network error occures while authenticating.
+     * @throws XMPPException If a protocol error occurs or the user is not authenticated.
+     */
+    public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
+        String[] mechanisms = { getName() };
+        Map<String,String> props = new HashMap<String,String>();
+        sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
+        authenticate();
+    }
+
+    protected void authenticate() throws IOException, XMPPException {
+        String authenticationText = null;
+        try {
+            if(sc.hasInitialResponse()) {
+                byte[] response = sc.evaluateChallenge(new byte[0]);
+                authenticationText = StringUtils.encodeBase64(response, false);
+            }
+        } catch (SaslException e) {
+            throw new XMPPException("SASL authentication failed", e);
+        }
+
+        // Send the authentication to the server
+        getSASLAuthentication().send(new AuthMechanism(getName(), authenticationText));
+    }
+
+
+    /**
+     * The server is challenging the SASL mechanism for the stanza he just sent. Send a
+     * response to the server's challenge.
+     *
+     * @param challenge a base64 encoded string representing the challenge.
+     * @throws IOException if an exception sending the response occurs.
+     */
+    public void challengeReceived(String challenge) throws IOException {
+        byte response[];
+        if(challenge != null) {
+            response = sc.evaluateChallenge(StringUtils.decodeBase64(challenge));
+        } else {
+            response = sc.evaluateChallenge(new byte[0]);
+        }
+
+        Packet responseStanza;
+        if (response == null) {
+            responseStanza = new Response();
+        }
+        else {
+            responseStanza = new Response(StringUtils.encodeBase64(response, false));
+        }
+
+        // Send the authentication to the server
+        getSASLAuthentication().send(responseStanza);
+    }
+
+    /**
+     * Returns the common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or GSSAPI.
+     *
+     * @return the common name of the SASL mechanism.
+     */
+    protected abstract String getName();
+
+
+    protected SASLAuthentication getSASLAuthentication() {
+        return saslAuthentication;
+    }
+
+    /**
+     * 
+     */
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            if (callbacks[i] instanceof NameCallback) {
+                NameCallback ncb = (NameCallback)callbacks[i];
+                ncb.setName(authenticationId);
+            } else if(callbacks[i] instanceof PasswordCallback) {
+                PasswordCallback pcb = (PasswordCallback)callbacks[i];
+                pcb.setPassword(password.toCharArray());
+            } else if(callbacks[i] instanceof RealmCallback) {
+                RealmCallback rcb = (RealmCallback)callbacks[i];
+                //Retrieve the REALM from the challenge response that the server returned when the client initiated the authentication 
+                //exchange. If this value is not null or empty, *this value* has to be sent back to the server in the client's response 
+                //to the server's challenge
+                String text = rcb.getDefaultText();
+                //The SASL client (sc) created in smack  uses rcb.getText when creating the negotiatedRealm to send it back to the server
+                //Make sure that this value matches the server's realm
+                rcb.setText(text);
+            } else if(callbacks[i] instanceof RealmChoiceCallback){
+                //unused
+                //RealmChoiceCallback rccb = (RealmChoiceCallback)callbacks[i];
+            } else {
+               throw new UnsupportedCallbackException(callbacks[i]);
+            }
+         }
+    }
+
+    /**
+     * Initiating SASL authentication by select a mechanism.
+     */
+    public class AuthMechanism extends Packet {
+        final private String name;
+        final private String authenticationText;
+
+        public AuthMechanism(String name, String authenticationText) {
+            if (name == null) {
+                throw new NullPointerException("SASL mechanism name shouldn't be null.");
+            }
+            this.name = name;
+            this.authenticationText = authenticationText;
+        }
+
+        public String toXML() {
+            StringBuilder stanza = new StringBuilder();
+            stanza.append("<auth mechanism=\"").append(name);
+            stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+            if (authenticationText != null &&
+                    authenticationText.trim().length() > 0) {
+                stanza.append(authenticationText);
+            }
+            stanza.append("</auth>");
+            return stanza.toString();
+        }
+    }
+
+    /**
+     * A SASL challenge stanza.
+     */
+    public static class Challenge extends Packet {
+        final private String data;
+
+        public Challenge(String data) {
+            this.data = data;
+        }
+
+        public String toXML() {
+            StringBuilder stanza = new StringBuilder();
+            stanza.append("<challenge xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+            if (data != null &&
+                    data.trim().length() > 0) {
+                stanza.append(data);
+            }
+            stanza.append("</challenge>");
+            return stanza.toString();
+        }
+    }
+
+    /**
+     * A SASL response stanza.
+     */
+    public class Response extends Packet {
+        final private String authenticationText;
+
+        public Response() {
+            authenticationText = null;
+        }
+
+        public Response(String authenticationText) {
+            if (authenticationText == null || authenticationText.trim().length() == 0) {
+                this.authenticationText = null;
+            }
+            else {
+                this.authenticationText = authenticationText;
+            }
+        }
+
+        public String toXML() {
+            StringBuilder stanza = new StringBuilder();
+            stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+            if (authenticationText != null) {
+                stanza.append(authenticationText);
+            }
+            stanza.append("</response>");
+            return stanza.toString();
+        }
+    }
+
+    /**
+     * A SASL success stanza.
+     */
+    public static class Success extends Packet {
+        final private String data;
+
+        public Success(String data) {
+            this.data = data;
+        }
+
+        public String toXML() {
+            StringBuilder stanza = new StringBuilder();
+            stanza.append("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+            if (data != null &&
+                    data.trim().length() > 0) {
+                stanza.append(data);
+            }
+            stanza.append("</success>");
+            return stanza.toString();
+        }
+    }
+
+    /**
+     * A SASL failure stanza.
+     */
+    public static class Failure extends Packet {
+        final private String condition;
+
+        public Failure(String condition) {
+            this.condition = condition;
+        }
+
+        /**
+         * Get the SASL related error condition.
+         * 
+         * @return the SASL related error condition.
+         */
+        public String getCondition() {
+            return condition;
+        }
+
+        public String toXML() {
+            StringBuilder stanza = new StringBuilder();
+            stanza.append("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+            if (condition != null &&
+                    condition.trim().length() > 0) {
+                stanza.append("<").append(condition).append("/>");
+            }
+            stanza.append("</failure>");
+            return stanza.toString();
+        }
+    }        
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java
index cd973eb..c341d67 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java
@@ -1,34 +1,34 @@
-/**

- *

- * 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.sasl;

-

-import org.jivesoftware.smack.SASLAuthentication;

-

-/**

- * Implementation of the SASL PLAIN mechanism

- *

- * @author Jay Kline

- */

-public class SASLPlainMechanism extends SASLMechanism {

-

-    public SASLPlainMechanism(SASLAuthentication saslAuthentication) {

-        super(saslAuthentication);

-    }

-

-    protected String getName() {

-        return "PLAIN";

-    }

-}

+/**
+ *
+ * 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.sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+
+/**
+ * Implementation of the SASL PLAIN mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLPlainMechanism extends SASLMechanism {
+
+    public SASLPlainMechanism(SASLAuthentication saslAuthentication) {
+        super(saslAuthentication);
+    }
+
+    protected String getName() {
+        return "PLAIN";
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/Base64.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/Base64.java
index ba6eb37..0923f03 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/Base64.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/Base64.java
@@ -4,1686 +4,1686 @@
  * $Date$
  *
  */
-package org.jivesoftware.smack.util;

-

-/**

+package org.jivesoftware.smack.util;
+
+/**
  * <p>Encodes and decodes to and from Base64 notation.</p>
- * This code was obtained from <a href="http://iharder.net/base64">http://iharder.net/base64</a></p>

- *

- *

- * @author Robert Harder

- * @author rob@iharder.net

- * @version 2.2.1

- */

-public class Base64

-{

-

-/* ********  P U B L I C   F I E L D S  ******** */

-

-

-    /** No options specified. Value is zero. */

-    public final static int NO_OPTIONS = 0;

-

-    /** Specify encoding. */

-    public final static int ENCODE = 1;

-

-

-    /** Specify decoding. */

-    public final static int DECODE = 0;

-

-

-    /** Specify that data should be gzip-compressed. */

-    public final static int GZIP = 2;

-

-

-    /** Don't break lines when encoding (violates strict Base64 specification) */

-    public final static int DONT_BREAK_LINES = 8;

-

-	/**

-	 * Encode using Base64-like encoding that is URL- and Filename-safe as described

-	 * in Section 4 of RFC3548:

-	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.

-	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64,

-	 * or at the very least should not be called Base64 without also specifying that is

-	 * was encoded using the URL- and Filename-safe dialect.

-	 */

-	 public final static int URL_SAFE = 16;

-

-

-	 /**

-	  * Encode using the special "ordered" dialect of Base64 described here:

-	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.

-	  */

-	 public final static int ORDERED = 32;

-

-

-/* ********  P R I V A T E   F I E L D S  ******** */

-

-

-    /** Maximum line length (76) of Base64 output. */

-    private final static int MAX_LINE_LENGTH = 76;

-

-

-    /** The equals sign (=) as a byte. */

-    private final static byte EQUALS_SIGN = (byte)'=';

-

-

-    /** The new line character (\n) as a byte. */

-    private final static byte NEW_LINE = (byte)'\n';

-

-

-    /** Preferred encoding. */

-    private final static String PREFERRED_ENCODING = "UTF-8";

-

-

-    // I think I end up not using the BAD_ENCODING indicator.

-    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding

-    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding

-    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding

-

-

-/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */

-

-    /** The 64 valid Base64 values. */

-    //private final static byte[] ALPHABET;

-	/* Host platform me be something funny like EBCDIC, so we hardcode these values. */

-	private final static byte[] _STANDARD_ALPHABET =

-    {

-        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',

-        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',

-        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',

-        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',

-        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',

-        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',

-        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',

-        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',

-        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',

-        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'

-    };

-

-

-    /**

-     * Translates a Base64 value to either its 6-bit reconstruction value

-     * or a negative number indicating some other meaning.

-     **/

-    private final static byte[] _STANDARD_DECODABET =

-    {

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8

-        -5,-5,                                      // Whitespace: Tab and Linefeed

-        -9,-9,                                      // Decimal 11 - 12

-        -5,                                         // Whitespace: Carriage Return

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26

-        -9,-9,-9,-9,-9,                             // Decimal 27 - 31

-        -5,                                         // Whitespace: Space

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42

-        62,                                         // Plus sign at decimal 43

-        -9,-9,-9,                                   // Decimal 44 - 46

-        63,                                         // Slash at decimal 47

-        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine

-        -9,-9,-9,                                   // Decimal 58 - 60

-        -1,                                         // Equals sign at decimal 61

-        -9,-9,-9,                                      // Decimal 62 - 64

-        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'

-        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'

-        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96

-        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'

-        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'

-        -9,-9,-9,-9                                 // Decimal 123 - 126

-        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */

-    };

-

-

-/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */

-

-	/**

-	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:

-	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.

-	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."

-	 */

-    private final static byte[] _URL_SAFE_ALPHABET =

-    {

-      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',

-      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',

-      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',

-      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',

-      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',

-      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',

-      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',

-      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',

-      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',

-      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'

-    };

-

-	/**

-	 * Used in decoding URL- and Filename-safe dialects of Base64.

-	 */

-    private final static byte[] _URL_SAFE_DECODABET =

-    {

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8

-      -5,-5,                                      // Whitespace: Tab and Linefeed

-      -9,-9,                                      // Decimal 11 - 12

-      -5,                                         // Whitespace: Carriage Return

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26

-      -9,-9,-9,-9,-9,                             // Decimal 27 - 31

-      -5,                                         // Whitespace: Space

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42

-      -9,                                         // Plus sign at decimal 43

-      -9,                                         // Decimal 44

-      62,                                         // Minus sign at decimal 45

-      -9,                                         // Decimal 46

-      -9,                                         // Slash at decimal 47

-      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine

-      -9,-9,-9,                                   // Decimal 58 - 60

-      -1,                                         // Equals sign at decimal 61

-      -9,-9,-9,                                   // Decimal 62 - 64

-      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'

-      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'

-      -9,-9,-9,-9,                                // Decimal 91 - 94

-      63,                                         // Underscore at decimal 95

-      -9,                                         // Decimal 96

-      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'

-      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'

-      -9,-9,-9,-9                                 // Decimal 123 - 126

-      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */

-    };

-

-

-

-/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */

-

-	/**

-	 * I don't get the point of this technique, but it is described here:

-	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.

-	 */

-    private final static byte[] _ORDERED_ALPHABET =

-    {

-      (byte)'-',

-      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',

-      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',

-      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',

-      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',

-      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',

-      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',

-      (byte)'_',

-      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',

-      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',

-      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',

-      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'

-    };

-

-	/**

-	 * Used in decoding the "ordered" dialect of Base64.

-	 */

-    private final static byte[] _ORDERED_DECODABET =

-    {

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8

-      -5,-5,                                      // Whitespace: Tab and Linefeed

-      -9,-9,                                      // Decimal 11 - 12

-      -5,                                         // Whitespace: Carriage Return

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26

-      -9,-9,-9,-9,-9,                             // Decimal 27 - 31

-      -5,                                         // Whitespace: Space

-      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42

-      -9,                                         // Plus sign at decimal 43

-      -9,                                         // Decimal 44

-      0,                                          // Minus sign at decimal 45

-      -9,                                         // Decimal 46

-      -9,                                         // Slash at decimal 47

-      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine

-      -9,-9,-9,                                   // Decimal 58 - 60

-      -1,                                         // Equals sign at decimal 61

-      -9,-9,-9,                                   // Decimal 62 - 64

-      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'

-      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'

-      -9,-9,-9,-9,                                // Decimal 91 - 94

-      37,                                         // Underscore at decimal 95

-      -9,                                         // Decimal 96

-      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'

-      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'

-      -9,-9,-9,-9                                 // Decimal 123 - 126

-      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243

-        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */

-    };

-

-

-/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */

-

-

-	/**

-	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on

-	 * the options specified.

-	 * It's possible, though silly, to specify ORDERED and URLSAFE

-	 * in which case one of them will be picked, though there is

-	 * no guarantee as to which one will be picked.

-	 */

-	private final static byte[] getAlphabet( int options )

-	{

-		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;

-		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;

-		else return _STANDARD_ALPHABET;

-

-	}	// end getAlphabet

-

-

-	/**

-	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on

-	 * the options specified.

-	 * It's possible, though silly, to specify ORDERED and URL_SAFE

-	 * in which case one of them will be picked, though there is

-	 * no guarantee as to which one will be picked.

-	 */

-	private final static byte[] getDecodabet( int options )

-	{

-		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;

-		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;

-		else return _STANDARD_DECODABET;

-

-	}	// end getAlphabet

-

-

-

-    /** Defeats instantiation. */

-    private Base64(){}

-

-    /**

-     * Prints command line usage.

-     *

-     * @param msg A message to include with usage info.

-     */

-    private final static void usage( String msg )

-    {

-        System.err.println( msg );

-        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );

-    }   // end usage

-

-

-/* ********  E N C O D I N G   M E T H O D S  ******** */

-

-

-    /**

-     * Encodes up to the first three bytes of array <var>threeBytes</var>

-     * and returns a four-byte array in Base64 notation.

-     * The actual number of significant bytes in your array is

-     * given by <var>numSigBytes</var>.

-     * The array <var>threeBytes</var> needs only be as big as

-     * <var>numSigBytes</var>.

-     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.

-     *

-     * @param b4 A reusable byte array to reduce array instantiation

-     * @param threeBytes the array to convert

-     * @param numSigBytes the number of significant bytes in your array

-     * @return four byte array in Base64 notation.

-     * @since 1.5.1

-     */

-    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )

-    {

-        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );

-        return b4;

-    }   // end encode3to4

-

-

-    /**

-     * <p>Encodes up to three bytes of the array <var>source</var>

-     * and writes the resulting four Base64 bytes to <var>destination</var>.

-     * The source and destination arrays can be manipulated

-     * anywhere along their length by specifying

-     * <var>srcOffset</var> and <var>destOffset</var>.

-     * This method does not check to make sure your arrays

-     * are large enough to accomodate <var>srcOffset</var> + 3 for

-     * the <var>source</var> array or <var>destOffset</var> + 4 for

-     * the <var>destination</var> array.

-     * The actual number of significant bytes in your array is

-     * given by <var>numSigBytes</var>.</p>

-	 * <p>This is the lowest level of the encoding methods with

-	 * all possible parameters.</p>

-     *

-     * @param source the array to convert

-     * @param srcOffset the index where conversion begins

-     * @param numSigBytes the number of significant bytes in your array

-     * @param destination the array to hold the conversion

-     * @param destOffset the index where output will be put

-     * @return the <var>destination</var> array

-     * @since 1.3

-     */

-    private static byte[] encode3to4(

-     byte[] source, int srcOffset, int numSigBytes,

-     byte[] destination, int destOffset, int options )

-    {

-		byte[] ALPHABET = getAlphabet( options );

-

-        //           1         2         3

-        // 01234567890123456789012345678901 Bit position

-        // --------000000001111111122222222 Array position from threeBytes

-        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET

-        //          >>18  >>12  >> 6  >> 0  Right shift necessary

-        //                0x3f  0x3f  0x3f  Additional AND

-

-        // Create buffer with zero-padding if there are only one or two

-        // significant bytes passed in the array.

-        // We have to shift left 24 in order to flush out the 1's that appear

-        // when Java treats a value as negative that is cast from a byte to an int.

-        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )

-                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )

-                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );

-

-        switch( numSigBytes )

-        {

-            case 3:

-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];

-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];

-                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];

-                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];

-                return destination;

-

-            case 2:

-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];

-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];

-                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];

-                destination[ destOffset + 3 ] = EQUALS_SIGN;

-                return destination;

-

-            case 1:

-                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];

-                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];

-                destination[ destOffset + 2 ] = EQUALS_SIGN;

-                destination[ destOffset + 3 ] = EQUALS_SIGN;

-                return destination;

-

-            default:

-                return destination;

-        }   // end switch

-    }   // end encode3to4

-

-

-

-    /**

-     * Serializes an object and returns the Base64-encoded

-     * version of that serialized object. If the object

-     * cannot be serialized or there is another error,

-     * the method will return <tt>null</tt>.

-     * The object is not GZip-compressed before being encoded.

-     *

-     * @param serializableObject The object to encode

-     * @return The Base64-encoded object

-     * @since 1.4

-     */

-    public static String encodeObject( java.io.Serializable serializableObject )

-    {

-        return encodeObject( serializableObject, NO_OPTIONS );

-    }   // end encodeObject

-

-

-

-    /**

-     * Serializes an object and returns the Base64-encoded

-     * version of that serialized object. If the object

-     * cannot be serialized or there is another error,

-     * the method will return <tt>null</tt>.

-     * <p>

-     * Valid options:<pre>

-     *   GZIP: gzip-compresses object before encoding it.

-     *   DONT_BREAK_LINES: don't break lines at 76 characters

-     *     <i>Note: Technically, this makes your encoding non-compliant.</i>

-     * </pre>

-     * <p>

-     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or

-     * <p>

-     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>

-     *

-     * @param serializableObject The object to encode

-     * @param options Specified options

-     * @return The Base64-encoded object

-     * @see Base64#GZIP

-     * @see Base64#DONT_BREAK_LINES

-     * @since 2.0

-     */

-    public static String encodeObject( java.io.Serializable serializableObject, int options )

-    {

-        // Streams

-        java.io.ByteArrayOutputStream  baos  = null;

-        java.io.OutputStream           b64os = null;

-        java.io.ObjectOutputStream     oos   = null;

-        java.util.zip.GZIPOutputStream gzos  = null;

-

-        // Isolate options

-        int gzip           = (options & GZIP);

-        int dontBreakLines = (options & DONT_BREAK_LINES);

-

-        try

-        {

-            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream

-            baos  = new java.io.ByteArrayOutputStream();

-            b64os = new Base64.OutputStream( baos, ENCODE | options );

-

-            // GZip?

-            if( gzip == GZIP )

-            {

-                gzos = new java.util.zip.GZIPOutputStream( b64os );

-                oos  = new java.io.ObjectOutputStream( gzos );

-            }   // end if: gzip

-            else

-                oos   = new java.io.ObjectOutputStream( b64os );

-

-            oos.writeObject( serializableObject );

-        }   // end try

-        catch( java.io.IOException e )

-        {

-            e.printStackTrace();

-            return null;

-        }   // end catch

-        finally

-        {

-            try{ oos.close();   } catch( Exception e ){}

-            try{ gzos.close();  } catch( Exception e ){}

-            try{ b64os.close(); } catch( Exception e ){}

-            try{ baos.close();  } catch( Exception e ){}

-        }   // end finally

-

-        // Return value according to relevant encoding.

-        try

-        {

-            return new String( baos.toByteArray(), PREFERRED_ENCODING );

-        }   // end try

-        catch (java.io.UnsupportedEncodingException uue)

-        {

-            return new String( baos.toByteArray() );

-        }   // end catch

-

-    }   // end encode

-

-

-

-    /**

-     * Encodes a byte array into Base64 notation.

-     * Does not GZip-compress data.

-     *

-     * @param source The data to convert

-     * @since 1.4

-     */

-    public static String encodeBytes( byte[] source )

-    {

-        return encodeBytes( source, 0, source.length, NO_OPTIONS );

-    }   // end encodeBytes

-

-

-

-    /**

-     * Encodes a byte array into Base64 notation.

-     * <p>

-     * Valid options:<pre>

-     *   GZIP: gzip-compresses object before encoding it.

-     *   DONT_BREAK_LINES: don't break lines at 76 characters

-     *     <i>Note: Technically, this makes your encoding non-compliant.</i>

-     * </pre>

-     * <p>

-     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or

-     * <p>

-     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>

-     *

-     *

-     * @param source The data to convert

-     * @param options Specified options

-     * @see Base64#GZIP

-     * @see Base64#DONT_BREAK_LINES

-     * @since 2.0

-     */

-    public static String encodeBytes( byte[] source, int options )

-    {

-        return encodeBytes( source, 0, source.length, options );

-    }   // end encodeBytes

-

-

-    /**

-     * Encodes a byte array into Base64 notation.

-     * Does not GZip-compress data.

-     *

-     * @param source The data to convert

-     * @param off Offset in array where conversion should begin

-     * @param len Length of data to convert

-     * @since 1.4

-     */

-    public static String encodeBytes( byte[] source, int off, int len )

-    {

-        return encodeBytes( source, off, len, NO_OPTIONS );

-    }   // end encodeBytes

-

-

-

-    /**

-     * Encodes a byte array into Base64 notation.

-     * <p>

-     * Valid options:<pre>

-     *   GZIP: gzip-compresses object before encoding it.

-     *   DONT_BREAK_LINES: don't break lines at 76 characters

-     *     <i>Note: Technically, this makes your encoding non-compliant.</i>

-     * </pre>

-     * <p>

-     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or

-     * <p>

-     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>

-     *

-     *

-     * @param source The data to convert

-     * @param off Offset in array where conversion should begin

-     * @param len Length of data to convert

-     * @param options Specified options; alphabet type is pulled from this (standard, url-safe, ordered)

-     * @see Base64#GZIP

-     * @see Base64#DONT_BREAK_LINES

-     * @since 2.0

-     */

-    public static String encodeBytes( byte[] source, int off, int len, int options )

-    {

-        // Isolate options

-        int dontBreakLines = ( options & DONT_BREAK_LINES );

-        int gzip           = ( options & GZIP   );

-

-        // Compress?

-        if( gzip == GZIP )

-        {

-            java.io.ByteArrayOutputStream  baos  = null;

-            java.util.zip.GZIPOutputStream gzos  = null;

-            Base64.OutputStream            b64os = null;

-

-

-            try

-            {

-                // GZip -> Base64 -> ByteArray

-                baos = new java.io.ByteArrayOutputStream();

-                b64os = new Base64.OutputStream( baos, ENCODE | options );

-                gzos  = new java.util.zip.GZIPOutputStream( b64os );

-

-                gzos.write( source, off, len );

-                gzos.close();

-            }   // end try

-            catch( java.io.IOException e )

-            {

-                e.printStackTrace();

-                return null;

-            }   // end catch

-            finally

-            {

-                try{ gzos.close();  } catch( Exception e ){}

-                try{ b64os.close(); } catch( Exception e ){}

-                try{ baos.close();  } catch( Exception e ){}

-            }   // end finally

-

-            // Return value according to relevant encoding.

-            try

-            {

-                return new String( baos.toByteArray(), PREFERRED_ENCODING );

-            }   // end try

-            catch (java.io.UnsupportedEncodingException uue)

-            {

-                return new String( baos.toByteArray() );

-            }   // end catch

-        }   // end if: compress

-

-        // Else, don't compress. Better not to use streams at all then.

-        else

-        {

-            // Convert option to boolean in way that code likes it.

-            boolean breakLines = dontBreakLines == 0;

-

-            int    len43   = len * 4 / 3;

-            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3

-                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding

-                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines

-            int d = 0;

-            int e = 0;

-            int len2 = len - 2;

-            int lineLength = 0;

-            for( ; d < len2; d+=3, e+=4 )

-            {

-                encode3to4( source, d+off, 3, outBuff, e, options );

-

-                lineLength += 4;

-                if( breakLines && lineLength == MAX_LINE_LENGTH )

-                {

-                    outBuff[e+4] = NEW_LINE;

-                    e++;

-                    lineLength = 0;

-                }   // end if: end of line

-            }   // en dfor: each piece of array

-

-            if( d < len )

-            {

-                encode3to4( source, d+off, len - d, outBuff, e, options );

-                e += 4;

-            }   // end if: some padding needed

-

-

-            // Return value according to relevant encoding.

-            try

-            {

-                return new String( outBuff, 0, e, PREFERRED_ENCODING );

-            }   // end try

-            catch (java.io.UnsupportedEncodingException uue)

-            {

-                return new String( outBuff, 0, e );

-            }   // end catch

-

-        }   // end else: don't compress

-

-    }   // end encodeBytes

-

-

-

-

-

-/* ********  D E C O D I N G   M E T H O D S  ******** */

-

-

-    /**

-     * Decodes four bytes from array <var>source</var>

-     * and writes the resulting bytes (up to three of them)

-     * to <var>destination</var>.

-     * The source and destination arrays can be manipulated

-     * anywhere along their length by specifying

-     * <var>srcOffset</var> and <var>destOffset</var>.

-     * This method does not check to make sure your arrays

-     * are large enough to accomodate <var>srcOffset</var> + 4 for

-     * the <var>source</var> array or <var>destOffset</var> + 3 for

-     * the <var>destination</var> array.

-     * This method returns the actual number of bytes that

-     * were converted from the Base64 encoding.

-	 * <p>This is the lowest level of the decoding methods with

-	 * all possible parameters.</p>

-     *

-     *

-     * @param source the array to convert

-     * @param srcOffset the index where conversion begins

-     * @param destination the array to hold the conversion

-     * @param destOffset the index where output will be put

-	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)

-     * @return the number of decoded bytes converted

-     * @since 1.3

-     */

-    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )

-    {

-		byte[] DECODABET = getDecodabet( options );

-

-        // Example: Dk==

-        if( source[ srcOffset + 2] == EQUALS_SIGN )

-        {

-            // Two ways to do the same thing. Don't know which way I like best.

-            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )

-            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );

-            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )

-                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );

-

-            destination[ destOffset ] = (byte)( outBuff >>> 16 );

-            return 1;

-        }

-

-        // Example: DkL=

-        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )

-        {

-            // Two ways to do the same thing. Don't know which way I like best.

-            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )

-            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )

-            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );

-            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )

-                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )

-                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );

-

-            destination[ destOffset     ] = (byte)( outBuff >>> 16 );

-            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );

-            return 2;

-        }

-

-        // Example: DkLE

-        else

-        {

-            try{

-            // Two ways to do the same thing. Don't know which way I like best.

-            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )

-            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )

-            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )

-            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );

-            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )

-                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )

-                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)

-                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );

-

-

-            destination[ destOffset     ] = (byte)( outBuff >> 16 );

-            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );

-            destination[ destOffset + 2 ] = (byte)( outBuff       );

-

-            return 3;

-            }catch( Exception e){

-                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );

-                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );

-                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );

-                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );

-                return -1;

-            }   // end catch

-        }

-    }   // end decodeToBytes

-

-

-

-

-    /**

-     * Very low-level access to decoding ASCII characters in

-     * the form of a byte array. Does not support automatically

-     * gunzipping or any other "fancy" features.

-     *

-     * @param source The Base64 encoded data

-     * @param off    The offset of where to begin decoding

-     * @param len    The length of characters to decode

-     * @return decoded data

-     * @since 1.3

-     */

-    public static byte[] decode( byte[] source, int off, int len, int options )

-    {

-		byte[] DECODABET = getDecodabet( options );

-

-        int    len34   = len * 3 / 4;

-        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output

-        int    outBuffPosn = 0;

-

-        byte[] b4        = new byte[4];

-        int    b4Posn    = 0;

-        int    i         = 0;

-        byte   sbiCrop   = 0;

-        byte   sbiDecode = 0;

-        for( i = off; i < off+len; i++ )

-        {

-            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits

-            sbiDecode = DECODABET[ sbiCrop ];

-

-            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better

-            {

-                if( sbiDecode >= EQUALS_SIGN_ENC )

-                {

-                    b4[ b4Posn++ ] = sbiCrop;

-                    if( b4Posn > 3 )

-                    {

-                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );

-                        b4Posn = 0;

-

-                        // If that was the equals sign, break out of 'for' loop

-                        if( sbiCrop == EQUALS_SIGN )

-                            break;

-                    }   // end if: quartet built

-

-                }   // end if: equals sign or better

-

-            }   // end if: white space, equals sign or better

-            else

-            {

-                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );

-                return null;

-            }   // end else:

-        }   // each input character

-

-        byte[] out = new byte[ outBuffPosn ];

-        System.arraycopy( outBuff, 0, out, 0, outBuffPosn );

-        return out;

-    }   // end decode

-

-

-

-

-    /**

-     * Decodes data from Base64 notation, automatically

-     * detecting gzip-compressed data and decompressing it.

-     *

-     * @param s the string to decode

-     * @return the decoded data

-     * @since 1.4

-     */

-    public static byte[] decode( String s )

-	{

-		return decode( s, NO_OPTIONS );

-	}

-

-

-    /**

-     * Decodes data from Base64 notation, automatically

-     * detecting gzip-compressed data and decompressing it.

-     *

-     * @param s the string to decode

-	 * @param options encode options such as URL_SAFE

-     * @return the decoded data

-     * @since 1.4

-     */

-    public static byte[] decode( String s, int options )

-    {

-        byte[] bytes;

-        try

-        {

-            bytes = s.getBytes( PREFERRED_ENCODING );

-        }   // end try

-        catch( java.io.UnsupportedEncodingException uee )

-        {

-            bytes = s.getBytes();

-        }   // end catch

-		//</change>

-

-        // Decode

-        bytes = decode( bytes, 0, bytes.length, options );

-

-

-        // Check to see if it's gzip-compressed

-        // GZIP Magic Two-Byte Number: 0x8b1f (35615)

-        if( bytes != null && bytes.length >= 4 )

-        {

-

-            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);

-            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )

-            {

-                java.io.ByteArrayInputStream  bais = null;

-                java.util.zip.GZIPInputStream gzis = null;

-                java.io.ByteArrayOutputStream baos = null;

-                byte[] buffer = new byte[2048];

-                int    length = 0;

-

-                try

-                {

-                    baos = new java.io.ByteArrayOutputStream();

-                    bais = new java.io.ByteArrayInputStream( bytes );

-                    gzis = new java.util.zip.GZIPInputStream( bais );

-

-                    while( ( length = gzis.read( buffer ) ) >= 0 )

-                    {

-                        baos.write(buffer,0,length);

-                    }   // end while: reading input

-

-                    // No error? Get new bytes.

-                    bytes = baos.toByteArray();

-

-                }   // end try

-                catch( java.io.IOException e )

-                {

-                    // Just return originally-decoded bytes

-                }   // end catch

-                finally

-                {

-                    try{ baos.close(); } catch( Exception e ){}

-                    try{ gzis.close(); } catch( Exception e ){}

-                    try{ bais.close(); } catch( Exception e ){}

-                }   // end finally

-

-            }   // end if: gzipped

-        }   // end if: bytes.length >= 2

-

-        return bytes;

-    }   // end decode

-

-

-

-

-    /**

-     * Attempts to decode Base64 data and deserialize a Java

-     * Object within. Returns <tt>null</tt> if there was an error.

-     *

-     * @param encodedObject The Base64 data to decode

-     * @return The decoded and deserialized object

-     * @since 1.5

-     */

-    public static Object decodeToObject( String encodedObject )

-    {

-        // Decode and gunzip if necessary

-        byte[] objBytes = decode( encodedObject );

-

-        java.io.ByteArrayInputStream  bais = null;

-        java.io.ObjectInputStream     ois  = null;

-        Object obj = null;

-

-        try

-        {

-            bais = new java.io.ByteArrayInputStream( objBytes );

-            ois  = new java.io.ObjectInputStream( bais );

-

-            obj = ois.readObject();

-        }   // end try

-        catch( java.io.IOException e )

-        {

-            e.printStackTrace();

-            obj = null;

-        }   // end catch

-        catch( java.lang.ClassNotFoundException e )

-        {

-            e.printStackTrace();

-            obj = null;

-        }   // end catch

-        finally

-        {

-            try{ bais.close(); } catch( Exception e ){}

-            try{ ois.close();  } catch( Exception e ){}

-        }   // end finally

-

-        return obj;

-    }   // end decodeObject

-

-

-

-    /**

-     * Convenience method for encoding data to a file.

-     *

-     * @param dataToEncode byte array of data to encode in base64 form

-     * @param filename Filename for saving encoded data

-     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise

-     *

-     * @since 2.1

-     */

-    public static boolean encodeToFile( byte[] dataToEncode, String filename )

-    {

-        boolean success = false;

-        Base64.OutputStream bos = null;

-        try

-        {

-            bos = new Base64.OutputStream(

-                      new java.io.FileOutputStream( filename ), Base64.ENCODE );

-            bos.write( dataToEncode );

-            success = true;

-        }   // end try

-        catch( java.io.IOException e )

-        {

-

-            success = false;

-        }   // end catch: IOException

-        finally

-        {

-            try{ bos.close(); } catch( Exception e ){}

-        }   // end finally

-

-        return success;

-    }   // end encodeToFile

-

-

-    /**

-     * Convenience method for decoding data to a file.

-     *

-     * @param dataToDecode Base64-encoded data as a string

-     * @param filename Filename for saving decoded data

-     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise

-     *

-     * @since 2.1

-     */

-    public static boolean decodeToFile( String dataToDecode, String filename )

-    {

-        boolean success = false;

-        Base64.OutputStream bos = null;

-        try

-        {

-                bos = new Base64.OutputStream(

-                          new java.io.FileOutputStream( filename ), Base64.DECODE );

-                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );

-                success = true;

-        }   // end try

-        catch( java.io.IOException e )

-        {

-            success = false;

-        }   // end catch: IOException

-        finally

-        {

-                try{ bos.close(); } catch( Exception e ){}

-        }   // end finally

-

-        return success;

-    }   // end decodeToFile

-

-

-

-

-    /**

-     * Convenience method for reading a base64-encoded

-     * file and decoding it.

-     *

-     * @param filename Filename for reading encoded data

-     * @return decoded byte array or null if unsuccessful

-     *

-     * @since 2.1

-     */

-    public static byte[] decodeFromFile( String filename )

-    {

-        byte[] decodedData = null;

-        Base64.InputStream bis = null;

-        try

-        {

-            // Set up some useful variables

-            java.io.File file = new java.io.File( filename );

-            byte[] buffer = null;

-            int length   = 0;

-            int numBytes = 0;

-

-            // Check for size of file

-            if( file.length() > Integer.MAX_VALUE )

-            {

-                System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );

-                return null;

-            }   // end if: file too big for int index

-            buffer = new byte[ (int)file.length() ];

-

-            // Open a stream

-            bis = new Base64.InputStream(

-                      new java.io.BufferedInputStream(

-                      new java.io.FileInputStream( file ) ), Base64.DECODE );

-

-            // Read until done

-            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )

-                length += numBytes;

-

-            // Save in a variable to return

-            decodedData = new byte[ length ];

-            System.arraycopy( buffer, 0, decodedData, 0, length );

-

-        }   // end try

-        catch( java.io.IOException e )

-        {

-            System.err.println( "Error decoding from file " + filename );

-        }   // end catch: IOException

-        finally

-        {

-            try{ bis.close(); } catch( Exception e) {}

-        }   // end finally

-

-        return decodedData;

-    }   // end decodeFromFile

-

-

-

-    /**

-     * Convenience method for reading a binary file

-     * and base64-encoding it.

-     *

-     * @param filename Filename for reading binary data

-     * @return base64-encoded string or null if unsuccessful

-     *

-     * @since 2.1

-     */

-    public static String encodeFromFile( String filename )

-    {

-        String encodedData = null;

-        Base64.InputStream bis = null;

-        try

-        {

-            // Set up some useful variables

-            java.io.File file = new java.io.File( filename );

-            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)

-            int length   = 0;

-            int numBytes = 0;

-

-            // Open a stream

-            bis = new Base64.InputStream(

-                      new java.io.BufferedInputStream(

-                      new java.io.FileInputStream( file ) ), Base64.ENCODE );

-

-            // Read until done

-            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )

-                length += numBytes;

-

-            // Save in a variable to return

-            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );

-

-        }   // end try

-        catch( java.io.IOException e )

-        {

-            System.err.println( "Error encoding from file " + filename );

-        }   // end catch: IOException

-        finally

-        {

-            try{ bis.close(); } catch( Exception e) {}

-        }   // end finally

-

-        return encodedData;

-        }   // end encodeFromFile

-

-    /**

-     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.

-     *

-     * @param infile Input file

-     * @param outfile Output file

-     * @since 2.2

-     */

-    public static void encodeFileToFile( String infile, String outfile )

-    {

-        String encoded = Base64.encodeFromFile( infile );

-        java.io.OutputStream out = null;

-        try{

-            out = new java.io.BufferedOutputStream(

-                  new java.io.FileOutputStream( outfile ) );

-            out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.

-        }   // end try

-        catch( java.io.IOException ex ) {

-            ex.printStackTrace();

-        }   // end catch

-        finally {

-            try { out.close(); }

-            catch( Exception ex ){}

-        }   // end finally

-    }   // end encodeFileToFile

-

-

-    /**

-     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.

-     *

-     * @param infile Input file

-     * @param outfile Output file

-     * @since 2.2

-     */

-    public static void decodeFileToFile( String infile, String outfile )

-    {

-        byte[] decoded = Base64.decodeFromFile( infile );

-        java.io.OutputStream out = null;

-        try{

-            out = new java.io.BufferedOutputStream(

-                  new java.io.FileOutputStream( outfile ) );

-            out.write( decoded );

-        }   // end try

-        catch( java.io.IOException ex ) {

-            ex.printStackTrace();

-        }   // end catch

-        finally {

-            try { out.close(); }

-            catch( Exception ex ){}

-        }   // end finally

-    }   // end decodeFileToFile

-

-

-    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */

-

-

-

-    /**

-     * A {@link Base64.InputStream} will read data from another

-     * <tt>java.io.InputStream</tt>, given in the constructor,

-     * and encode/decode to/from Base64 notation on the fly.

-     *

-     * @see Base64

-     * @since 1.3

-     */

-    public static class InputStream extends java.io.FilterInputStream

-    {

-        private boolean encode;         // Encoding or decoding

-        private int     position;       // Current position in the buffer

-        private byte[]  buffer;         // Small buffer holding converted data

-        private int     bufferLength;   // Length of buffer (3 or 4)

-        private int     numSigBytes;    // Number of meaningful bytes in the buffer

-        private int     lineLength;

-        private boolean breakLines;     // Break lines at less than 80 characters

-		private int     options;        // Record options used to create the stream.

-		private byte[]  alphabet;	    // Local copies to avoid extra method calls

-		private byte[]  decodabet;		// Local copies to avoid extra method calls

-

-

-        /**

-         * Constructs a {@link Base64.InputStream} in DECODE mode.

-         *

-         * @param in the <tt>java.io.InputStream</tt> from which to read data.

-         * @since 1.3

-         */

-        public InputStream( java.io.InputStream in )

-        {

-            this( in, DECODE );

-        }   // end constructor

-

-

-        /**

-         * Constructs a {@link Base64.InputStream} in

-         * either ENCODE or DECODE mode.

-         * <p>

-         * Valid options:<pre>

-         *   ENCODE or DECODE: Encode or Decode as data is read.

-         *   DONT_BREAK_LINES: don't break lines at 76 characters

-         *     (only meaningful when encoding)

-         *     <i>Note: Technically, this makes your encoding non-compliant.</i>

-         * </pre>

-         * <p>

-         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>

-         *

-         *

-         * @param in the <tt>java.io.InputStream</tt> from which to read data.

-         * @param options Specified options

-         * @see Base64#ENCODE

-         * @see Base64#DECODE

-         * @see Base64#DONT_BREAK_LINES

-         * @since 2.0

-         */

-        public InputStream( java.io.InputStream in, int options )

-        {

-            super( in );

-            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;

-            this.encode       = (options & ENCODE) == ENCODE;

-            this.bufferLength = encode ? 4 : 3;

-            this.buffer       = new byte[ bufferLength ];

-            this.position     = -1;

-            this.lineLength   = 0;

-			this.options      = options; // Record for later, mostly to determine which alphabet to use

-			this.alphabet     = getAlphabet(options);

-			this.decodabet    = getDecodabet(options);

-        }   // end constructor

-

-        /**

-         * Reads enough of the input stream to convert

-         * to/from Base64 and returns the next byte.

-         *

-         * @return next byte

-         * @since 1.3

-         */

-        public int read() throws java.io.IOException

-        {

-            // Do we need to get data?

-            if( position < 0 )

-            {

-                if( encode )

-                {

-                    byte[] b3 = new byte[3];

-                    int numBinaryBytes = 0;

-                    for( int i = 0; i < 3; i++ )

-                    {

-                        try

-                        {

-                            int b = in.read();

-

-                            // If end of stream, b is -1.

-                            if( b >= 0 )

-                            {

-                                b3[i] = (byte)b;

-                                numBinaryBytes++;

-                            }   // end if: not end of stream

-

-                        }   // end try: read

-                        catch( java.io.IOException e )

-                        {

-                            // Only a problem if we got no data at all.

-                            if( i == 0 )

-                                throw e;

-

-                        }   // end catch

-                    }   // end for: each needed input byte

-

-                    if( numBinaryBytes > 0 )

-                    {

-                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );

-                        position = 0;

-                        numSigBytes = 4;

-                    }   // end if: got data

-                    else

-                    {

-                        return -1;

-                    }   // end else

-                }   // end if: encoding

-

-                // Else decoding

-                else

-                {

-                    byte[] b4 = new byte[4];

-                    int i = 0;

-                    for( i = 0; i < 4; i++ )

-                    {

-                        // Read four "meaningful" bytes:

-                        int b = 0;

-                        do{ b = in.read(); }

-                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );

-

-                        if( b < 0 )

-                            break; // Reads a -1 if end of stream

-

-                        b4[i] = (byte)b;

-                    }   // end for: each needed input byte

-

-                    if( i == 4 )

-                    {

-                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );

-                        position = 0;

-                    }   // end if: got four characters

-                    else if( i == 0 ){

-                        return -1;

-                    }   // end else if: also padded correctly

-                    else

-                    {

-                        // Must have broken out from above.

-                        throw new java.io.IOException( "Improperly padded Base64 input." );

-                    }   // end

-

-                }   // end else: decode

-            }   // end else: get data

-

-            // Got data?

-            if( position >= 0 )

-            {

-                // End of relevant data?

-                if( /*!encode &&*/ position >= numSigBytes )

-                    return -1;

-

-                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )

-                {

-                    lineLength = 0;

-                    return '\n';

-                }   // end if

-                else

-                {

-                    lineLength++;   // This isn't important when decoding

-                                    // but throwing an extra "if" seems

-                                    // just as wasteful.

-

-                    int b = buffer[ position++ ];

-

-                    if( position >= bufferLength )

-                        position = -1;

-

-                    return b & 0xFF; // This is how you "cast" a byte that's

-                                     // intended to be unsigned.

-                }   // end else

-            }   // end if: position >= 0

-

-            // Else error

-            else

-            {

-                // When JDK1.4 is more accepted, use an assertion here.

-                throw new java.io.IOException( "Error in Base64 code reading stream." );

-            }   // end else

-        }   // end read

-

-

-        /**

-         * Calls {@link #read()} repeatedly until the end of stream

-         * is reached or <var>len</var> bytes are read.

-         * Returns number of bytes read into array or -1 if

-         * end of stream is encountered.

-         *

-         * @param dest array to hold values

-         * @param off offset for array

-         * @param len max number of bytes to read into array

-         * @return bytes read into array or -1 if end of stream is encountered.

-         * @since 1.3

-         */

-        public int read( byte[] dest, int off, int len ) throws java.io.IOException

-        {

-            int i;

-            int b;

-            for( i = 0; i < len; i++ )

-            {

-                b = read();

-

-                //if( b < 0 && i == 0 )

-                //    return -1;

-

-                if( b >= 0 )

-                    dest[off + i] = (byte)b;

-                else if( i == 0 )

-                    return -1;

-                else

-                    break; // Out of 'for' loop

-            }   // end for: each byte read

-            return i;

-        }   // end read

-

-    }   // end inner class InputStream

-

-

-

-

-

-

-    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */

-

-

-

-    /**

-     * A {@link Base64.OutputStream} will write data to another

-     * <tt>java.io.OutputStream</tt>, given in the constructor,

-     * and encode/decode to/from Base64 notation on the fly.

-     *

-     * @see Base64

-     * @since 1.3

-     */

-    public static class OutputStream extends java.io.FilterOutputStream

-    {

-        private boolean encode;

-        private int     position;

-        private byte[]  buffer;

-        private int     bufferLength;

-        private int     lineLength;

-        private boolean breakLines;

-        private byte[]  b4; // Scratch used in a few places

-        private boolean suspendEncoding;

-		private int options; // Record for later

-		private byte[]  alphabet;	    // Local copies to avoid extra method calls

-		private byte[]  decodabet;		// Local copies to avoid extra method calls

-

-        /**

-         * Constructs a {@link Base64.OutputStream} in ENCODE mode.

-         *

-         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.

-         * @since 1.3

-         */

-        public OutputStream( java.io.OutputStream out )

-        {

-            this( out, ENCODE );

-        }   // end constructor

-

-

-        /**

-         * Constructs a {@link Base64.OutputStream} in

-         * either ENCODE or DECODE mode.

-         * <p>

-         * Valid options:<pre>

-         *   ENCODE or DECODE: Encode or Decode as data is read.

-         *   DONT_BREAK_LINES: don't break lines at 76 characters

-         *     (only meaningful when encoding)

-         *     <i>Note: Technically, this makes your encoding non-compliant.</i>

-         * </pre>

-         * <p>

-         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>

-         *

-         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.

-         * @param options Specified options.

-         * @see Base64#ENCODE

-         * @see Base64#DECODE

-         * @see Base64#DONT_BREAK_LINES

-         * @since 1.3

-         */

-        public OutputStream( java.io.OutputStream out, int options )

-        {

-            super( out );

-            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;

-            this.encode       = (options & ENCODE) == ENCODE;

-            this.bufferLength = encode ? 3 : 4;

-            this.buffer       = new byte[ bufferLength ];

-            this.position     = 0;

-            this.lineLength   = 0;

-            this.suspendEncoding = false;

-            this.b4           = new byte[4];

-			this.options      = options;

-			this.alphabet     = getAlphabet(options);

-			this.decodabet    = getDecodabet(options);

-        }   // end constructor

-

-

-        /**

-         * Writes the byte to the output stream after

-         * converting to/from Base64 notation.

-         * When encoding, bytes are buffered three

-         * at a time before the output stream actually

-         * gets a write() call.

-         * When decoding, bytes are buffered four

-         * at a time.

-         *

-         * @param theByte the byte to write

-         * @since 1.3

-         */

-        public void write(int theByte) throws java.io.IOException

-        {

-            // Encoding suspended?

-            if( suspendEncoding )

-            {

-                super.out.write( theByte );

-                return;

-            }   // end if: supsended

-

-            // Encode?

-            if( encode )

-            {

-                buffer[ position++ ] = (byte)theByte;

-                if( position >= bufferLength )  // Enough to encode.

-                {

-                    out.write( encode3to4( b4, buffer, bufferLength, options ) );

-

-                    lineLength += 4;

-                    if( breakLines && lineLength >= MAX_LINE_LENGTH )

-                    {

-                        out.write( NEW_LINE );

-                        lineLength = 0;

-                    }   // end if: end of line

-

-                    position = 0;

-                }   // end if: enough to output

-            }   // end if: encoding

-

-            // Else, Decoding

-            else

-            {

-                // Meaningful Base64 character?

-                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )

-                {

-                    buffer[ position++ ] = (byte)theByte;

-                    if( position >= bufferLength )  // Enough to output.

-                    {

-                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );

-                        out.write( b4, 0, len );

-                        //out.write( Base64.decode4to3( buffer ) );

-                        position = 0;

-                    }   // end if: enough to output

-                }   // end if: meaningful base64 character

-                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )

-                {

-                    throw new java.io.IOException( "Invalid character in Base64 data." );

-                }   // end else: not white space either

-            }   // end else: decoding

-        }   // end write

-

-

-

-        /**

-         * Calls {@link #write(int)} repeatedly until <var>len</var>

-         * bytes are written.

-         *

-         * @param theBytes array from which to read bytes

-         * @param off offset for array

-         * @param len max number of bytes to read into array

-         * @since 1.3

-         */

-        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException

-        {

-            // Encoding suspended?

-            if( suspendEncoding )

-            {

-                super.out.write( theBytes, off, len );

-                return;

-            }   // end if: supsended

-

-            for( int i = 0; i < len; i++ )

-            {

-                write( theBytes[ off + i ] );

-            }   // end for: each byte written

-

-        }   // end write

-

-

-

-        /**

-         * Method added by PHIL. [Thanks, PHIL. -Rob]

-         * This pads the buffer without closing the stream.

-         */

-        public void flushBase64() throws java.io.IOException

-        {

-            if( position > 0 )

-            {

-                if( encode )

-                {

-                    out.write( encode3to4( b4, buffer, position, options ) );

-                    position = 0;

-                }   // end if: encoding

-                else

-                {

-                    throw new java.io.IOException( "Base64 input not properly padded." );

-                }   // end else: decoding

-            }   // end if: buffer partially full

-

-        }   // end flush

-

-

-        /**

-         * Flushes and closes (I think, in the superclass) the stream.

-         *

-         * @since 1.3

-         */

-        public void close() throws java.io.IOException

-        {

-            // 1. Ensure that pending characters are written

-            flushBase64();

-

-            // 2. Actually close the stream

-            // Base class both flushes and closes.

-            super.close();

-

-            buffer = null;

-            out    = null;

-        }   // end close

-

-

-

-        /**

-         * Suspends encoding of the stream.

-         * May be helpful if you need to embed a piece of

-         * base640-encoded data in a stream.

-         *

-         * @since 1.5.1

-         */

-        public void suspendEncoding() throws java.io.IOException

-        {

-            flushBase64();

-            this.suspendEncoding = true;

-        }   // end suspendEncoding

-

-

-        /**

-         * Resumes encoding of the stream.

-         * May be helpful if you need to embed a piece of

-         * base640-encoded data in a stream.

-         *

-         * @since 1.5.1

-         */

-        public void resumeEncoding()

-        {

-            this.suspendEncoding = false;

-        }   // end resumeEncoding

-

-

-

-    }   // end inner class OutputStream

-

-

-}   // end class Base64

-

+ * This code was obtained from <a href="http://iharder.net/base64">http://iharder.net/base64</a></p>
+ *
+ *
+ * @author Robert Harder
+ * @author rob@iharder.net
+ * @version 2.2.1
+ */
+public class Base64
+{
+
+/* ********  P U B L I C   F I E L D S  ******** */
+
+
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+
+
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+
+
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+
+
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+
+	/**
+	 * Encode using Base64-like encoding that is URL- and Filename-safe as described
+	 * in Section 4 of RFC3548:
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
+	 * or at the very least should not be called Base64 without also specifying that is
+	 * was encoded using the URL- and Filename-safe dialect.
+	 */
+	 public final static int URL_SAFE = 16;
+
+
+	 /**
+	  * Encode using the special "ordered" dialect of Base64 described here:
+	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	  */
+	 public final static int ORDERED = 32;
+
+
+/* ********  P R I V A T E   F I E L D S  ******** */
+
+
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+
+
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+
+
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+
+
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+
+
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+
+
+/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */
+
+    /** The 64 valid Base64 values. */
+    //private final static byte[] ALPHABET;
+	/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
+	private final static byte[] _STANDARD_ALPHABET =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+
+
+    /**
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] _STANDARD_DECODABET =
+    {
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
+
+	/**
+	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
+	 */
+    private final static byte[] _URL_SAFE_ALPHABET =
+    {
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
+    };
+
+	/**
+	 * Used in decoding URL- and Filename-safe dialects of Base64.
+	 */
+    private final static byte[] _URL_SAFE_DECODABET =
+    {
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      62,                                         // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      63,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+
+/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
+
+	/**
+	 * I don't get the point of this technique, but it is described here:
+	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	 */
+    private final static byte[] _ORDERED_ALPHABET =
+    {
+      (byte)'-',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
+      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'_',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
+    };
+
+	/**
+	 * Used in decoding the "ordered" dialect of Base64.
+	 */
+    private final static byte[] _ORDERED_DECODABET =
+    {
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      0,                                          // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
+      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      37,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
+      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
+
+
+	/**
+	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URLSAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getAlphabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
+		else return _STANDARD_ALPHABET;
+
+	}	// end getAlphabet
+
+
+	/**
+	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URL_SAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getDecodabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
+		else return _STANDARD_DECODABET;
+
+	}	// end getAlphabet
+
+
+
+    /** Defeats instantiation. */
+    private Base64(){}
+
+    /**
+     * Prints command line usage.
+     *
+     * @param msg A message to include with usage info.
+     */
+    private final static void usage( String msg )
+    {
+        System.err.println( msg );
+        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );
+    }   // end usage
+
+
+/* ********  E N C O D I N G   M E T H O D S  ******** */
+
+
+    /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var>
+     * and returns a four-byte array in Base64 notation.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     * The array <var>threeBytes</var> needs only be as big as
+     * <var>numSigBytes</var>.
+     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     *
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
+    {
+        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
+        return b4;
+    }   // end encode3to4
+
+
+    /**
+     * <p>Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.</p>
+	 * <p>This is the lowest level of the encoding methods with
+	 * all possible parameters.</p>
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4(
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset, int options )
+    {
+		byte[] ALPHABET = getAlphabet( options );
+
+        //           1         2         3
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * The object is not GZip-compressed before being encoded.
+     *
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
+     * @since 1.4
+     */
+    public static String encodeObject( java.io.Serializable serializableObject )
+    {
+        return encodeObject( serializableObject, NO_OPTIONS );
+    }   // end encodeObject
+
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject, int options )
+    {
+        // Streams
+        java.io.ByteArrayOutputStream  baos  = null;
+        java.io.OutputStream           b64os = null;
+        java.io.ObjectOutputStream     oos   = null;
+        java.util.zip.GZIPOutputStream gzos  = null;
+
+        // Isolate options
+        int gzip           = (options & GZIP);
+        int dontBreakLines = (options & DONT_BREAK_LINES);
+
+        try
+        {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos  = new java.io.ByteArrayOutputStream();
+            b64os = new Base64.OutputStream( baos, ENCODE | options );
+
+            // GZip?
+            if( gzip == GZIP )
+            {
+                gzos = new java.util.zip.GZIPOutputStream( b64os );
+                oos  = new java.io.ObjectOutputStream( gzos );
+            }   // end if: gzip
+            else
+                oos   = new java.io.ObjectOutputStream( b64os );
+
+            oos.writeObject( serializableObject );
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            e.printStackTrace();
+            return null;
+        }   // end catch
+        finally
+        {
+            try{ oos.close();   } catch( Exception e ){}
+            try{ gzos.close();  } catch( Exception e ){}
+            try{ b64os.close(); } catch( Exception e ){}
+            try{ baos.close();  } catch( Exception e ){}
+        }   // end finally
+
+        // Return value according to relevant encoding.
+        try
+        {
+            return new String( baos.toByteArray(), PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException uue)
+        {
+            return new String( baos.toByteArray() );
+        }   // end catch
+
+    }   // end encode
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options Specified options; alphabet type is pulled from this (standard, url-safe, ordered)
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+
+        // Compress?
+        if( gzip == GZIP )
+        {
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            Base64.OutputStream            b64os = null;
+
+
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream( baos, ENCODE | options );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os );
+
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+                e.printStackTrace();
+                return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){}
+                try{ b64os.close(); } catch( Exception e ){}
+                try{ baos.close();  } catch( Exception e ){}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+        }   // end if: compress
+
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e, options );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e, options );
+                e += 4;
+            }   // end if: some padding needed
+
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+
+        }   // end else: don't compress
+
+    }   // end encodeBytes
+
+
+
+
+
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+
+
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that
+     * were converted from the Base64 encoding.
+	 * <p>This is the lowest level of the decoding methods with
+	 * all possible parameters.</p>
+     *
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
+    {
+		byte[] DECODABET = getDecodabet( options );
+
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
+                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
+                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
+                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
+                return -1;
+            }   // end catch
+        }
+    }   // end decodeToBytes
+
+
+
+
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len, int options )
+    {
+		byte[] DECODABET = getDecodabet( options );
+
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
+                        b4Posn = 0;
+
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+
+                }   // end if: equals sign or better
+
+            }   // end if: white space, equals sign or better
+            else
+            {
+                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else:
+        }   // each input character
+
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
+        return out;
+    }   // end decode
+
+
+
+
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+	{
+		return decode( s, NO_OPTIONS );
+	}
+
+
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+	 * @param options encode options such as URL_SAFE
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s, int options )
+    {
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();
+        }   // end catch
+		//</change>
+
+        // Decode
+        bytes = decode( bytes, 0, bytes.length, options );
+
+
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){}
+                    try{ gzis.close(); } catch( Exception e ){}
+                    try{ bais.close(); } catch( Exception e ){}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+
+        return bytes;
+    }   // end decode
+
+
+
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java
+     * Object within. Returns <tt>null</tt> if there was an error.
+     *
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject )
+    {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode( encodedObject );
+
+        java.io.ByteArrayInputStream  bais = null;
+        java.io.ObjectInputStream     ois  = null;
+        Object obj = null;
+
+        try
+        {
+            bais = new java.io.ByteArrayInputStream( objBytes );
+            ois  = new java.io.ObjectInputStream( bais );
+
+            obj = ois.readObject();
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            e.printStackTrace();
+            obj = null;
+        }   // end catch
+        catch( java.lang.ClassNotFoundException e )
+        {
+            e.printStackTrace();
+            obj = null;
+        }   // end catch
+        finally
+        {
+            try{ bais.close(); } catch( Exception e ){}
+            try{ ois.close();  } catch( Exception e ){}
+        }   // end finally
+
+        return obj;
+    }   // end decodeObject
+
+
+
+    /**
+     * Convenience method for encoding data to a file.
+     *
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+            bos = new Base64.OutputStream(
+                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
+            bos.write( dataToEncode );
+            success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+            try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+
+        return success;
+    }   // end encodeToFile
+
+
+    /**
+     * Convenience method for decoding data to a file.
+     *
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+                bos = new Base64.OutputStream(
+                          new java.io.FileOutputStream( filename ), Base64.DECODE );
+                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+                success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+                try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+
+        return success;
+    }   // end decodeToFile
+
+
+
+
+    /**
+     * Convenience method for reading a base64-encoded
+     * file and decoding it.
+     *
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename )
+    {
+        byte[] decodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = null;
+            int length   = 0;
+            int numBytes = 0;
+
+            // Check for size of file
+            if( file.length() > Integer.MAX_VALUE )
+            {
+                System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
+                return null;
+            }   // end if: file too big for int index
+            buffer = new byte[ (int)file.length() ];
+
+            // Open a stream
+            bis = new Base64.InputStream(
+                      new java.io.BufferedInputStream(
+                      new java.io.FileInputStream( file ) ), Base64.DECODE );
+
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+
+            // Save in a variable to return
+            decodedData = new byte[ length ];
+            System.arraycopy( buffer, 0, decodedData, 0, length );
+
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            System.err.println( "Error decoding from file " + filename );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+
+        return decodedData;
+    }   // end decodeFromFile
+
+
+
+    /**
+     * Convenience method for reading a binary file
+     * and base64-encoding it.
+     *
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename )
+    {
+        String encodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
+            int length   = 0;
+            int numBytes = 0;
+
+            // Open a stream
+            bis = new Base64.InputStream(
+                      new java.io.BufferedInputStream(
+                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
+
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+
+            // Save in a variable to return
+            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
+
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            System.err.println( "Error encoding from file " + filename );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+
+        return encodedData;
+        }   // end encodeFromFile
+
+    /**
+     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @since 2.2
+     */
+    public static void encodeFileToFile( String infile, String outfile )
+    {
+        String encoded = Base64.encodeFromFile( infile );
+        java.io.OutputStream out = null;
+        try{
+            out = new java.io.BufferedOutputStream(
+                  new java.io.FileOutputStream( outfile ) );
+            out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
+        }   // end try
+        catch( java.io.IOException ex ) {
+            ex.printStackTrace();
+        }   // end catch
+        finally {
+            try { out.close(); }
+            catch( Exception ex ){}
+        }   // end finally
+    }   // end encodeFileToFile
+
+
+    /**
+     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @since 2.2
+     */
+    public static void decodeFileToFile( String infile, String outfile )
+    {
+        byte[] decoded = Base64.decodeFromFile( infile );
+        java.io.OutputStream out = null;
+        try{
+            out = new java.io.BufferedOutputStream(
+                  new java.io.FileOutputStream( outfile ) );
+            out.write( decoded );
+        }   // end try
+        catch( java.io.IOException ex ) {
+            ex.printStackTrace();
+        }   // end catch
+        finally {
+            try { out.close(); }
+            catch( Exception ex ){}
+        }   // end finally
+    }   // end decodeFileToFile
+
+
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+
+
+
+    /**
+     * A {@link Base64.InputStream} will read data from another
+     * <tt>java.io.InputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream
+    {
+        private boolean encode;         // Encoding or decoding
+        private int     position;       // Current position in the buffer
+        private byte[]  buffer;         // Small buffer holding converted data
+        private int     bufferLength;   // Length of buffer (3 or 4)
+        private int     numSigBytes;    // Number of meaningful bytes in the buffer
+        private int     lineLength;
+        private boolean breakLines;     // Break lines at less than 80 characters
+		private int     options;        // Record options used to create the stream.
+		private byte[]  alphabet;	    // Local copies to avoid extra method calls
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+
+
+        /**
+         * Constructs a {@link Base64.InputStream} in DECODE mode.
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in )
+        {
+            this( in, DECODE );
+        }   // end constructor
+
+
+        /**
+         * Constructs a {@link Base64.InputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+         *
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in, int options )
+        {
+            super( in );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = -1;
+            this.lineLength   = 0;
+			this.options      = options; // Record for later, mostly to determine which alphabet to use
+			this.alphabet     = getAlphabet(options);
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+
+        /**
+         * Reads enough of the input stream to convert
+         * to/from Base64 and returns the next byte.
+         *
+         * @return next byte
+         * @since 1.3
+         */
+        public int read() throws java.io.IOException
+        {
+            // Do we need to get data?
+            if( position < 0 )
+            {
+                if( encode )
+                {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for( int i = 0; i < 3; i++ )
+                    {
+                        try
+                        {
+                            int b = in.read();
+
+                            // If end of stream, b is -1.
+                            if( b >= 0 )
+                            {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            }   // end if: not end of stream
+
+                        }   // end try: read
+                        catch( java.io.IOException e )
+                        {
+                            // Only a problem if we got no data at all.
+                            if( i == 0 )
+                                throw e;
+
+                        }   // end catch
+                    }   // end for: each needed input byte
+
+                    if( numBinaryBytes > 0 )
+                    {
+                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
+                        position = 0;
+                        numSigBytes = 4;
+                    }   // end if: got data
+                    else
+                    {
+                        return -1;
+                    }   // end else
+                }   // end if: encoding
+
+                // Else decoding
+                else
+                {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for( i = 0; i < 4; i++ )
+                    {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do{ b = in.read(); }
+                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
+
+                        if( b < 0 )
+                            break; // Reads a -1 if end of stream
+
+                        b4[i] = (byte)b;
+                    }   // end for: each needed input byte
+
+                    if( i == 4 )
+                    {
+                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
+                        position = 0;
+                    }   // end if: got four characters
+                    else if( i == 0 ){
+                        return -1;
+                    }   // end else if: also padded correctly
+                    else
+                    {
+                        // Must have broken out from above.
+                        throw new java.io.IOException( "Improperly padded Base64 input." );
+                    }   // end
+
+                }   // end else: decode
+            }   // end else: get data
+
+            // Got data?
+            if( position >= 0 )
+            {
+                // End of relevant data?
+                if( /*!encode &&*/ position >= numSigBytes )
+                    return -1;
+
+                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+                {
+                    lineLength = 0;
+                    return '\n';
+                }   // end if
+                else
+                {
+                    lineLength++;   // This isn't important when decoding
+                                    // but throwing an extra "if" seems
+                                    // just as wasteful.
+
+                    int b = buffer[ position++ ];
+
+                    if( position >= bufferLength )
+                        position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                                     // intended to be unsigned.
+                }   // end else
+            }   // end if: position >= 0
+
+            // Else error
+            else
+            {
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new java.io.IOException( "Error in Base64 code reading stream." );
+            }   // end else
+        }   // end read
+
+
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream
+         * is reached or <var>len</var> bytes are read.
+         * Returns number of bytes read into array or -1 if
+         * end of stream is encountered.
+         *
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @since 1.3
+         */
+        public int read( byte[] dest, int off, int len ) throws java.io.IOException
+        {
+            int i;
+            int b;
+            for( i = 0; i < len; i++ )
+            {
+                b = read();
+
+                //if( b < 0 && i == 0 )
+                //    return -1;
+
+                if( b >= 0 )
+                    dest[off + i] = (byte)b;
+                else if( i == 0 )
+                    return -1;
+                else
+                    break; // Out of 'for' loop
+            }   // end for: each byte read
+            return i;
+        }   // end read
+
+    }   // end inner class InputStream
+
+
+
+
+
+
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+
+
+
+    /**
+     * A {@link Base64.OutputStream} will write data to another
+     * <tt>java.io.OutputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream
+    {
+        private boolean encode;
+        private int     position;
+        private byte[]  buffer;
+        private int     bufferLength;
+        private int     lineLength;
+        private boolean breakLines;
+        private byte[]  b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+		private int options; // Record for later
+		private byte[]  alphabet;	    // Local copies to avoid extra method calls
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out )
+        {
+            this( out, ENCODE );
+        }   // end constructor
+
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out, int options )
+        {
+            super( out );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = 0;
+            this.lineLength   = 0;
+            this.suspendEncoding = false;
+            this.b4           = new byte[4];
+			this.options      = options;
+			this.alphabet     = getAlphabet(options);
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+
+
+        /**
+         * Writes the byte to the output stream after
+         * converting to/from Base64 notation.
+         * When encoding, bytes are buffered three
+         * at a time before the output stream actually
+         * gets a write() call.
+         * When decoding, bytes are buffered four
+         * at a time.
+         *
+         * @param theByte the byte to write
+         * @since 1.3
+         */
+        public void write(int theByte) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theByte );
+                return;
+            }   // end if: supsended
+
+            // Encode?
+            if( encode )
+            {
+                buffer[ position++ ] = (byte)theByte;
+                if( position >= bufferLength )  // Enough to encode.
+                {
+                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
+
+                    lineLength += 4;
+                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
+                    {
+                        out.write( NEW_LINE );
+                        lineLength = 0;
+                    }   // end if: end of line
+
+                    position = 0;
+                }   // end if: enough to output
+            }   // end if: encoding
+
+            // Else, Decoding
+            else
+            {
+                // Meaningful Base64 character?
+                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+                {
+                    buffer[ position++ ] = (byte)theByte;
+                    if( position >= bufferLength )  // Enough to output.
+                    {
+                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
+                        out.write( b4, 0, len );
+                        //out.write( Base64.decode4to3( buffer ) );
+                        position = 0;
+                    }   // end if: enough to output
+                }   // end if: meaningful base64 character
+                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+                {
+                    throw new java.io.IOException( "Invalid character in Base64 data." );
+                }   // end else: not white space either
+            }   // end else: decoding
+        }   // end write
+
+
+
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var>
+         * bytes are written.
+         *
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @since 1.3
+         */
+        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theBytes, off, len );
+                return;
+            }   // end if: supsended
+
+            for( int i = 0; i < len; i++ )
+            {
+                write( theBytes[ off + i ] );
+            }   // end for: each byte written
+
+        }   // end write
+
+
+
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob]
+         * This pads the buffer without closing the stream.
+         */
+        public void flushBase64() throws java.io.IOException
+        {
+            if( position > 0 )
+            {
+                if( encode )
+                {
+                    out.write( encode3to4( b4, buffer, position, options ) );
+                    position = 0;
+                }   // end if: encoding
+                else
+                {
+                    throw new java.io.IOException( "Base64 input not properly padded." );
+                }   // end else: decoding
+            }   // end if: buffer partially full
+
+        }   // end flush
+
+
+        /**
+         * Flushes and closes (I think, in the superclass) the stream.
+         *
+         * @since 1.3
+         */
+        public void close() throws java.io.IOException
+        {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+
+            buffer = null;
+            out    = null;
+        }   // end close
+
+
+
+        /**
+         * Suspends encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws java.io.IOException
+        {
+            flushBase64();
+            this.suspendEncoding = true;
+        }   // end suspendEncoding
+
+
+        /**
+         * Resumes encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void resumeEncoding()
+        {
+            this.suspendEncoding = false;
+        }   // end resumeEncoding
+
+
+
+    }   // end inner class OutputStream
+
+
+}   // end class Base64
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/SyncPacketSend.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/SyncPacketSend.java
index 6506cbd..58cf1f8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/SyncPacketSend.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smack/util/SyncPacketSend.java
@@ -1,64 +1,64 @@
-/**

- * 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.util;

-

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.SmackError;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.Packet;

-

-/**

- * Utility class for doing synchronous calls to the server.  Provides several

- * methods for sending a packet to the server and waiting for the reply.

- * 

- * @author Robin Collier

- */

-final public class SyncPacketSend

-{

-	private SyncPacketSend()

-	{	}

-	

-	static public Packet getReply(Connection connection, Packet packet, long timeout)

-		throws XMPPException

-	{

-        PacketFilter responseFilter = new PacketIDFilter(packet.getPacketID());

-        PacketCollector response = connection.createPacketCollector(responseFilter);

-        

-        connection.sendPacket(packet);

-

-        // Wait up to a certain number of seconds for a reply.

-        Packet result = response.nextResult(timeout);

-

-        // Stop queuing results

-        response.cancel();

-

-        if (result == null) {

-            throw new XMPPException(SmackError.NO_RESPONSE_FROM_SERVER);

-        }

-        else if (result.getError() != null) {

-            throw new XMPPException(result.getError());

-        }

-        return result;

-	}

-

-	static public Packet getReply(Connection connection, Packet packet)

-		throws XMPPException

-	{

-		return getReply(connection, packet, SmackConfiguration.getPacketReplyTimeout());

-	}

-}

+/**
+ * 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.util;
+
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.SmackError;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.Packet;
+
+/**
+ * Utility class for doing synchronous calls to the server.  Provides several
+ * methods for sending a packet to the server and waiting for the reply.
+ * 
+ * @author Robin Collier
+ */
+final public class SyncPacketSend
+{
+	private SyncPacketSend()
+	{	}
+	
+	static public Packet getReply(Connection connection, Packet packet, long timeout)
+		throws XMPPException
+	{
+        PacketFilter responseFilter = new PacketIDFilter(packet.getPacketID());
+        PacketCollector response = connection.createPacketCollector(responseFilter);
+        
+        connection.sendPacket(packet);
+
+        // Wait up to a certain number of seconds for a reply.
+        Packet result = response.nextResult(timeout);
+
+        // Stop queuing results
+        response.cancel();
+
+        if (result == null) {
+            throw new XMPPException(SmackError.NO_RESPONSE_FROM_SERVER);
+        }
+        else if (result.getError() != null) {
+            throw new XMPPException(result.getError());
+        }
+        return result;
+	}
+
+	static public Packet getReply(Connection connection, Packet packet)
+		throws XMPPException
+	{
+		return getReply(connection, packet, SmackConfiguration.getPacketReplyTimeout());
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/LastActivityManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/LastActivityManager.java
index a9d1f12..7729bc0 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/LastActivityManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/LastActivityManager.java
@@ -1,230 +1,230 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx;

-

-import org.jivesoftware.smack.*;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.IQTypeFilter;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Message;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.Presence;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.LastActivity;

-

-/**

- * A last activity manager for handling information about the last activity

- * associated with a Jabber ID. A manager handles incoming LastActivity requests

- * of existing Connections. It also allows to request last activity information

- * of other users.

- * <p>

- * 

- * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval

- * of:

- * <ol>

- * <li>How long a particular user has been idle

- * <li>How long a particular user has been logged-out and the message the

- * specified when doing so.

- * <li>How long a host has been up.

- * </ol>

- * <p/>

- * 

- * For example to get the idle time of a user logged in a resource, simple send

- * the LastActivity packet to them, as in the following code:

- * <p>

- * 

- * <pre>

- * Connection con = new XMPPConnection(&quot;jabber.org&quot;);

- * con.login(&quot;john&quot;, &quot;doe&quot;);

- * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org/Smack&quot;);

- * </pre>

- * 

- * To get the lapsed time since the last user logout is the same as above but

- * with out the resource:

- * 

- * <pre>

- * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org&quot;);

- * </pre>

- * 

- * To get the uptime of a host, you simple send the LastActivity packet to it,

- * as in the following code example:

- * <p>

- * 

- * <pre>

- * LastActivity activity = LastActivity.getLastActivity(con, &quot;jabber.org&quot;);

- * </pre>

- * 

- * @author Gabriel Guardincerri

- * @see <a href="http://xmpp.org/extensions/xep-0012.html">XEP-0012: Last

- *      Activity</a>

- */

-

-public class LastActivityManager {

-

-    private long lastMessageSent;

-

-    private Connection connection;

-

-    // Enable the LastActivity support on every established connection

-    static {

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-            public void connectionCreated(Connection connection) {

-                new LastActivityManager(connection);

-            }

-        });

-    }

-

-    /**

-     * Creates a last activity manager to response last activity requests.

-     * 

-     * @param connection

-     *            The Connection that the last activity requests will use.

-     */

-    private LastActivityManager(Connection connection) {

-        this.connection = connection;

-

-        // Listen to all the sent messages to reset the idle time on each one

-        connection.addPacketSendingListener(new PacketListener() {

-            public void processPacket(Packet packet) {

-                Presence presence = (Presence) packet;

-                Presence.Mode mode = presence.getMode();

-                if (mode == null) return;

-                switch (mode) {

-                case available:

-                case chat:

-                    // We assume that only a switch to available and chat indicates user activity

-                    // since other mode changes could be also a result of some sort of automatism

-                    resetIdleTime();

-                }

-            }

-        }, new PacketTypeFilter(Presence.class));

-

-        connection.addPacketListener(new PacketListener() {

-            @Override

-            public void processPacket(Packet packet) {

-                Message message = (Message) packet;

-                // if it's not an error message, reset the idle time

-                if (message.getType() == Message.Type.error) return;

-                resetIdleTime();

-            }

-        }, new PacketTypeFilter(Message.class));

-

-        // Register a listener for a last activity query

-        connection.addPacketListener(new PacketListener() {

-

-            public void processPacket(Packet packet) {

-                LastActivity message = new LastActivity();

-                message.setType(IQ.Type.RESULT);

-                message.setTo(packet.getFrom());

-                message.setFrom(packet.getTo());

-                message.setPacketID(packet.getPacketID());

-                message.setLastActivity(getIdleTime());

-

-                LastActivityManager.this.connection.sendPacket(message);

-            }

-

-        }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class)));

-        ServiceDiscoveryManager.getInstanceFor(connection).addFeature(LastActivity.NAMESPACE);

-        resetIdleTime();

-    }

-

-    /**

-     * Resets the idle time to 0, this should be invoked when a new message is

-     * sent.

-     */

-    private void resetIdleTime() {

-        long now = System.currentTimeMillis();

-        synchronized (this) {

-            lastMessageSent = now;

-        }

-    }

-

-    /**

-     * The idle time is the lapsed time between the last message sent and now.

-     * 

-     * @return the lapsed time between the last message sent and now.

-     */

-    private long getIdleTime() {

-        long lms;

-        long now = System.currentTimeMillis();

-        synchronized (this) {

-            lms = lastMessageSent;

-        }

-        return ((now - lms) / 1000);

-    }

-

-    /**

-     * Returns the last activity of a particular jid. If the jid is a full JID

-     * (i.e., a JID of the form of 'user@host/resource') then the last activity

-     * is the idle time of that connected resource. On the other hand, when the

-     * jid is a bare JID (e.g. 'user@host') then the last activity is the lapsed

-     * time since the last logout or 0 if the user is currently logged in.

-     * Moreover, when the jid is a server or component (e.g., a JID of the form

-     * 'host') the last activity is the uptime.

-     * 

-     * @param con

-     *            the current Connection.

-     * @param jid

-     *            the JID of the user.

-     * @return the LastActivity packet of the jid.

-     * @throws XMPPException

-     *             thrown if a server error has occured.

-     */

-    public static LastActivity getLastActivity(Connection con, String jid) throws XMPPException {

-        LastActivity activity = new LastActivity();

-        activity.setTo(jid);

-

-        PacketCollector collector = con.createPacketCollector(new PacketIDFilter(activity.getPacketID()));

-        con.sendPacket(activity);

-

-        LastActivity response = (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Returns true if Last Activity (XEP-0012) is supported by a given JID

-     * 

-     * @param connection the connection to be used

-     * @param jid a JID to be tested for Last Activity support

-     * @return true if Last Activity is supported, otherwise false

-     */

-    public static boolean isLastActivitySupported(Connection connection, String jid) {

-        try {

-            DiscoverInfo result =

-                ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid);

-            return result.containsFeature(LastActivity.NAMESPACE);

-        }

-        catch (XMPPException e) {

-            return false;

-        }

-    }

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.IQTypeFilter;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.LastActivity;
+
+/**
+ * A last activity manager for handling information about the last activity
+ * associated with a Jabber ID. A manager handles incoming LastActivity requests
+ * of existing Connections. It also allows to request last activity information
+ * of other users.
+ * <p>
+ * 
+ * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval
+ * of:
+ * <ol>
+ * <li>How long a particular user has been idle
+ * <li>How long a particular user has been logged-out and the message the
+ * specified when doing so.
+ * <li>How long a host has been up.
+ * </ol>
+ * <p/>
+ * 
+ * For example to get the idle time of a user logged in a resource, simple send
+ * the LastActivity packet to them, as in the following code:
+ * <p>
+ * 
+ * <pre>
+ * Connection con = new XMPPConnection(&quot;jabber.org&quot;);
+ * con.login(&quot;john&quot;, &quot;doe&quot;);
+ * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org/Smack&quot;);
+ * </pre>
+ * 
+ * To get the lapsed time since the last user logout is the same as above but
+ * with out the resource:
+ * 
+ * <pre>
+ * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org&quot;);
+ * </pre>
+ * 
+ * To get the uptime of a host, you simple send the LastActivity packet to it,
+ * as in the following code example:
+ * <p>
+ * 
+ * <pre>
+ * LastActivity activity = LastActivity.getLastActivity(con, &quot;jabber.org&quot;);
+ * </pre>
+ * 
+ * @author Gabriel Guardincerri
+ * @see <a href="http://xmpp.org/extensions/xep-0012.html">XEP-0012: Last
+ *      Activity</a>
+ */
+
+public class LastActivityManager {
+
+    private long lastMessageSent;
+
+    private Connection connection;
+
+    // Enable the LastActivity support on every established connection
+    static {
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+            public void connectionCreated(Connection connection) {
+                new LastActivityManager(connection);
+            }
+        });
+    }
+
+    /**
+     * Creates a last activity manager to response last activity requests.
+     * 
+     * @param connection
+     *            The Connection that the last activity requests will use.
+     */
+    private LastActivityManager(Connection connection) {
+        this.connection = connection;
+
+        // Listen to all the sent messages to reset the idle time on each one
+        connection.addPacketSendingListener(new PacketListener() {
+            public void processPacket(Packet packet) {
+                Presence presence = (Presence) packet;
+                Presence.Mode mode = presence.getMode();
+                if (mode == null) return;
+                switch (mode) {
+                case available:
+                case chat:
+                    // We assume that only a switch to available and chat indicates user activity
+                    // since other mode changes could be also a result of some sort of automatism
+                    resetIdleTime();
+                }
+            }
+        }, new PacketTypeFilter(Presence.class));
+
+        connection.addPacketListener(new PacketListener() {
+            @Override
+            public void processPacket(Packet packet) {
+                Message message = (Message) packet;
+                // if it's not an error message, reset the idle time
+                if (message.getType() == Message.Type.error) return;
+                resetIdleTime();
+            }
+        }, new PacketTypeFilter(Message.class));
+
+        // Register a listener for a last activity query
+        connection.addPacketListener(new PacketListener() {
+
+            public void processPacket(Packet packet) {
+                LastActivity message = new LastActivity();
+                message.setType(IQ.Type.RESULT);
+                message.setTo(packet.getFrom());
+                message.setFrom(packet.getTo());
+                message.setPacketID(packet.getPacketID());
+                message.setLastActivity(getIdleTime());
+
+                LastActivityManager.this.connection.sendPacket(message);
+            }
+
+        }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class)));
+        ServiceDiscoveryManager.getInstanceFor(connection).addFeature(LastActivity.NAMESPACE);
+        resetIdleTime();
+    }
+
+    /**
+     * Resets the idle time to 0, this should be invoked when a new message is
+     * sent.
+     */
+    private void resetIdleTime() {
+        long now = System.currentTimeMillis();
+        synchronized (this) {
+            lastMessageSent = now;
+        }
+    }
+
+    /**
+     * The idle time is the lapsed time between the last message sent and now.
+     * 
+     * @return the lapsed time between the last message sent and now.
+     */
+    private long getIdleTime() {
+        long lms;
+        long now = System.currentTimeMillis();
+        synchronized (this) {
+            lms = lastMessageSent;
+        }
+        return ((now - lms) / 1000);
+    }
+
+    /**
+     * Returns the last activity of a particular jid. If the jid is a full JID
+     * (i.e., a JID of the form of 'user@host/resource') then the last activity
+     * is the idle time of that connected resource. On the other hand, when the
+     * jid is a bare JID (e.g. 'user@host') then the last activity is the lapsed
+     * time since the last logout or 0 if the user is currently logged in.
+     * Moreover, when the jid is a server or component (e.g., a JID of the form
+     * 'host') the last activity is the uptime.
+     * 
+     * @param con
+     *            the current Connection.
+     * @param jid
+     *            the JID of the user.
+     * @return the LastActivity packet of the jid.
+     * @throws XMPPException
+     *             thrown if a server error has occured.
+     */
+    public static LastActivity getLastActivity(Connection con, String jid) throws XMPPException {
+        LastActivity activity = new LastActivity();
+        activity.setTo(jid);
+
+        PacketCollector collector = con.createPacketCollector(new PacketIDFilter(activity.getPacketID()));
+        con.sendPacket(activity);
+
+        LastActivity response = (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Returns true if Last Activity (XEP-0012) is supported by a given JID
+     * 
+     * @param connection the connection to be used
+     * @param jid a JID to be tested for Last Activity support
+     * @return true if Last Activity is supported, otherwise false
+     */
+    public static boolean isLastActivitySupported(Connection connection, String jid) {
+        try {
+            DiscoverInfo result =
+                ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid);
+            return result.containsFeature(LastActivity.NAMESPACE);
+        }
+        catch (XMPPException e) {
+            return false;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/SharedGroupManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/SharedGroupManager.java
index 76cd527..44ab670 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/SharedGroupManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/SharedGroupManager.java
@@ -17,56 +17,56 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smackx.packet.SharedGroupsInfo;

-

-import java.util.List;

-

-/**

- * A SharedGroupManager provides services for discovering the shared groups where a user belongs.<p>

- *

- * Important note: This functionality is not part of the XMPP spec and it will only work

- * with Wildfire.

- *

- * @author Gaston Dombiak

- */

-public class SharedGroupManager {

-

-    /**

-     * Returns the collection that will contain the name of the shared groups where the user

-     * logged in with the specified session belongs.

-     *

-     * @param connection connection to use to get the user's shared groups.

-     * @return collection with the shared groups' name of the logged user.

-     */

-    public static List<String> getSharedGroups(Connection connection) throws XMPPException {

-        // Discover the shared groups of the logged user

-        SharedGroupsInfo info = new SharedGroupsInfo();

-        info.setType(IQ.Type.GET);

-

-        // Create a packet collector to listen for a response.

-        PacketCollector collector =

-            connection.createPacketCollector(new PacketIDFilter(info.getPacketID()));

-

-        connection.sendPacket(info);

-

-        // Wait up to 5 seconds for a result.

-        IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-        // Stop queuing results

-        collector.cancel();

-        if (result == null) {

-            throw new XMPPException("No response from the server.");

-        }

-        if (result.getType() == IQ.Type.ERROR) {

-            throw new XMPPException(result.getError());

-        }

-        return ((SharedGroupsInfo) result).getGroups();

-    }

-}

+package org.jivesoftware.smackx;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.packet.SharedGroupsInfo;
+
+import java.util.List;
+
+/**
+ * A SharedGroupManager provides services for discovering the shared groups where a user belongs.<p>
+ *
+ * Important note: This functionality is not part of the XMPP spec and it will only work
+ * with Wildfire.
+ *
+ * @author Gaston Dombiak
+ */
+public class SharedGroupManager {
+
+    /**
+     * Returns the collection that will contain the name of the shared groups where the user
+     * logged in with the specified session belongs.
+     *
+     * @param connection connection to use to get the user's shared groups.
+     * @return collection with the shared groups' name of the logged user.
+     */
+    public static List<String> getSharedGroups(Connection connection) throws XMPPException {
+        // Discover the shared groups of the logged user
+        SharedGroupsInfo info = new SharedGroupsInfo();
+        info.setType(IQ.Type.GET);
+
+        // Create a packet collector to listen for a response.
+        PacketCollector collector =
+            connection.createPacketCollector(new PacketIDFilter(info.getPacketID()));
+
+        connection.sendPacket(info);
+
+        // Wait up to 5 seconds for a result.
+        IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+        // Stop queuing results
+        collector.cancel();
+        if (result == null) {
+            throw new XMPPException("No response from the server.");
+        }
+        if (result.getType() == IQ.Type.ERROR) {
+            throw new XMPPException(result.getError());
+        }
+        return ((SharedGroupsInfo) result).getGroups();
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamListener.java
index be78255..87ab2f5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamListener.java
@@ -1,47 +1,47 @@
-/**

- * 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.smackx.bytestreams;

-

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;

-

-/**

- * BytestreamListener are notified if a remote user wants to initiate a bytestream. Implement this

- * interface to handle incoming bytestream requests.

- * <p>

- * BytestreamListener can be registered at the {@link Socks5BytestreamManager} or the

- * {@link InBandBytestreamManager}.

- * <p>

- * There are two ways to add this listener. See

- * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and

- * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for further

- * details.

- * <p>

- * {@link Socks5BytestreamListener} or {@link InBandBytestreamListener} provide a more specific

- * interface of the BytestreamListener.

- * 

- * @author Henning Staib

- */

-public interface BytestreamListener {

-

-    /**

-     * This listener is notified if a bytestream request from another user has been received.

-     * 

-     * @param request the incoming bytestream request

-     */

-    public void incomingBytestreamRequest(BytestreamRequest request);

-

-}

+/**
+ * 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.smackx.bytestreams;
+
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
+
+/**
+ * BytestreamListener are notified if a remote user wants to initiate a bytestream. Implement this
+ * interface to handle incoming bytestream requests.
+ * <p>
+ * BytestreamListener can be registered at the {@link Socks5BytestreamManager} or the
+ * {@link InBandBytestreamManager}.
+ * <p>
+ * There are two ways to add this listener. See
+ * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and
+ * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for further
+ * details.
+ * <p>
+ * {@link Socks5BytestreamListener} or {@link InBandBytestreamListener} provide a more specific
+ * interface of the BytestreamListener.
+ * 
+ * @author Henning Staib
+ */
+public interface BytestreamListener {
+
+    /**
+     * This listener is notified if a bytestream request from another user has been received.
+     * 
+     * @param request the incoming bytestream request
+     */
+    public void incomingBytestreamRequest(BytestreamRequest request);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamManager.java
index ca6bbc6..232af2a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamManager.java
@@ -1,114 +1,114 @@
-/**

- * 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.smackx.bytestreams;

-

-import java.io.IOException;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;

-

-/**

- * BytestreamManager provides a generic interface for bytestream managers.

- * <p>

- * There are two implementations of the interface. See {@link Socks5BytestreamManager} and

- * {@link InBandBytestreamManager}.

- * 

- * @author Henning Staib

- */

-public interface BytestreamManager {

-

-    /**

-     * Adds {@link BytestreamListener} that is called for every incoming bytestream request unless

-     * there is a user specific {@link BytestreamListener} registered.

-     * <p>

-     * See {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and

-     * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener)} for further

-     * details.

-     * 

-     * @param listener the listener to register

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener);

-

-    /**

-     * Removes the given listener from the list of listeners for all incoming bytestream requests.

-     * 

-     * @param listener the listener to remove

-     */

-    public void removeIncomingBytestreamListener(BytestreamListener listener);

-

-    /**

-     * Adds {@link BytestreamListener} that is called for every incoming bytestream request unless

-     * there is a user specific {@link BytestreamListener} registered.

-     * <p>

-     * Use this method if you are awaiting an incoming bytestream request from a specific user.

-     * <p>

-     * See {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)}

-     * and {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)}

-     * for further details.

-     * 

-     * @param listener the listener to register

-     * @param initiatorJID the JID of the user that wants to establish a bytestream

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID);

-

-    /**

-     * Removes the listener for the given user.

-     * 

-     * @param initiatorJID the JID of the user the listener should be removed

-     */

-    public void removeIncomingBytestreamListener(String initiatorJID);

-

-    /**

-     * Establishes a bytestream with the given user and returns the session to send/receive data

-     * to/from the user.

-     * <p>

-     * Use this method to establish bytestreams to users accepting all incoming bytestream requests

-     * since this method doesn't provide a way to tell the user something about the data to be sent.

-     * <p>

-     * To establish a bytestream after negotiation the kind of data to be sent (e.g. file transfer)

-     * use {@link #establishSession(String, String)}.

-     * <p>

-     * See {@link Socks5BytestreamManager#establishSession(String)} and

-     * {@link InBandBytestreamManager#establishSession(String)} for further details.

-     * 

-     * @param targetJID the JID of the user a bytestream should be established

-     * @return the session to send/receive data to/from the user

-     * @throws XMPPException if an error occurred while establishing the session

-     * @throws IOException if an IO error occurred while establishing the session

-     * @throws InterruptedException if the thread was interrupted while waiting in a blocking

-     *         operation

-     */

-    public BytestreamSession establishSession(String targetJID) throws XMPPException, IOException,

-                    InterruptedException;

-

-    /**

-     * Establishes a bytestream with the given user and returns the session to send/receive data

-     * to/from the user.

-     * <p>

-     * See {@link Socks5BytestreamManager#establishSession(String)} and

-     * {@link InBandBytestreamManager#establishSession(String)} for further details.

-     * 

-     * @param targetJID the JID of the user a bytestream should be established

-     * @param sessionID the session ID for the bytestream request

-     * @return the session to send/receive data to/from the user

-     * @throws XMPPException if an error occurred while establishing the session

-     * @throws IOException if an IO error occurred while establishing the session

-     * @throws InterruptedException if the thread was interrupted while waiting in a blocking

-     *         operation

-     */

-    public BytestreamSession establishSession(String targetJID, String sessionID)

-                    throws XMPPException, IOException, InterruptedException;

-

-}

+/**
+ * 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.smackx.bytestreams;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
+
+/**
+ * BytestreamManager provides a generic interface for bytestream managers.
+ * <p>
+ * There are two implementations of the interface. See {@link Socks5BytestreamManager} and
+ * {@link InBandBytestreamManager}.
+ * 
+ * @author Henning Staib
+ */
+public interface BytestreamManager {
+
+    /**
+     * Adds {@link BytestreamListener} that is called for every incoming bytestream request unless
+     * there is a user specific {@link BytestreamListener} registered.
+     * <p>
+     * See {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and
+     * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener)} for further
+     * details.
+     * 
+     * @param listener the listener to register
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener);
+
+    /**
+     * Removes the given listener from the list of listeners for all incoming bytestream requests.
+     * 
+     * @param listener the listener to remove
+     */
+    public void removeIncomingBytestreamListener(BytestreamListener listener);
+
+    /**
+     * Adds {@link BytestreamListener} that is called for every incoming bytestream request unless
+     * there is a user specific {@link BytestreamListener} registered.
+     * <p>
+     * Use this method if you are awaiting an incoming bytestream request from a specific user.
+     * <p>
+     * See {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)}
+     * and {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)}
+     * for further details.
+     * 
+     * @param listener the listener to register
+     * @param initiatorJID the JID of the user that wants to establish a bytestream
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID);
+
+    /**
+     * Removes the listener for the given user.
+     * 
+     * @param initiatorJID the JID of the user the listener should be removed
+     */
+    public void removeIncomingBytestreamListener(String initiatorJID);
+
+    /**
+     * Establishes a bytestream with the given user and returns the session to send/receive data
+     * to/from the user.
+     * <p>
+     * Use this method to establish bytestreams to users accepting all incoming bytestream requests
+     * since this method doesn't provide a way to tell the user something about the data to be sent.
+     * <p>
+     * To establish a bytestream after negotiation the kind of data to be sent (e.g. file transfer)
+     * use {@link #establishSession(String, String)}.
+     * <p>
+     * See {@link Socks5BytestreamManager#establishSession(String)} and
+     * {@link InBandBytestreamManager#establishSession(String)} for further details.
+     * 
+     * @param targetJID the JID of the user a bytestream should be established
+     * @return the session to send/receive data to/from the user
+     * @throws XMPPException if an error occurred while establishing the session
+     * @throws IOException if an IO error occurred while establishing the session
+     * @throws InterruptedException if the thread was interrupted while waiting in a blocking
+     *         operation
+     */
+    public BytestreamSession establishSession(String targetJID) throws XMPPException, IOException,
+                    InterruptedException;
+
+    /**
+     * Establishes a bytestream with the given user and returns the session to send/receive data
+     * to/from the user.
+     * <p>
+     * See {@link Socks5BytestreamManager#establishSession(String)} and
+     * {@link InBandBytestreamManager#establishSession(String)} for further details.
+     * 
+     * @param targetJID the JID of the user a bytestream should be established
+     * @param sessionID the session ID for the bytestream request
+     * @return the session to send/receive data to/from the user
+     * @throws XMPPException if an error occurred while establishing the session
+     * @throws IOException if an IO error occurred while establishing the session
+     * @throws InterruptedException if the thread was interrupted while waiting in a blocking
+     *         operation
+     */
+    public BytestreamSession establishSession(String targetJID, String sessionID)
+                    throws XMPPException, IOException, InterruptedException;
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java
index e368bad..e16474b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java
@@ -1,59 +1,59 @@
-/**

- * 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.smackx.bytestreams;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;

-

-/**

- * BytestreamRequest provides an interface to handle incoming bytestream requests.

- * <p>

- * There are two implementations of the interface. See {@link Socks5BytestreamRequest} and

- * {@link InBandBytestreamRequest}.

- * 

- * @author Henning Staib

- */

-public interface BytestreamRequest {

-

-    /**

-     * Returns the sender of the bytestream open request.

-     * 

-     * @return the sender of the bytestream open request

-     */

-    public String getFrom();

-

-    /**

-     * Returns the session ID of the bytestream open request.

-     * 

-     * @return the session ID of the bytestream open request

-     */

-    public String getSessionID();

-

-    /**

-     * Accepts the bytestream open request and returns the session to send/receive data.

-     * 

-     * @return the session to send/receive data

-     * @throws XMPPException if an error occurred while accepting the bytestream request

-     * @throws InterruptedException if the thread was interrupted while waiting in a blocking

-     *         operation

-     */

-    public BytestreamSession accept() throws XMPPException, InterruptedException;

-

-    /**

-     * Rejects the bytestream request by sending a reject error to the initiator.

-     */

-    public void reject();

-

-}

+/**
+ * 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.smackx.bytestreams;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
+
+/**
+ * BytestreamRequest provides an interface to handle incoming bytestream requests.
+ * <p>
+ * There are two implementations of the interface. See {@link Socks5BytestreamRequest} and
+ * {@link InBandBytestreamRequest}.
+ * 
+ * @author Henning Staib
+ */
+public interface BytestreamRequest {
+
+    /**
+     * Returns the sender of the bytestream open request.
+     * 
+     * @return the sender of the bytestream open request
+     */
+    public String getFrom();
+
+    /**
+     * Returns the session ID of the bytestream open request.
+     * 
+     * @return the session ID of the bytestream open request
+     */
+    public String getSessionID();
+
+    /**
+     * Accepts the bytestream open request and returns the session to send/receive data.
+     * 
+     * @return the session to send/receive data
+     * @throws XMPPException if an error occurred while accepting the bytestream request
+     * @throws InterruptedException if the thread was interrupted while waiting in a blocking
+     *         operation
+     */
+    public BytestreamSession accept() throws XMPPException, InterruptedException;
+
+    /**
+     * Rejects the bytestream request by sending a reject error to the initiator.
+     */
+    public void reject();
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamSession.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamSession.java
index 7aafc35..3041d77 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamSession.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/BytestreamSession.java
@@ -1,81 +1,81 @@
-/**

- * 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.smackx.bytestreams;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;

-

-/**

- * BytestreamSession provides an interface for established bytestream sessions.

- * <p>

- * There are two implementations of the interface. See {@link Socks5BytestreamSession} and

- * {@link InBandBytestreamSession}.

- * 

- * @author Henning Staib

- */

-public interface BytestreamSession {

-

-    /**

-     * Returns the InputStream associated with this session to send data.

-     * 

-     * @return the InputStream associated with this session to send data

-     * @throws IOException if an error occurs while retrieving the input stream

-     */

-    public InputStream getInputStream() throws IOException;

-

-    /**

-     * Returns the OutputStream associated with this session to receive data.

-     * 

-     * @return the OutputStream associated with this session to receive data

-     * @throws IOException if an error occurs while retrieving the output stream

-     */

-    public OutputStream getOutputStream() throws IOException;

-

-    /**

-     * Closes the bytestream session.

-     * <p>

-     * Closing the session will also close the input stream and the output stream associated to this

-     * session.

-     * 

-     * @throws IOException if an error occurs while closing the session

-     */

-    public void close() throws IOException;

-

-    /**

-     * Returns the timeout for read operations of the input stream associated with this session. 0

-     * returns implies that the option is disabled (i.e., timeout of infinity). Default is 0.

-     * 

-     * @return the timeout for read operations

-     * @throws IOException if there is an error in the underlying protocol

-     */

-    public int getReadTimeout() throws IOException;

-

-    /**

-     * Sets the specified timeout, in milliseconds. With this option set to a non-zero timeout, a

-     * read() call on the input stream associated with this session will block for only this amount

-     * of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the

-     * session is still valid. The option must be enabled prior to entering the blocking operation

-     * to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite

-     * timeout. Default is 0.

-     * 

-     * @param timeout the specified timeout, in milliseconds

-     * @throws IOException if there is an error in the underlying protocol

-     */

-    public void setReadTimeout(int timeout) throws IOException;

-

-}

+/**
+ * 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.smackx.bytestreams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
+
+/**
+ * BytestreamSession provides an interface for established bytestream sessions.
+ * <p>
+ * There are two implementations of the interface. See {@link Socks5BytestreamSession} and
+ * {@link InBandBytestreamSession}.
+ * 
+ * @author Henning Staib
+ */
+public interface BytestreamSession {
+
+    /**
+     * Returns the InputStream associated with this session to send data.
+     * 
+     * @return the InputStream associated with this session to send data
+     * @throws IOException if an error occurs while retrieving the input stream
+     */
+    public InputStream getInputStream() throws IOException;
+
+    /**
+     * Returns the OutputStream associated with this session to receive data.
+     * 
+     * @return the OutputStream associated with this session to receive data
+     * @throws IOException if an error occurs while retrieving the output stream
+     */
+    public OutputStream getOutputStream() throws IOException;
+
+    /**
+     * Closes the bytestream session.
+     * <p>
+     * Closing the session will also close the input stream and the output stream associated to this
+     * session.
+     * 
+     * @throws IOException if an error occurs while closing the session
+     */
+    public void close() throws IOException;
+
+    /**
+     * Returns the timeout for read operations of the input stream associated with this session. 0
+     * returns implies that the option is disabled (i.e., timeout of infinity). Default is 0.
+     * 
+     * @return the timeout for read operations
+     * @throws IOException if there is an error in the underlying protocol
+     */
+    public int getReadTimeout() throws IOException;
+
+    /**
+     * Sets the specified timeout, in milliseconds. With this option set to a non-zero timeout, a
+     * read() call on the input stream associated with this session will block for only this amount
+     * of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the
+     * session is still valid. The option must be enabled prior to entering the blocking operation
+     * to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite
+     * timeout. Default is 0.
+     * 
+     * @param timeout the specified timeout, in milliseconds
+     * @throws IOException if there is an error in the underlying protocol
+     */
+    public void setReadTimeout(int timeout) throws IOException;
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java
index 7690e95..83ca51c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java
@@ -1,75 +1,75 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.IQTypeFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;

-

-/**

- * CloseListener handles all In-Band Bytestream close requests.

- * <p>

- * If a close request is received it looks if a stored In-Band Bytestream

- * session exists and closes it. If no session with the given session ID exists

- * an &lt;item-not-found/&gt; error is returned to the sender.

- * 

- * @author Henning Staib

- */

-class CloseListener implements PacketListener {

-

-    /* manager containing the listeners and the XMPP connection */

-    private final InBandBytestreamManager manager;

-

-    /* packet filter for all In-Band Bytestream close requests */

-    private final PacketFilter closeFilter = new AndFilter(new PacketTypeFilter(

-                    Close.class), new IQTypeFilter(IQ.Type.SET));

-

-    /**

-     * Constructor.

-     * 

-     * @param manager the In-Band Bytestream manager

-     */

-    protected CloseListener(InBandBytestreamManager manager) {

-        this.manager = manager;

-    }

-

-    public void processPacket(Packet packet) {

-        Close closeRequest = (Close) packet;

-        InBandBytestreamSession ibbSession = this.manager.getSessions().get(

-                        closeRequest.getSessionID());

-        if (ibbSession == null) {

-            this.manager.replyItemNotFoundPacket(closeRequest);

-        }

-        else {

-            ibbSession.closeByPeer(closeRequest);

-            this.manager.getSessions().remove(closeRequest.getSessionID());

-        }

-

-    }

-

-    /**

-     * Returns the packet filter for In-Band Bytestream close requests.

-     * 

-     * @return the packet filter for In-Band Bytestream close requests

-     */

-    protected PacketFilter getFilter() {

-        return this.closeFilter;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.IQTypeFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;
+
+/**
+ * CloseListener handles all In-Band Bytestream close requests.
+ * <p>
+ * If a close request is received it looks if a stored In-Band Bytestream
+ * session exists and closes it. If no session with the given session ID exists
+ * an &lt;item-not-found/&gt; error is returned to the sender.
+ * 
+ * @author Henning Staib
+ */
+class CloseListener implements PacketListener {
+
+    /* manager containing the listeners and the XMPP connection */
+    private final InBandBytestreamManager manager;
+
+    /* packet filter for all In-Band Bytestream close requests */
+    private final PacketFilter closeFilter = new AndFilter(new PacketTypeFilter(
+                    Close.class), new IQTypeFilter(IQ.Type.SET));
+
+    /**
+     * Constructor.
+     * 
+     * @param manager the In-Band Bytestream manager
+     */
+    protected CloseListener(InBandBytestreamManager manager) {
+        this.manager = manager;
+    }
+
+    public void processPacket(Packet packet) {
+        Close closeRequest = (Close) packet;
+        InBandBytestreamSession ibbSession = this.manager.getSessions().get(
+                        closeRequest.getSessionID());
+        if (ibbSession == null) {
+            this.manager.replyItemNotFoundPacket(closeRequest);
+        }
+        else {
+            ibbSession.closeByPeer(closeRequest);
+            this.manager.getSessions().remove(closeRequest.getSessionID());
+        }
+
+    }
+
+    /**
+     * Returns the packet filter for In-Band Bytestream close requests.
+     * 
+     * @return the packet filter for In-Band Bytestream close requests
+     */
+    protected PacketFilter getFilter() {
+        return this.closeFilter;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java
index 166c146..6ef759c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java
@@ -1,73 +1,73 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;

-

-/**

- * DataListener handles all In-Band Bytestream IQ stanzas containing a data

- * packet extension that don't belong to an existing session.

- * <p>

- * If a data packet is received it looks if a stored In-Band Bytestream session

- * exists. If no session with the given session ID exists an

- * &lt;item-not-found/&gt; error is returned to the sender.

- * <p>

- * Data packets belonging to a running In-Band Bytestream session are processed

- * by more specific listeners registered when an {@link InBandBytestreamSession}

- * is created.

- * 

- * @author Henning Staib

- */

-class DataListener implements PacketListener {

-

-    /* manager containing the listeners and the XMPP connection */

-    private final InBandBytestreamManager manager;

-

-    /* packet filter for all In-Band Bytestream data packets */

-    private final PacketFilter dataFilter = new AndFilter(

-                    new PacketTypeFilter(Data.class));

-

-    /**

-     * Constructor.

-     * 

-     * @param manager the In-Band Bytestream manager

-     */

-    public DataListener(InBandBytestreamManager manager) {

-        this.manager = manager;

-    }

-

-    public void processPacket(Packet packet) {

-        Data data = (Data) packet;

-        InBandBytestreamSession ibbSession = this.manager.getSessions().get(

-                        data.getDataPacketExtension().getSessionID());

-        if (ibbSession == null) {

-            this.manager.replyItemNotFoundPacket(data);

-        }

-    }

-

-    /**

-     * Returns the packet filter for In-Band Bytestream data packets.

-     * 

-     * @return the packet filter for In-Band Bytestream data packets

-     */

-    protected PacketFilter getFilter() {

-        return this.dataFilter;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;
+
+/**
+ * DataListener handles all In-Band Bytestream IQ stanzas containing a data
+ * packet extension that don't belong to an existing session.
+ * <p>
+ * If a data packet is received it looks if a stored In-Band Bytestream session
+ * exists. If no session with the given session ID exists an
+ * &lt;item-not-found/&gt; error is returned to the sender.
+ * <p>
+ * Data packets belonging to a running In-Band Bytestream session are processed
+ * by more specific listeners registered when an {@link InBandBytestreamSession}
+ * is created.
+ * 
+ * @author Henning Staib
+ */
+class DataListener implements PacketListener {
+
+    /* manager containing the listeners and the XMPP connection */
+    private final InBandBytestreamManager manager;
+
+    /* packet filter for all In-Band Bytestream data packets */
+    private final PacketFilter dataFilter = new AndFilter(
+                    new PacketTypeFilter(Data.class));
+
+    /**
+     * Constructor.
+     * 
+     * @param manager the In-Band Bytestream manager
+     */
+    public DataListener(InBandBytestreamManager manager) {
+        this.manager = manager;
+    }
+
+    public void processPacket(Packet packet) {
+        Data data = (Data) packet;
+        InBandBytestreamSession ibbSession = this.manager.getSessions().get(
+                        data.getDataPacketExtension().getSessionID());
+        if (ibbSession == null) {
+            this.manager.replyItemNotFoundPacket(data);
+        }
+    }
+
+    /**
+     * Returns the packet filter for In-Band Bytestream data packets.
+     * 
+     * @return the packet filter for In-Band Bytestream data packets
+     */
+    protected PacketFilter getFilter() {
+        return this.dataFilter;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java
index 68791a6..6c84013 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java
@@ -1,46 +1,46 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.BytestreamRequest;

-

-/**

- * InBandBytestreamListener are informed if a remote user wants to initiate an In-Band Bytestream.

- * Implement this interface to handle incoming In-Band Bytestream requests.

- * <p>

- * There are two ways to add this listener. See

- * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and

- * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for

- * further details.

- * 

- * @author Henning Staib

- */

-public abstract class InBandBytestreamListener implements BytestreamListener {

-

-    

-    

-    public void incomingBytestreamRequest(BytestreamRequest request) {

-        incomingBytestreamRequest((InBandBytestreamRequest) request);

-    }

-

-    /**

-     * This listener is notified if an In-Band Bytestream request from another user has been

-     * received.

-     * 

-     * @param request the incoming In-Band Bytestream request

-     */

-    public abstract void incomingBytestreamRequest(InBandBytestreamRequest request);

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
+
+/**
+ * InBandBytestreamListener are informed if a remote user wants to initiate an In-Band Bytestream.
+ * Implement this interface to handle incoming In-Band Bytestream requests.
+ * <p>
+ * There are two ways to add this listener. See
+ * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and
+ * {@link InBandBytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for
+ * further details.
+ * 
+ * @author Henning Staib
+ */
+public abstract class InBandBytestreamListener implements BytestreamListener {
+
+    
+    
+    public void incomingBytestreamRequest(BytestreamRequest request) {
+        incomingBytestreamRequest((InBandBytestreamRequest) request);
+    }
+
+    /**
+     * This listener is notified if an In-Band Bytestream request from another user has been
+     * received.
+     * 
+     * @param request the incoming In-Band Bytestream request
+     */
+    public abstract void incomingBytestreamRequest(InBandBytestreamRequest request);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java
index ef52531..a4f3592 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java
@@ -1,546 +1,546 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.LinkedList;

-import java.util.List;

-import java.util.Map;

-import java.util.Random;

-import java.util.concurrent.ConcurrentHashMap;

-

-import org.jivesoftware.smack.AbstractConnectionListener;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.ConnectionCreationListener;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.SyncPacketSend;

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.BytestreamManager;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-import org.jivesoftware.smackx.filetransfer.FileTransferManager;

-

-/**

- * The InBandBytestreamManager class handles establishing In-Band Bytestreams as specified in the <a

- * href="http://xmpp.org/extensions/xep-0047.html">XEP-0047</a>.

- * <p>

- * The In-Band Bytestreams (IBB) enables two entities to establish a virtual bytestream over which

- * they can exchange Base64-encoded chunks of data over XMPP itself. It is the fall-back mechanism

- * in case the Socks5 bytestream method of transferring data is not available.

- * <p>

- * There are two ways to send data over an In-Band Bytestream. It could either use IQ stanzas to

- * send data packets or message stanzas. If IQ stanzas are used every data packet is acknowledged by

- * the receiver. This is the recommended way to avoid possible rate-limiting penalties. Message

- * stanzas are not acknowledged because most XMPP server implementation don't support stanza

- * flow-control method like <a href="http://xmpp.org/extensions/xep-0079.html">Advanced Message

- * Processing</a>. To set the stanza that should be used invoke {@link #setStanza(StanzaType)}.

- * <p>

- * To establish an In-Band Bytestream invoke the {@link #establishSession(String)} method. This will

- * negotiate an in-band bytestream with the given target JID and return a session.

- * <p>

- * If a session ID for the In-Band Bytestream was already negotiated (e.g. while negotiating a file

- * transfer) invoke {@link #establishSession(String, String)}.

- * <p>

- * To handle incoming In-Band Bytestream requests add an {@link InBandBytestreamListener} to the

- * manager. There are two ways to add this listener. If you want to be informed about incoming

- * In-Band Bytestreams from a specific user add the listener by invoking

- * {@link #addIncomingBytestreamListener(BytestreamListener, String)}. If the listener should

- * respond to all In-Band Bytestream requests invoke

- * {@link #addIncomingBytestreamListener(BytestreamListener)}.

- * <p>

- * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming

- * In-Band bytestream requests sent in the context of <a

- * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

- * {@link FileTransferManager})

- * <p>

- * If no {@link InBandBytestreamListener}s are registered, all incoming In-Band bytestream requests

- * will be rejected by returning a &lt;not-acceptable/&gt; error to the initiator.

- * 

- * @author Henning Staib

- */

-public class InBandBytestreamManager implements BytestreamManager {

-

-    /**

-     * Stanzas that can be used to encapsulate In-Band Bytestream data packets.

-     */

-    public enum StanzaType {

-

-        /**

-         * IQ stanza.

-         */

-        IQ,

-

-        /**

-         * Message stanza.

-         */

-        MESSAGE

-    }

-

-    /*

-     * create a new InBandBytestreamManager and register its shutdown listener on every established

-     * connection

-     */

-    static {

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-            public void connectionCreated(Connection connection) {

-                final InBandBytestreamManager manager;

-                manager = InBandBytestreamManager.getByteStreamManager(connection);

-

-                // register shutdown listener

-                connection.addConnectionListener(new AbstractConnectionListener() {

-

-                    public void connectionClosed() {

-                        manager.disableService();

-                    }

-

-                });

-

-            }

-        });

-    }

-

-    /**

-     * The XMPP namespace of the In-Band Bytestream

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/ibb";

-

-    /**

-     * Maximum block size that is allowed for In-Band Bytestreams

-     */

-    public static final int MAXIMUM_BLOCK_SIZE = 65535;

-

-    /* prefix used to generate session IDs */

-    private static final String SESSION_ID_PREFIX = "jibb_";

-

-    /* random generator to create session IDs */

-    private final static Random randomGenerator = new Random();

-

-    /* stores one InBandBytestreamManager for each XMPP connection */

-    private final static Map<Connection, InBandBytestreamManager> managers = new HashMap<Connection, InBandBytestreamManager>();

-

-    /* XMPP connection */

-    private final Connection connection;

-

-    /*

-     * assigns a user to a listener that is informed if an In-Band Bytestream request for this user

-     * is received

-     */

-    private final Map<String, BytestreamListener> userListeners = new ConcurrentHashMap<String, BytestreamListener>();

-

-    /*

-     * list of listeners that respond to all In-Band Bytestream requests if there are no user

-     * specific listeners for that request

-     */

-    private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());

-

-    /* listener that handles all incoming In-Band Bytestream requests */

-    private final InitiationListener initiationListener;

-

-    /* listener that handles all incoming In-Band Bytestream IQ data packets */

-    private final DataListener dataListener;

-

-    /* listener that handles all incoming In-Band Bytestream close requests */

-    private final CloseListener closeListener;

-

-    /* assigns a session ID to the In-Band Bytestream session */

-    private final Map<String, InBandBytestreamSession> sessions = new ConcurrentHashMap<String, InBandBytestreamSession>();

-

-    /* block size used for new In-Band Bytestreams */

-    private int defaultBlockSize = 4096;

-

-    /* maximum block size allowed for this connection */

-    private int maximumBlockSize = MAXIMUM_BLOCK_SIZE;

-

-    /* the stanza used to send data packets */

-    private StanzaType stanza = StanzaType.IQ;

-

-    /*

-     * list containing session IDs of In-Band Bytestream open packets that should be ignored by the

-     * InitiationListener

-     */

-    private List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());

-

-    /**

-     * Returns the InBandBytestreamManager to handle In-Band Bytestreams for a given

-     * {@link Connection}.

-     * 

-     * @param connection the XMPP connection

-     * @return the InBandBytestreamManager for the given XMPP connection

-     */

-    public static synchronized InBandBytestreamManager getByteStreamManager(Connection connection) {

-        if (connection == null)

-            return null;

-        InBandBytestreamManager manager = managers.get(connection);

-        if (manager == null) {

-            manager = new InBandBytestreamManager(connection);

-            managers.put(connection, manager);

-        }

-        return manager;

-    }

-

-    /**

-     * Constructor.

-     * 

-     * @param connection the XMPP connection

-     */

-    private InBandBytestreamManager(Connection connection) {

-        this.connection = connection;

-

-        // register bytestream open packet listener

-        this.initiationListener = new InitiationListener(this);

-        this.connection.addPacketListener(this.initiationListener,

-                        this.initiationListener.getFilter());

-

-        // register bytestream data packet listener

-        this.dataListener = new DataListener(this);

-        this.connection.addPacketListener(this.dataListener, this.dataListener.getFilter());

-

-        // register bytestream close packet listener

-        this.closeListener = new CloseListener(this);

-        this.connection.addPacketListener(this.closeListener, this.closeListener.getFilter());

-

-    }

-

-    /**

-     * Adds InBandBytestreamListener that is called for every incoming in-band bytestream request

-     * unless there is a user specific InBandBytestreamListener registered.

-     * <p>

-     * If no listeners are registered all In-Band Bytestream request are rejected with a

-     * &lt;not-acceptable/&gt; error.

-     * <p>

-     * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming

-     * Socks5 bytestream requests sent in the context of <a

-     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

-     * {@link FileTransferManager})

-     * 

-     * @param listener the listener to register

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener) {

-        this.allRequestListeners.add(listener);

-    }

-

-    /**

-     * Removes the given listener from the list of listeners for all incoming In-Band Bytestream

-     * requests.

-     * 

-     * @param listener the listener to remove

-     */

-    public void removeIncomingBytestreamListener(BytestreamListener listener) {

-        this.allRequestListeners.remove(listener);

-    }

-

-    /**

-     * Adds InBandBytestreamListener that is called for every incoming in-band bytestream request

-     * from the given user.

-     * <p>

-     * Use this method if you are awaiting an incoming Socks5 bytestream request from a specific

-     * user.

-     * <p>

-     * If no listeners are registered all In-Band Bytestream request are rejected with a

-     * &lt;not-acceptable/&gt; error.

-     * <p>

-     * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming

-     * Socks5 bytestream requests sent in the context of <a

-     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

-     * {@link FileTransferManager})

-     * 

-     * @param listener the listener to register

-     * @param initiatorJID the JID of the user that wants to establish an In-Band Bytestream

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID) {

-        this.userListeners.put(initiatorJID, listener);

-    }

-

-    /**

-     * Removes the listener for the given user.

-     * 

-     * @param initiatorJID the JID of the user the listener should be removed

-     */

-    public void removeIncomingBytestreamListener(String initiatorJID) {

-        this.userListeners.remove(initiatorJID);

-    }

-

-    /**

-     * Use this method to ignore the next incoming In-Band Bytestream request containing the given

-     * session ID. No listeners will be notified for this request and and no error will be returned

-     * to the initiator.

-     * <p>

-     * This method should be used if you are awaiting an In-Band Bytestream request as a reply to

-     * another packet (e.g. file transfer).

-     * 

-     * @param sessionID to be ignored

-     */

-    public void ignoreBytestreamRequestOnce(String sessionID) {

-        this.ignoredBytestreamRequests.add(sessionID);

-    }

-

-    /**

-     * Returns the default block size that is used for all outgoing in-band bytestreams for this

-     * connection.

-     * <p>

-     * The recommended default block size is 4096 bytes. See <a

-     * href="http://xmpp.org/extensions/xep-0047.html#usage">XEP-0047</a> Section 5.

-     * 

-     * @return the default block size

-     */

-    public int getDefaultBlockSize() {

-        return defaultBlockSize;

-    }

-

-    /**

-     * Sets the default block size that is used for all outgoing in-band bytestreams for this

-     * connection.

-     * <p>

-     * The default block size must be between 1 and 65535 bytes. The recommended default block size

-     * is 4096 bytes. See <a href="http://xmpp.org/extensions/xep-0047.html#usage">XEP-0047</a>

-     * Section 5.

-     * 

-     * @param defaultBlockSize the default block size to set

-     */

-    public void setDefaultBlockSize(int defaultBlockSize) {

-        if (defaultBlockSize <= 0 || defaultBlockSize > MAXIMUM_BLOCK_SIZE) {

-            throw new IllegalArgumentException("Default block size must be between 1 and "

-                            + MAXIMUM_BLOCK_SIZE);

-        }

-        this.defaultBlockSize = defaultBlockSize;

-    }

-

-    /**

-     * Returns the maximum block size that is allowed for In-Band Bytestreams for this connection.

-     * <p>

-     * Incoming In-Band Bytestream open request will be rejected with an

-     * &lt;resource-constraint/&gt; error if the block size is greater then the maximum allowed

-     * block size.

-     * <p>

-     * The default maximum block size is 65535 bytes.

-     * 

-     * @return the maximum block size

-     */

-    public int getMaximumBlockSize() {

-        return maximumBlockSize;

-    }

-

-    /**

-     * Sets the maximum block size that is allowed for In-Band Bytestreams for this connection.

-     * <p>

-     * The maximum block size must be between 1 and 65535 bytes.

-     * <p>

-     * Incoming In-Band Bytestream open request will be rejected with an

-     * &lt;resource-constraint/&gt; error if the block size is greater then the maximum allowed

-     * block size.

-     * 

-     * @param maximumBlockSize the maximum block size to set

-     */

-    public void setMaximumBlockSize(int maximumBlockSize) {

-        if (maximumBlockSize <= 0 || maximumBlockSize > MAXIMUM_BLOCK_SIZE) {

-            throw new IllegalArgumentException("Maximum block size must be between 1 and "

-                            + MAXIMUM_BLOCK_SIZE);

-        }

-        this.maximumBlockSize = maximumBlockSize;

-    }

-

-    /**

-     * Returns the stanza used to send data packets.

-     * <p>

-     * Default is {@link StanzaType#IQ}. See <a

-     * href="http://xmpp.org/extensions/xep-0047.html#message">XEP-0047</a> Section 4.

-     * 

-     * @return the stanza used to send data packets

-     */

-    public StanzaType getStanza() {

-        return stanza;

-    }

-

-    /**

-     * Sets the stanza used to send data packets.

-     * <p>

-     * The use of {@link StanzaType#IQ} is recommended. See <a

-     * href="http://xmpp.org/extensions/xep-0047.html#message">XEP-0047</a> Section 4.

-     * 

-     * @param stanza the stanza to set

-     */

-    public void setStanza(StanzaType stanza) {

-        this.stanza = stanza;

-    }

-

-    /**

-     * Establishes an In-Band Bytestream with the given user and returns the session to send/receive

-     * data to/from the user.

-     * <p>

-     * Use this method to establish In-Band Bytestreams to users accepting all incoming In-Band

-     * Bytestream requests since this method doesn't provide a way to tell the user something about

-     * the data to be sent.

-     * <p>

-     * To establish an In-Band Bytestream after negotiation the kind of data to be sent (e.g. file

-     * transfer) use {@link #establishSession(String, String)}.

-     * 

-     * @param targetJID the JID of the user an In-Band Bytestream should be established

-     * @return the session to send/receive data to/from the user

-     * @throws XMPPException if the user doesn't support or accept in-band bytestreams, or if the

-     *         user prefers smaller block sizes

-     */

-    public InBandBytestreamSession establishSession(String targetJID) throws XMPPException {

-        String sessionID = getNextSessionID();

-        return establishSession(targetJID, sessionID);

-    }

-

-    /**

-     * Establishes an In-Band Bytestream with the given user using the given session ID and returns

-     * the session to send/receive data to/from the user.

-     * 

-     * @param targetJID the JID of the user an In-Band Bytestream should be established

-     * @param sessionID the session ID for the In-Band Bytestream request

-     * @return the session to send/receive data to/from the user

-     * @throws XMPPException if the user doesn't support or accept in-band bytestreams, or if the

-     *         user prefers smaller block sizes

-     */

-    public InBandBytestreamSession establishSession(String targetJID, String sessionID)

-                    throws XMPPException {

-        Open byteStreamRequest = new Open(sessionID, this.defaultBlockSize, this.stanza);

-        byteStreamRequest.setTo(targetJID);

-

-        // sending packet will throw exception on timeout or error reply

-        SyncPacketSend.getReply(this.connection, byteStreamRequest);

-

-        InBandBytestreamSession inBandBytestreamSession = new InBandBytestreamSession(

-                        this.connection, byteStreamRequest, targetJID);

-        this.sessions.put(sessionID, inBandBytestreamSession);

-

-        return inBandBytestreamSession;

-    }

-

-    /**

-     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream is

-     * not accepted.

-     * 

-     * @param request IQ packet that should be answered with a not-acceptable error

-     */

-    protected void replyRejectPacket(IQ request) {

-        XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable);

-        IQ error = IQ.createErrorResponse(request, xmppError);

-        this.connection.sendPacket(error);

-    }

-

-    /**

-     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream open

-     * request is rejected because its block size is greater than the maximum allowed block size.

-     * 

-     * @param request IQ packet that should be answered with a resource-constraint error

-     */

-    protected void replyResourceConstraintPacket(IQ request) {

-        XMPPError xmppError = new XMPPError(XMPPError.Condition.resource_constraint);

-        IQ error = IQ.createErrorResponse(request, xmppError);

-        this.connection.sendPacket(error);

-    }

-

-    /**

-     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream

-     * session could not be found.

-     * 

-     * @param request IQ packet that should be answered with a item-not-found error

-     */

-    protected void replyItemNotFoundPacket(IQ request) {

-        XMPPError xmppError = new XMPPError(XMPPError.Condition.item_not_found);

-        IQ error = IQ.createErrorResponse(request, xmppError);

-        this.connection.sendPacket(error);

-    }

-

-    /**

-     * Returns a new unique session ID.

-     * 

-     * @return a new unique session ID

-     */

-    private String getNextSessionID() {

-        StringBuilder buffer = new StringBuilder();

-        buffer.append(SESSION_ID_PREFIX);

-        buffer.append(Math.abs(randomGenerator.nextLong()));

-        return buffer.toString();

-    }

-

-    /**

-     * Returns the XMPP connection.

-     * 

-     * @return the XMPP connection

-     */

-    protected Connection getConnection() {

-        return this.connection;

-    }

-

-    /**

-     * Returns the {@link InBandBytestreamListener} that should be informed if a In-Band Bytestream

-     * request from the given initiator JID is received.

-     * 

-     * @param initiator the initiator's JID

-     * @return the listener

-     */

-    protected BytestreamListener getUserListener(String initiator) {

-        return this.userListeners.get(initiator);

-    }

-

-    /**

-     * Returns a list of {@link InBandBytestreamListener} that are informed if there are no

-     * listeners for a specific initiator.

-     * 

-     * @return list of listeners

-     */

-    protected List<BytestreamListener> getAllRequestListeners() {

-        return this.allRequestListeners;

-    }

-

-    /**

-     * Returns the sessions map.

-     * 

-     * @return the sessions map

-     */

-    protected Map<String, InBandBytestreamSession> getSessions() {

-        return sessions;

-    }

-

-    /**

-     * Returns the list of session IDs that should be ignored by the InitialtionListener

-     * 

-     * @return list of session IDs

-     */

-    protected List<String> getIgnoredBytestreamRequests() {

-        return ignoredBytestreamRequests;

-    }

-

-    /**

-     * Disables the InBandBytestreamManager by removing its packet listeners and resetting its

-     * internal status.

-     */

-    private void disableService() {

-

-        // remove manager from static managers map

-        managers.remove(connection);

-

-        // remove all listeners registered by this manager

-        this.connection.removePacketListener(this.initiationListener);

-        this.connection.removePacketListener(this.dataListener);

-        this.connection.removePacketListener(this.closeListener);

-

-        // shutdown threads

-        this.initiationListener.shutdown();

-

-        // reset internal status

-        this.userListeners.clear();

-        this.allRequestListeners.clear();

-        this.sessions.clear();

-        this.ignoredBytestreamRequests.clear();

-

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jivesoftware.smack.AbstractConnectionListener;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.SyncPacketSend;
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.BytestreamManager;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+import org.jivesoftware.smackx.filetransfer.FileTransferManager;
+
+/**
+ * The InBandBytestreamManager class handles establishing In-Band Bytestreams as specified in the <a
+ * href="http://xmpp.org/extensions/xep-0047.html">XEP-0047</a>.
+ * <p>
+ * The In-Band Bytestreams (IBB) enables two entities to establish a virtual bytestream over which
+ * they can exchange Base64-encoded chunks of data over XMPP itself. It is the fall-back mechanism
+ * in case the Socks5 bytestream method of transferring data is not available.
+ * <p>
+ * There are two ways to send data over an In-Band Bytestream. It could either use IQ stanzas to
+ * send data packets or message stanzas. If IQ stanzas are used every data packet is acknowledged by
+ * the receiver. This is the recommended way to avoid possible rate-limiting penalties. Message
+ * stanzas are not acknowledged because most XMPP server implementation don't support stanza
+ * flow-control method like <a href="http://xmpp.org/extensions/xep-0079.html">Advanced Message
+ * Processing</a>. To set the stanza that should be used invoke {@link #setStanza(StanzaType)}.
+ * <p>
+ * To establish an In-Band Bytestream invoke the {@link #establishSession(String)} method. This will
+ * negotiate an in-band bytestream with the given target JID and return a session.
+ * <p>
+ * If a session ID for the In-Band Bytestream was already negotiated (e.g. while negotiating a file
+ * transfer) invoke {@link #establishSession(String, String)}.
+ * <p>
+ * To handle incoming In-Band Bytestream requests add an {@link InBandBytestreamListener} to the
+ * manager. There are two ways to add this listener. If you want to be informed about incoming
+ * In-Band Bytestreams from a specific user add the listener by invoking
+ * {@link #addIncomingBytestreamListener(BytestreamListener, String)}. If the listener should
+ * respond to all In-Band Bytestream requests invoke
+ * {@link #addIncomingBytestreamListener(BytestreamListener)}.
+ * <p>
+ * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming
+ * In-Band bytestream requests sent in the context of <a
+ * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+ * {@link FileTransferManager})
+ * <p>
+ * If no {@link InBandBytestreamListener}s are registered, all incoming In-Band bytestream requests
+ * will be rejected by returning a &lt;not-acceptable/&gt; error to the initiator.
+ * 
+ * @author Henning Staib
+ */
+public class InBandBytestreamManager implements BytestreamManager {
+
+    /**
+     * Stanzas that can be used to encapsulate In-Band Bytestream data packets.
+     */
+    public enum StanzaType {
+
+        /**
+         * IQ stanza.
+         */
+        IQ,
+
+        /**
+         * Message stanza.
+         */
+        MESSAGE
+    }
+
+    /*
+     * create a new InBandBytestreamManager and register its shutdown listener on every established
+     * connection
+     */
+    static {
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+            public void connectionCreated(Connection connection) {
+                final InBandBytestreamManager manager;
+                manager = InBandBytestreamManager.getByteStreamManager(connection);
+
+                // register shutdown listener
+                connection.addConnectionListener(new AbstractConnectionListener() {
+
+                    public void connectionClosed() {
+                        manager.disableService();
+                    }
+
+                });
+
+            }
+        });
+    }
+
+    /**
+     * The XMPP namespace of the In-Band Bytestream
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/ibb";
+
+    /**
+     * Maximum block size that is allowed for In-Band Bytestreams
+     */
+    public static final int MAXIMUM_BLOCK_SIZE = 65535;
+
+    /* prefix used to generate session IDs */
+    private static final String SESSION_ID_PREFIX = "jibb_";
+
+    /* random generator to create session IDs */
+    private final static Random randomGenerator = new Random();
+
+    /* stores one InBandBytestreamManager for each XMPP connection */
+    private final static Map<Connection, InBandBytestreamManager> managers = new HashMap<Connection, InBandBytestreamManager>();
+
+    /* XMPP connection */
+    private final Connection connection;
+
+    /*
+     * assigns a user to a listener that is informed if an In-Band Bytestream request for this user
+     * is received
+     */
+    private final Map<String, BytestreamListener> userListeners = new ConcurrentHashMap<String, BytestreamListener>();
+
+    /*
+     * list of listeners that respond to all In-Band Bytestream requests if there are no user
+     * specific listeners for that request
+     */
+    private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());
+
+    /* listener that handles all incoming In-Band Bytestream requests */
+    private final InitiationListener initiationListener;
+
+    /* listener that handles all incoming In-Band Bytestream IQ data packets */
+    private final DataListener dataListener;
+
+    /* listener that handles all incoming In-Band Bytestream close requests */
+    private final CloseListener closeListener;
+
+    /* assigns a session ID to the In-Band Bytestream session */
+    private final Map<String, InBandBytestreamSession> sessions = new ConcurrentHashMap<String, InBandBytestreamSession>();
+
+    /* block size used for new In-Band Bytestreams */
+    private int defaultBlockSize = 4096;
+
+    /* maximum block size allowed for this connection */
+    private int maximumBlockSize = MAXIMUM_BLOCK_SIZE;
+
+    /* the stanza used to send data packets */
+    private StanzaType stanza = StanzaType.IQ;
+
+    /*
+     * list containing session IDs of In-Band Bytestream open packets that should be ignored by the
+     * InitiationListener
+     */
+    private List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());
+
+    /**
+     * Returns the InBandBytestreamManager to handle In-Band Bytestreams for a given
+     * {@link Connection}.
+     * 
+     * @param connection the XMPP connection
+     * @return the InBandBytestreamManager for the given XMPP connection
+     */
+    public static synchronized InBandBytestreamManager getByteStreamManager(Connection connection) {
+        if (connection == null)
+            return null;
+        InBandBytestreamManager manager = managers.get(connection);
+        if (manager == null) {
+            manager = new InBandBytestreamManager(connection);
+            managers.put(connection, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param connection the XMPP connection
+     */
+    private InBandBytestreamManager(Connection connection) {
+        this.connection = connection;
+
+        // register bytestream open packet listener
+        this.initiationListener = new InitiationListener(this);
+        this.connection.addPacketListener(this.initiationListener,
+                        this.initiationListener.getFilter());
+
+        // register bytestream data packet listener
+        this.dataListener = new DataListener(this);
+        this.connection.addPacketListener(this.dataListener, this.dataListener.getFilter());
+
+        // register bytestream close packet listener
+        this.closeListener = new CloseListener(this);
+        this.connection.addPacketListener(this.closeListener, this.closeListener.getFilter());
+
+    }
+
+    /**
+     * Adds InBandBytestreamListener that is called for every incoming in-band bytestream request
+     * unless there is a user specific InBandBytestreamListener registered.
+     * <p>
+     * If no listeners are registered all In-Band Bytestream request are rejected with a
+     * &lt;not-acceptable/&gt; error.
+     * <p>
+     * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming
+     * Socks5 bytestream requests sent in the context of <a
+     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+     * {@link FileTransferManager})
+     * 
+     * @param listener the listener to register
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener) {
+        this.allRequestListeners.add(listener);
+    }
+
+    /**
+     * Removes the given listener from the list of listeners for all incoming In-Band Bytestream
+     * requests.
+     * 
+     * @param listener the listener to remove
+     */
+    public void removeIncomingBytestreamListener(BytestreamListener listener) {
+        this.allRequestListeners.remove(listener);
+    }
+
+    /**
+     * Adds InBandBytestreamListener that is called for every incoming in-band bytestream request
+     * from the given user.
+     * <p>
+     * Use this method if you are awaiting an incoming Socks5 bytestream request from a specific
+     * user.
+     * <p>
+     * If no listeners are registered all In-Band Bytestream request are rejected with a
+     * &lt;not-acceptable/&gt; error.
+     * <p>
+     * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming
+     * Socks5 bytestream requests sent in the context of <a
+     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+     * {@link FileTransferManager})
+     * 
+     * @param listener the listener to register
+     * @param initiatorJID the JID of the user that wants to establish an In-Band Bytestream
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID) {
+        this.userListeners.put(initiatorJID, listener);
+    }
+
+    /**
+     * Removes the listener for the given user.
+     * 
+     * @param initiatorJID the JID of the user the listener should be removed
+     */
+    public void removeIncomingBytestreamListener(String initiatorJID) {
+        this.userListeners.remove(initiatorJID);
+    }
+
+    /**
+     * Use this method to ignore the next incoming In-Band Bytestream request containing the given
+     * session ID. No listeners will be notified for this request and and no error will be returned
+     * to the initiator.
+     * <p>
+     * This method should be used if you are awaiting an In-Band Bytestream request as a reply to
+     * another packet (e.g. file transfer).
+     * 
+     * @param sessionID to be ignored
+     */
+    public void ignoreBytestreamRequestOnce(String sessionID) {
+        this.ignoredBytestreamRequests.add(sessionID);
+    }
+
+    /**
+     * Returns the default block size that is used for all outgoing in-band bytestreams for this
+     * connection.
+     * <p>
+     * The recommended default block size is 4096 bytes. See <a
+     * href="http://xmpp.org/extensions/xep-0047.html#usage">XEP-0047</a> Section 5.
+     * 
+     * @return the default block size
+     */
+    public int getDefaultBlockSize() {
+        return defaultBlockSize;
+    }
+
+    /**
+     * Sets the default block size that is used for all outgoing in-band bytestreams for this
+     * connection.
+     * <p>
+     * The default block size must be between 1 and 65535 bytes. The recommended default block size
+     * is 4096 bytes. See <a href="http://xmpp.org/extensions/xep-0047.html#usage">XEP-0047</a>
+     * Section 5.
+     * 
+     * @param defaultBlockSize the default block size to set
+     */
+    public void setDefaultBlockSize(int defaultBlockSize) {
+        if (defaultBlockSize <= 0 || defaultBlockSize > MAXIMUM_BLOCK_SIZE) {
+            throw new IllegalArgumentException("Default block size must be between 1 and "
+                            + MAXIMUM_BLOCK_SIZE);
+        }
+        this.defaultBlockSize = defaultBlockSize;
+    }
+
+    /**
+     * Returns the maximum block size that is allowed for In-Band Bytestreams for this connection.
+     * <p>
+     * Incoming In-Band Bytestream open request will be rejected with an
+     * &lt;resource-constraint/&gt; error if the block size is greater then the maximum allowed
+     * block size.
+     * <p>
+     * The default maximum block size is 65535 bytes.
+     * 
+     * @return the maximum block size
+     */
+    public int getMaximumBlockSize() {
+        return maximumBlockSize;
+    }
+
+    /**
+     * Sets the maximum block size that is allowed for In-Band Bytestreams for this connection.
+     * <p>
+     * The maximum block size must be between 1 and 65535 bytes.
+     * <p>
+     * Incoming In-Band Bytestream open request will be rejected with an
+     * &lt;resource-constraint/&gt; error if the block size is greater then the maximum allowed
+     * block size.
+     * 
+     * @param maximumBlockSize the maximum block size to set
+     */
+    public void setMaximumBlockSize(int maximumBlockSize) {
+        if (maximumBlockSize <= 0 || maximumBlockSize > MAXIMUM_BLOCK_SIZE) {
+            throw new IllegalArgumentException("Maximum block size must be between 1 and "
+                            + MAXIMUM_BLOCK_SIZE);
+        }
+        this.maximumBlockSize = maximumBlockSize;
+    }
+
+    /**
+     * Returns the stanza used to send data packets.
+     * <p>
+     * Default is {@link StanzaType#IQ}. See <a
+     * href="http://xmpp.org/extensions/xep-0047.html#message">XEP-0047</a> Section 4.
+     * 
+     * @return the stanza used to send data packets
+     */
+    public StanzaType getStanza() {
+        return stanza;
+    }
+
+    /**
+     * Sets the stanza used to send data packets.
+     * <p>
+     * The use of {@link StanzaType#IQ} is recommended. See <a
+     * href="http://xmpp.org/extensions/xep-0047.html#message">XEP-0047</a> Section 4.
+     * 
+     * @param stanza the stanza to set
+     */
+    public void setStanza(StanzaType stanza) {
+        this.stanza = stanza;
+    }
+
+    /**
+     * Establishes an In-Band Bytestream with the given user and returns the session to send/receive
+     * data to/from the user.
+     * <p>
+     * Use this method to establish In-Band Bytestreams to users accepting all incoming In-Band
+     * Bytestream requests since this method doesn't provide a way to tell the user something about
+     * the data to be sent.
+     * <p>
+     * To establish an In-Band Bytestream after negotiation the kind of data to be sent (e.g. file
+     * transfer) use {@link #establishSession(String, String)}.
+     * 
+     * @param targetJID the JID of the user an In-Band Bytestream should be established
+     * @return the session to send/receive data to/from the user
+     * @throws XMPPException if the user doesn't support or accept in-band bytestreams, or if the
+     *         user prefers smaller block sizes
+     */
+    public InBandBytestreamSession establishSession(String targetJID) throws XMPPException {
+        String sessionID = getNextSessionID();
+        return establishSession(targetJID, sessionID);
+    }
+
+    /**
+     * Establishes an In-Band Bytestream with the given user using the given session ID and returns
+     * the session to send/receive data to/from the user.
+     * 
+     * @param targetJID the JID of the user an In-Band Bytestream should be established
+     * @param sessionID the session ID for the In-Band Bytestream request
+     * @return the session to send/receive data to/from the user
+     * @throws XMPPException if the user doesn't support or accept in-band bytestreams, or if the
+     *         user prefers smaller block sizes
+     */
+    public InBandBytestreamSession establishSession(String targetJID, String sessionID)
+                    throws XMPPException {
+        Open byteStreamRequest = new Open(sessionID, this.defaultBlockSize, this.stanza);
+        byteStreamRequest.setTo(targetJID);
+
+        // sending packet will throw exception on timeout or error reply
+        SyncPacketSend.getReply(this.connection, byteStreamRequest);
+
+        InBandBytestreamSession inBandBytestreamSession = new InBandBytestreamSession(
+                        this.connection, byteStreamRequest, targetJID);
+        this.sessions.put(sessionID, inBandBytestreamSession);
+
+        return inBandBytestreamSession;
+    }
+
+    /**
+     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream is
+     * not accepted.
+     * 
+     * @param request IQ packet that should be answered with a not-acceptable error
+     */
+    protected void replyRejectPacket(IQ request) {
+        XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable);
+        IQ error = IQ.createErrorResponse(request, xmppError);
+        this.connection.sendPacket(error);
+    }
+
+    /**
+     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream open
+     * request is rejected because its block size is greater than the maximum allowed block size.
+     * 
+     * @param request IQ packet that should be answered with a resource-constraint error
+     */
+    protected void replyResourceConstraintPacket(IQ request) {
+        XMPPError xmppError = new XMPPError(XMPPError.Condition.resource_constraint);
+        IQ error = IQ.createErrorResponse(request, xmppError);
+        this.connection.sendPacket(error);
+    }
+
+    /**
+     * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream
+     * session could not be found.
+     * 
+     * @param request IQ packet that should be answered with a item-not-found error
+     */
+    protected void replyItemNotFoundPacket(IQ request) {
+        XMPPError xmppError = new XMPPError(XMPPError.Condition.item_not_found);
+        IQ error = IQ.createErrorResponse(request, xmppError);
+        this.connection.sendPacket(error);
+    }
+
+    /**
+     * Returns a new unique session ID.
+     * 
+     * @return a new unique session ID
+     */
+    private String getNextSessionID() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(SESSION_ID_PREFIX);
+        buffer.append(Math.abs(randomGenerator.nextLong()));
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the XMPP connection.
+     * 
+     * @return the XMPP connection
+     */
+    protected Connection getConnection() {
+        return this.connection;
+    }
+
+    /**
+     * Returns the {@link InBandBytestreamListener} that should be informed if a In-Band Bytestream
+     * request from the given initiator JID is received.
+     * 
+     * @param initiator the initiator's JID
+     * @return the listener
+     */
+    protected BytestreamListener getUserListener(String initiator) {
+        return this.userListeners.get(initiator);
+    }
+
+    /**
+     * Returns a list of {@link InBandBytestreamListener} that are informed if there are no
+     * listeners for a specific initiator.
+     * 
+     * @return list of listeners
+     */
+    protected List<BytestreamListener> getAllRequestListeners() {
+        return this.allRequestListeners;
+    }
+
+    /**
+     * Returns the sessions map.
+     * 
+     * @return the sessions map
+     */
+    protected Map<String, InBandBytestreamSession> getSessions() {
+        return sessions;
+    }
+
+    /**
+     * Returns the list of session IDs that should be ignored by the InitialtionListener
+     * 
+     * @return list of session IDs
+     */
+    protected List<String> getIgnoredBytestreamRequests() {
+        return ignoredBytestreamRequests;
+    }
+
+    /**
+     * Disables the InBandBytestreamManager by removing its packet listeners and resetting its
+     * internal status.
+     */
+    private void disableService() {
+
+        // remove manager from static managers map
+        managers.remove(connection);
+
+        // remove all listeners registered by this manager
+        this.connection.removePacketListener(this.initiationListener);
+        this.connection.removePacketListener(this.dataListener);
+        this.connection.removePacketListener(this.closeListener);
+
+        // shutdown threads
+        this.initiationListener.shutdown();
+
+        // reset internal status
+        this.userListeners.clear();
+        this.allRequestListeners.clear();
+        this.sessions.clear();
+        this.ignoredBytestreamRequests.clear();
+
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java
index 5bc689a..310b844 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java
@@ -1,92 +1,92 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smackx.bytestreams.BytestreamRequest;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-

-/**

- * InBandBytestreamRequest class handles incoming In-Band Bytestream requests.

- * 

- * @author Henning Staib

- */

-public class InBandBytestreamRequest implements BytestreamRequest {

-

-    /* the bytestream initialization request */

-    private final Open byteStreamRequest;

-

-    /*

-     * In-Band Bytestream manager containing the XMPP connection and helper

-     * methods

-     */

-    private final InBandBytestreamManager manager;

-

-    protected InBandBytestreamRequest(InBandBytestreamManager manager,

-                    Open byteStreamRequest) {

-        this.manager = manager;

-        this.byteStreamRequest = byteStreamRequest;

-    }

-

-    /**

-     * Returns the sender of the In-Band Bytestream open request.

-     * 

-     * @return the sender of the In-Band Bytestream open request

-     */

-    public String getFrom() {

-        return this.byteStreamRequest.getFrom();

-    }

-

-    /**

-     * Returns the session ID of the In-Band Bytestream open request.

-     * 

-     * @return the session ID of the In-Band Bytestream open request

-     */

-    public String getSessionID() {

-        return this.byteStreamRequest.getSessionID();

-    }

-

-    /**

-     * Accepts the In-Band Bytestream open request and returns the session to

-     * send/receive data.

-     * 

-     * @return the session to send/receive data

-     * @throws XMPPException if stream is invalid.

-     */

-    public InBandBytestreamSession accept() throws XMPPException {

-        Connection connection = this.manager.getConnection();

-

-        // create In-Band Bytestream session and store it

-        InBandBytestreamSession ibbSession = new InBandBytestreamSession(connection,

-                        this.byteStreamRequest, this.byteStreamRequest.getFrom());

-        this.manager.getSessions().put(this.byteStreamRequest.getSessionID(), ibbSession);

-

-        // acknowledge request

-        IQ resultIQ = IQ.createResultIQ(this.byteStreamRequest);

-        connection.sendPacket(resultIQ);

-

-        return ibbSession;

-    }

-

-    /**

-     * Rejects the In-Band Bytestream request by sending a reject error to the

-     * initiator.

-     */

-    public void reject() {

-        this.manager.replyRejectPacket(this.byteStreamRequest);

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+
+/**
+ * InBandBytestreamRequest class handles incoming In-Band Bytestream requests.
+ * 
+ * @author Henning Staib
+ */
+public class InBandBytestreamRequest implements BytestreamRequest {
+
+    /* the bytestream initialization request */
+    private final Open byteStreamRequest;
+
+    /*
+     * In-Band Bytestream manager containing the XMPP connection and helper
+     * methods
+     */
+    private final InBandBytestreamManager manager;
+
+    protected InBandBytestreamRequest(InBandBytestreamManager manager,
+                    Open byteStreamRequest) {
+        this.manager = manager;
+        this.byteStreamRequest = byteStreamRequest;
+    }
+
+    /**
+     * Returns the sender of the In-Band Bytestream open request.
+     * 
+     * @return the sender of the In-Band Bytestream open request
+     */
+    public String getFrom() {
+        return this.byteStreamRequest.getFrom();
+    }
+
+    /**
+     * Returns the session ID of the In-Band Bytestream open request.
+     * 
+     * @return the session ID of the In-Band Bytestream open request
+     */
+    public String getSessionID() {
+        return this.byteStreamRequest.getSessionID();
+    }
+
+    /**
+     * Accepts the In-Band Bytestream open request and returns the session to
+     * send/receive data.
+     * 
+     * @return the session to send/receive data
+     * @throws XMPPException if stream is invalid.
+     */
+    public InBandBytestreamSession accept() throws XMPPException {
+        Connection connection = this.manager.getConnection();
+
+        // create In-Band Bytestream session and store it
+        InBandBytestreamSession ibbSession = new InBandBytestreamSession(connection,
+                        this.byteStreamRequest, this.byteStreamRequest.getFrom());
+        this.manager.getSessions().put(this.byteStreamRequest.getSessionID(), ibbSession);
+
+        // acknowledge request
+        IQ resultIQ = IQ.createResultIQ(this.byteStreamRequest);
+        connection.sendPacket(resultIQ);
+
+        return ibbSession;
+    }
+
+    /**
+     * Rejects the In-Band Bytestream request by sending a reject error to the
+     * initiator.
+     */
+    public void reject() {
+        this.manager.replyRejectPacket(this.byteStreamRequest);
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java
index a33682c..6323d87 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java
@@ -1,795 +1,795 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.net.SocketTimeoutException;

-import java.util.concurrent.BlockingQueue;

-import java.util.concurrent.LinkedBlockingQueue;

-import java.util.concurrent.TimeUnit;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Message;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smack.util.SyncPacketSend;

-import org.jivesoftware.smackx.bytestreams.BytestreamSession;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-

-/**

- * InBandBytestreamSession class represents an In-Band Bytestream session.

- * <p>

- * In-band bytestreams are bidirectional and this session encapsulates the streams for both

- * directions.

- * <p>

- * Note that closing the In-Band Bytestream session will close both streams. If both streams are

- * closed individually the session will be closed automatically once the second stream is closed.

- * Use the {@link #setCloseBothStreamsEnabled(boolean)} method if both streams should be closed

- * automatically if one of them is closed.

- * 

- * @author Henning Staib

- */

-public class InBandBytestreamSession implements BytestreamSession {

-

-    /* XMPP connection */

-    private final Connection connection;

-

-    /* the In-Band Bytestream open request for this session */

-    private final Open byteStreamRequest;

-

-    /*

-     * the input stream for this session (either IQIBBInputStream or MessageIBBInputStream)

-     */

-    private IBBInputStream inputStream;

-

-    /*

-     * the output stream for this session (either IQIBBOutputStream or MessageIBBOutputStream)

-     */

-    private IBBOutputStream outputStream;

-

-    /* JID of the remote peer */

-    private String remoteJID;

-

-    /* flag to close both streams if one of them is closed */

-    private boolean closeBothStreamsEnabled = false;

-

-    /* flag to indicate if session is closed */

-    private boolean isClosed = false;

-

-    /**

-     * Constructor.

-     * 

-     * @param connection the XMPP connection

-     * @param byteStreamRequest the In-Band Bytestream open request for this session

-     * @param remoteJID JID of the remote peer

-     */

-    protected InBandBytestreamSession(Connection connection, Open byteStreamRequest,

-                    String remoteJID) {

-        this.connection = connection;

-        this.byteStreamRequest = byteStreamRequest;

-        this.remoteJID = remoteJID;

-

-        // initialize streams dependent to the uses stanza type

-        switch (byteStreamRequest.getStanza()) {

-        case IQ:

-            this.inputStream = new IQIBBInputStream();

-            this.outputStream = new IQIBBOutputStream();

-            break;

-        case MESSAGE:

-            this.inputStream = new MessageIBBInputStream();

-            this.outputStream = new MessageIBBOutputStream();

-            break;

-        }

-

-    }

-

-    public InputStream getInputStream() {

-        return this.inputStream;

-    }

-

-    public OutputStream getOutputStream() {

-        return this.outputStream;

-    }

-

-    public int getReadTimeout() {

-        return this.inputStream.readTimeout;

-    }

-

-    public void setReadTimeout(int timeout) {

-        if (timeout < 0) {

-            throw new IllegalArgumentException("Timeout must be >= 0");

-        }

-        this.inputStream.readTimeout = timeout;

-    }

-

-    /**

-     * Returns whether both streams should be closed automatically if one of the streams is closed.

-     * Default is <code>false</code>.

-     * 

-     * @return <code>true</code> if both streams will be closed if one of the streams is closed,

-     *         <code>false</code> if both streams can be closed independently.

-     */

-    public boolean isCloseBothStreamsEnabled() {

-        return closeBothStreamsEnabled;

-    }

-

-    /**

-     * Sets whether both streams should be closed automatically if one of the streams is closed.

-     * Default is <code>false</code>.

-     * 

-     * @param closeBothStreamsEnabled <code>true</code> if both streams should be closed if one of

-     *        the streams is closed, <code>false</code> if both streams should be closed

-     *        independently

-     */

-    public void setCloseBothStreamsEnabled(boolean closeBothStreamsEnabled) {

-        this.closeBothStreamsEnabled = closeBothStreamsEnabled;

-    }

-

-    public void close() throws IOException {

-        closeByLocal(true); // close input stream

-        closeByLocal(false); // close output stream

-    }

-

-    /**

-     * This method is invoked if a request to close the In-Band Bytestream has been received.

-     * 

-     * @param closeRequest the close request from the remote peer

-     */

-    protected void closeByPeer(Close closeRequest) {

-

-        /*

-         * close streams without flushing them, because stream is already considered closed on the

-         * remote peers side

-         */

-        this.inputStream.closeInternal();

-        this.inputStream.cleanup();

-        this.outputStream.closeInternal(false);

-

-        // acknowledge close request

-        IQ confirmClose = IQ.createResultIQ(closeRequest);

-        this.connection.sendPacket(confirmClose);

-

-    }

-

-    /**

-     * This method is invoked if one of the streams has been closed locally, if an error occurred

-     * locally or if the whole session should be closed.

-     * 

-     * @throws IOException if an error occurs while sending the close request

-     */

-    protected synchronized void closeByLocal(boolean in) throws IOException {

-        if (this.isClosed) {

-            return;

-        }

-

-        if (this.closeBothStreamsEnabled) {

-            this.inputStream.closeInternal();

-            this.outputStream.closeInternal(true);

-        }

-        else {

-            if (in) {

-                this.inputStream.closeInternal();

-            }

-            else {

-                // close stream but try to send any data left

-                this.outputStream.closeInternal(true);

-            }

-        }

-

-        if (this.inputStream.isClosed && this.outputStream.isClosed) {

-            this.isClosed = true;

-

-            // send close request

-            Close close = new Close(this.byteStreamRequest.getSessionID());

-            close.setTo(this.remoteJID);

-            try {

-                SyncPacketSend.getReply(this.connection, close);

-            }

-            catch (XMPPException e) {

-                throw new IOException("Error while closing stream: " + e.getMessage());

-            }

-

-            this.inputStream.cleanup();

-

-            // remove session from manager

-            InBandBytestreamManager.getByteStreamManager(this.connection).getSessions().remove(this);

-        }

-

-    }

-

-    /**

-     * IBBInputStream class is the base implementation of an In-Band Bytestream input stream.

-     * Subclasses of this input stream must provide a packet listener along with a packet filter to

-     * collect the In-Band Bytestream data packets.

-     */

-    private abstract class IBBInputStream extends InputStream {

-

-        /* the data packet listener to fill the data queue */

-        private final PacketListener dataPacketListener;

-

-        /* queue containing received In-Band Bytestream data packets */

-        protected final BlockingQueue<DataPacketExtension> dataQueue = new LinkedBlockingQueue<DataPacketExtension>();

-

-        /* buffer containing the data from one data packet */

-        private byte[] buffer;

-

-        /* pointer to the next byte to read from buffer */

-        private int bufferPointer = -1;

-

-        /* data packet sequence (range from 0 to 65535) */

-        private long seq = -1;

-

-        /* flag to indicate if input stream is closed */

-        private boolean isClosed = false;

-

-        /* flag to indicate if close method was invoked */

-        private boolean closeInvoked = false;

-

-        /* timeout for read operations */

-        private int readTimeout = 0;

-

-        /**

-         * Constructor.

-         */

-        public IBBInputStream() {

-            // add data packet listener to connection

-            this.dataPacketListener = getDataPacketListener();

-            connection.addPacketListener(this.dataPacketListener, getDataPacketFilter());

-        }

-

-        /**

-         * Returns the packet listener that processes In-Band Bytestream data packets.

-         * 

-         * @return the data packet listener

-         */

-        protected abstract PacketListener getDataPacketListener();

-

-        /**

-         * Returns the packet filter that accepts In-Band Bytestream data packets.

-         * 

-         * @return the data packet filter

-         */

-        protected abstract PacketFilter getDataPacketFilter();

-

-        public synchronized int read() throws IOException {

-            checkClosed();

-

-            // if nothing read yet or whole buffer has been read fill buffer

-            if (bufferPointer == -1 || bufferPointer >= buffer.length) {

-                // if no data available and stream was closed return -1

-                if (!loadBuffer()) {

-                    return -1;

-                }

-            }

-

-            // return byte and increment buffer pointer

-            return ((int) buffer[bufferPointer++]) & 0xff;

-        }

-

-        public synchronized int read(byte[] b, int off, int len) throws IOException {

-            if (b == null) {

-                throw new NullPointerException();

-            }

-            else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)

-                            || ((off + len) < 0)) {

-                throw new IndexOutOfBoundsException();

-            }

-            else if (len == 0) {

-                return 0;

-            }

-

-            checkClosed();

-

-            // if nothing read yet or whole buffer has been read fill buffer

-            if (bufferPointer == -1 || bufferPointer >= buffer.length) {

-                // if no data available and stream was closed return -1

-                if (!loadBuffer()) {

-                    return -1;

-                }

-            }

-

-            // if more bytes wanted than available return all available

-            int bytesAvailable = buffer.length - bufferPointer;

-            if (len > bytesAvailable) {

-                len = bytesAvailable;

-            }

-

-            System.arraycopy(buffer, bufferPointer, b, off, len);

-            bufferPointer += len;

-            return len;

-        }

-

-        public synchronized int read(byte[] b) throws IOException {

-            return read(b, 0, b.length);

-        }

-

-        /**

-         * This method blocks until a data packet is received, the stream is closed or the current

-         * thread is interrupted.

-         * 

-         * @return <code>true</code> if data was received, otherwise <code>false</code>

-         * @throws IOException if data packets are out of sequence

-         */

-        private synchronized boolean loadBuffer() throws IOException {

-

-            // wait until data is available or stream is closed

-            DataPacketExtension data = null;

-            try {

-                if (this.readTimeout == 0) {

-                    while (data == null) {

-                        if (isClosed && this.dataQueue.isEmpty()) {

-                            return false;

-                        }

-                        data = this.dataQueue.poll(1000, TimeUnit.MILLISECONDS);

-                    }

-                }

-                else {

-                    data = this.dataQueue.poll(this.readTimeout, TimeUnit.MILLISECONDS);

-                    if (data == null) {

-                        throw new SocketTimeoutException();

-                    }

-                }

-            }

-            catch (InterruptedException e) {

-                // Restore the interrupted status

-                Thread.currentThread().interrupt();

-                return false;

-            }

-

-            // handle sequence overflow

-            if (this.seq == 65535) {

-                this.seq = -1;

-            }

-

-            // check if data packets sequence is successor of last seen sequence

-            long seq = data.getSeq();

-            if (seq - 1 != this.seq) {

-                // packets out of order; close stream/session

-                InBandBytestreamSession.this.close();

-                throw new IOException("Packets out of sequence");

-            }

-            else {

-                this.seq = seq;

-            }

-

-            // set buffer to decoded data

-            buffer = data.getDecodedData();

-            bufferPointer = 0;

-            return true;

-        }

-

-        /**

-         * Checks if this stream is closed and throws an IOException if necessary

-         * 

-         * @throws IOException if stream is closed and no data should be read anymore

-         */

-        private void checkClosed() throws IOException {

-            /* throw no exception if there is data available, but not if close method was invoked */

-            if ((isClosed && this.dataQueue.isEmpty()) || closeInvoked) {

-                // clear data queue in case additional data was received after stream was closed

-                this.dataQueue.clear();

-                throw new IOException("Stream is closed");

-            }

-        }

-

-        public boolean markSupported() {

-            return false;

-        }

-

-        public void close() throws IOException {

-            if (isClosed) {

-                return;

-            }

-

-            this.closeInvoked = true;

-

-            InBandBytestreamSession.this.closeByLocal(true);

-        }

-

-        /**

-         * This method sets the close flag and removes the data packet listener.

-         */

-        private void closeInternal() {

-            if (isClosed) {

-                return;

-            }

-            isClosed = true;

-        }

-

-        /**

-         * Invoked if the session is closed.

-         */

-        private void cleanup() {

-            connection.removePacketListener(this.dataPacketListener);

-        }

-

-    }

-

-    /**

-     * IQIBBInputStream class implements IBBInputStream to be used with IQ stanzas encapsulating the

-     * data packets.

-     */

-    private class IQIBBInputStream extends IBBInputStream {

-

-        protected PacketListener getDataPacketListener() {

-            return new PacketListener() {

-

-                private long lastSequence = -1;

-

-                public void processPacket(Packet packet) {

-                    // get data packet extension

-                    DataPacketExtension data = (DataPacketExtension) packet.getExtension(

-                                    DataPacketExtension.ELEMENT_NAME,

-                                    InBandBytestreamManager.NAMESPACE);

-

-                    /*

-                     * check if sequence was not used already (see XEP-0047 Section 2.2)

-                     */

-                    if (data.getSeq() <= this.lastSequence) {

-                        IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, new XMPPError(

-                                        XMPPError.Condition.unexpected_request));

-                        connection.sendPacket(unexpectedRequest);

-                        return;

-

-                    }

-

-                    // check if encoded data is valid (see XEP-0047 Section 2.2)

-                    if (data.getDecodedData() == null) {

-                        // data is invalid; respond with bad-request error

-                        IQ badRequest = IQ.createErrorResponse((IQ) packet, new XMPPError(

-                                        XMPPError.Condition.bad_request));

-                        connection.sendPacket(badRequest);

-                        return;

-                    }

-

-                    // data is valid; add to data queue

-                    dataQueue.offer(data);

-

-                    // confirm IQ

-                    IQ confirmData = IQ.createResultIQ((IQ) packet);

-                    connection.sendPacket(confirmData);

-

-                    // set last seen sequence

-                    this.lastSequence = data.getSeq();

-                    if (this.lastSequence == 65535) {

-                        this.lastSequence = -1;

-                    }

-

-                }

-

-            };

-        }

-

-        protected PacketFilter getDataPacketFilter() {

-            /*

-             * filter all IQ stanzas having type 'SET' (represented by Data class), containing a

-             * data packet extension, matching session ID and recipient

-             */

-            return new AndFilter(new PacketTypeFilter(Data.class), new IBBDataPacketFilter());

-        }

-

-    }

-

-    /**

-     * MessageIBBInputStream class implements IBBInputStream to be used with message stanzas

-     * encapsulating the data packets.

-     */

-    private class MessageIBBInputStream extends IBBInputStream {

-

-        protected PacketListener getDataPacketListener() {

-            return new PacketListener() {

-

-                public void processPacket(Packet packet) {

-                    // get data packet extension

-                    DataPacketExtension data = (DataPacketExtension) packet.getExtension(

-                                    DataPacketExtension.ELEMENT_NAME,

-                                    InBandBytestreamManager.NAMESPACE);

-

-                    // check if encoded data is valid

-                    if (data.getDecodedData() == null) {

-                        /*

-                         * TODO once a majority of XMPP server implementation support XEP-0079

-                         * Advanced Message Processing the invalid message could be answered with an

-                         * appropriate error. For now we just ignore the packet. Subsequent packets

-                         * with an increased sequence will cause the input stream to close the

-                         * stream/session.

-                         */

-                        return;

-                    }

-

-                    // data is valid; add to data queue

-                    dataQueue.offer(data);

-

-                    // TODO confirm packet once XMPP servers support XEP-0079

-                }

-

-            };

-        }

-

-        @Override

-        protected PacketFilter getDataPacketFilter() {

-            /*

-             * filter all message stanzas containing a data packet extension, matching session ID

-             * and recipient

-             */

-            return new AndFilter(new PacketTypeFilter(Message.class), new IBBDataPacketFilter());

-        }

-

-    }

-

-    /**

-     * IBBDataPacketFilter class filters all packets from the remote peer of this session,

-     * containing an In-Band Bytestream data packet extension whose session ID matches this sessions

-     * ID.

-     */

-    private class IBBDataPacketFilter implements PacketFilter {

-

-        public boolean accept(Packet packet) {

-            // sender equals remote peer

-            if (!packet.getFrom().equalsIgnoreCase(remoteJID)) {

-                return false;

-            }

-

-            // stanza contains data packet extension

-            PacketExtension packetExtension = packet.getExtension(DataPacketExtension.ELEMENT_NAME,

-                            InBandBytestreamManager.NAMESPACE);

-            if (packetExtension == null || !(packetExtension instanceof DataPacketExtension)) {

-                return false;

-            }

-

-            // session ID equals this session ID

-            DataPacketExtension data = (DataPacketExtension) packetExtension;

-            if (!data.getSessionID().equals(byteStreamRequest.getSessionID())) {

-                return false;

-            }

-

-            return true;

-        }

-

-    }

-

-    /**

-     * IBBOutputStream class is the base implementation of an In-Band Bytestream output stream.

-     * Subclasses of this output stream must provide a method to send data over XMPP stream.

-     */

-    private abstract class IBBOutputStream extends OutputStream {

-

-        /* buffer with the size of this sessions block size */

-        protected final byte[] buffer;

-

-        /* pointer to next byte to write to buffer */

-        protected int bufferPointer = 0;

-

-        /* data packet sequence (range from 0 to 65535) */

-        protected long seq = 0;

-

-        /* flag to indicate if output stream is closed */

-        protected boolean isClosed = false;

-

-        /**

-         * Constructor.

-         */

-        public IBBOutputStream() {

-            this.buffer = new byte[(byteStreamRequest.getBlockSize()/4)*3];

-        }

-

-        /**

-         * Writes the given data packet to the XMPP stream.

-         * 

-         * @param data the data packet

-         * @throws IOException if an I/O error occurred while sending or if the stream is closed

-         */

-        protected abstract void writeToXML(DataPacketExtension data) throws IOException;

-

-        public synchronized void write(int b) throws IOException {

-            if (this.isClosed) {

-                throw new IOException("Stream is closed");

-            }

-

-            // if buffer is full flush buffer

-            if (bufferPointer >= buffer.length) {

-                flushBuffer();

-            }

-

-            buffer[bufferPointer++] = (byte) b;

-        }

-

-        public synchronized void write(byte b[], int off, int len) throws IOException {

-            if (b == null) {

-                throw new NullPointerException();

-            }

-            else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)

-                            || ((off + len) < 0)) {

-                throw new IndexOutOfBoundsException();

-            }

-            else if (len == 0) {

-                return;

-            }

-

-            if (this.isClosed) {

-                throw new IOException("Stream is closed");

-            }

-

-            // is data to send greater than buffer size

-            if (len >= buffer.length) {

-

-                // "byte" off the first chunk to write out

-                writeOut(b, off, buffer.length);

-

-                // recursively call this method with the lesser amount

-                write(b, off + buffer.length, len - buffer.length);

-            }

-            else {

-                writeOut(b, off, len);

-            }

-        }

-

-        public synchronized void write(byte[] b) throws IOException {

-            write(b, 0, b.length);

-        }

-

-        /**

-         * Fills the buffer with the given data and sends it over the XMPP stream if the buffers

-         * capacity has been reached. This method is only called from this class so it is assured

-         * that the amount of data to send is <= buffer capacity

-         * 

-         * @param b the data

-         * @param off the data

-         * @param len the number of bytes to write

-         * @throws IOException if an I/O error occurred while sending or if the stream is closed

-         */

-        private synchronized void writeOut(byte b[], int off, int len) throws IOException {

-            if (this.isClosed) {

-                throw new IOException("Stream is closed");

-            }

-

-            // set to 0 in case the next 'if' block is not executed

-            int available = 0;

-

-            // is data to send greater that buffer space left

-            if (len > buffer.length - bufferPointer) {

-                // fill buffer to capacity and send it

-                available = buffer.length - bufferPointer;

-                System.arraycopy(b, off, buffer, bufferPointer, available);

-                bufferPointer += available;

-                flushBuffer();

-            }

-

-            // copy the data left to buffer

-            System.arraycopy(b, off + available, buffer, bufferPointer, len - available);

-            bufferPointer += len - available;

-        }

-

-        public synchronized void flush() throws IOException {

-            if (this.isClosed) {

-                throw new IOException("Stream is closed");

-            }

-            flushBuffer();

-        }

-

-        private synchronized void flushBuffer() throws IOException {

-

-            // do nothing if no data to send available

-            if (bufferPointer == 0) {

-                return;

-            }

-

-            // create data packet

-            String enc = StringUtils.encodeBase64(buffer, 0, bufferPointer, false);

-            DataPacketExtension data = new DataPacketExtension(byteStreamRequest.getSessionID(),

-                            this.seq, enc);

-

-            // write to XMPP stream

-            writeToXML(data);

-

-            // reset buffer pointer

-            bufferPointer = 0;

-

-            // increment sequence, considering sequence overflow

-            this.seq = (this.seq + 1 == 65535 ? 0 : this.seq + 1);

-

-        }

-

-        public void close() throws IOException {

-            if (isClosed) {

-                return;

-            }

-            InBandBytestreamSession.this.closeByLocal(false);

-        }

-

-        /**

-         * Sets the close flag and optionally flushes the stream.

-         * 

-         * @param flush if <code>true</code> flushes the stream

-         */

-        protected void closeInternal(boolean flush) {

-            if (this.isClosed) {

-                return;

-            }

-            this.isClosed = true;

-

-            try {

-                if (flush) {

-                    flushBuffer();

-                }

-            }

-            catch (IOException e) {

-                /*

-                 * ignore, because writeToXML() will not throw an exception if stream is already

-                 * closed

-                 */

-            }

-        }

-

-    }

-

-    /**

-     * IQIBBOutputStream class implements IBBOutputStream to be used with IQ stanzas encapsulating

-     * the data packets.

-     */

-    private class IQIBBOutputStream extends IBBOutputStream {

-

-        @Override

-        protected synchronized void writeToXML(DataPacketExtension data) throws IOException {

-            // create IQ stanza containing data packet

-            IQ iq = new Data(data);

-            iq.setTo(remoteJID);

-

-            try {

-                SyncPacketSend.getReply(connection, iq);

-            }

-            catch (XMPPException e) {

-                // close session unless it is already closed

-                if (!this.isClosed) {

-                    InBandBytestreamSession.this.close();

-                    throw new IOException("Error while sending Data: " + e.getMessage());

-                }

-            }

-

-        }

-

-    }

-

-    /**

-     * MessageIBBOutputStream class implements IBBOutputStream to be used with message stanzas

-     * encapsulating the data packets.

-     */

-    private class MessageIBBOutputStream extends IBBOutputStream {

-

-        @Override

-        protected synchronized void writeToXML(DataPacketExtension data) {

-            // create message stanza containing data packet

-            Message message = new Message(remoteJID);

-            message.addExtension(data);

-

-            connection.sendPacket(message);

-

-        }

-

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.SyncPacketSend;
+import org.jivesoftware.smackx.bytestreams.BytestreamSession;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+
+/**
+ * InBandBytestreamSession class represents an In-Band Bytestream session.
+ * <p>
+ * In-band bytestreams are bidirectional and this session encapsulates the streams for both
+ * directions.
+ * <p>
+ * Note that closing the In-Band Bytestream session will close both streams. If both streams are
+ * closed individually the session will be closed automatically once the second stream is closed.
+ * Use the {@link #setCloseBothStreamsEnabled(boolean)} method if both streams should be closed
+ * automatically if one of them is closed.
+ * 
+ * @author Henning Staib
+ */
+public class InBandBytestreamSession implements BytestreamSession {
+
+    /* XMPP connection */
+    private final Connection connection;
+
+    /* the In-Band Bytestream open request for this session */
+    private final Open byteStreamRequest;
+
+    /*
+     * the input stream for this session (either IQIBBInputStream or MessageIBBInputStream)
+     */
+    private IBBInputStream inputStream;
+
+    /*
+     * the output stream for this session (either IQIBBOutputStream or MessageIBBOutputStream)
+     */
+    private IBBOutputStream outputStream;
+
+    /* JID of the remote peer */
+    private String remoteJID;
+
+    /* flag to close both streams if one of them is closed */
+    private boolean closeBothStreamsEnabled = false;
+
+    /* flag to indicate if session is closed */
+    private boolean isClosed = false;
+
+    /**
+     * Constructor.
+     * 
+     * @param connection the XMPP connection
+     * @param byteStreamRequest the In-Band Bytestream open request for this session
+     * @param remoteJID JID of the remote peer
+     */
+    protected InBandBytestreamSession(Connection connection, Open byteStreamRequest,
+                    String remoteJID) {
+        this.connection = connection;
+        this.byteStreamRequest = byteStreamRequest;
+        this.remoteJID = remoteJID;
+
+        // initialize streams dependent to the uses stanza type
+        switch (byteStreamRequest.getStanza()) {
+        case IQ:
+            this.inputStream = new IQIBBInputStream();
+            this.outputStream = new IQIBBOutputStream();
+            break;
+        case MESSAGE:
+            this.inputStream = new MessageIBBInputStream();
+            this.outputStream = new MessageIBBOutputStream();
+            break;
+        }
+
+    }
+
+    public InputStream getInputStream() {
+        return this.inputStream;
+    }
+
+    public OutputStream getOutputStream() {
+        return this.outputStream;
+    }
+
+    public int getReadTimeout() {
+        return this.inputStream.readTimeout;
+    }
+
+    public void setReadTimeout(int timeout) {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Timeout must be >= 0");
+        }
+        this.inputStream.readTimeout = timeout;
+    }
+
+    /**
+     * Returns whether both streams should be closed automatically if one of the streams is closed.
+     * Default is <code>false</code>.
+     * 
+     * @return <code>true</code> if both streams will be closed if one of the streams is closed,
+     *         <code>false</code> if both streams can be closed independently.
+     */
+    public boolean isCloseBothStreamsEnabled() {
+        return closeBothStreamsEnabled;
+    }
+
+    /**
+     * Sets whether both streams should be closed automatically if one of the streams is closed.
+     * Default is <code>false</code>.
+     * 
+     * @param closeBothStreamsEnabled <code>true</code> if both streams should be closed if one of
+     *        the streams is closed, <code>false</code> if both streams should be closed
+     *        independently
+     */
+    public void setCloseBothStreamsEnabled(boolean closeBothStreamsEnabled) {
+        this.closeBothStreamsEnabled = closeBothStreamsEnabled;
+    }
+
+    public void close() throws IOException {
+        closeByLocal(true); // close input stream
+        closeByLocal(false); // close output stream
+    }
+
+    /**
+     * This method is invoked if a request to close the In-Band Bytestream has been received.
+     * 
+     * @param closeRequest the close request from the remote peer
+     */
+    protected void closeByPeer(Close closeRequest) {
+
+        /*
+         * close streams without flushing them, because stream is already considered closed on the
+         * remote peers side
+         */
+        this.inputStream.closeInternal();
+        this.inputStream.cleanup();
+        this.outputStream.closeInternal(false);
+
+        // acknowledge close request
+        IQ confirmClose = IQ.createResultIQ(closeRequest);
+        this.connection.sendPacket(confirmClose);
+
+    }
+
+    /**
+     * This method is invoked if one of the streams has been closed locally, if an error occurred
+     * locally or if the whole session should be closed.
+     * 
+     * @throws IOException if an error occurs while sending the close request
+     */
+    protected synchronized void closeByLocal(boolean in) throws IOException {
+        if (this.isClosed) {
+            return;
+        }
+
+        if (this.closeBothStreamsEnabled) {
+            this.inputStream.closeInternal();
+            this.outputStream.closeInternal(true);
+        }
+        else {
+            if (in) {
+                this.inputStream.closeInternal();
+            }
+            else {
+                // close stream but try to send any data left
+                this.outputStream.closeInternal(true);
+            }
+        }
+
+        if (this.inputStream.isClosed && this.outputStream.isClosed) {
+            this.isClosed = true;
+
+            // send close request
+            Close close = new Close(this.byteStreamRequest.getSessionID());
+            close.setTo(this.remoteJID);
+            try {
+                SyncPacketSend.getReply(this.connection, close);
+            }
+            catch (XMPPException e) {
+                throw new IOException("Error while closing stream: " + e.getMessage());
+            }
+
+            this.inputStream.cleanup();
+
+            // remove session from manager
+            InBandBytestreamManager.getByteStreamManager(this.connection).getSessions().remove(this);
+        }
+
+    }
+
+    /**
+     * IBBInputStream class is the base implementation of an In-Band Bytestream input stream.
+     * Subclasses of this input stream must provide a packet listener along with a packet filter to
+     * collect the In-Band Bytestream data packets.
+     */
+    private abstract class IBBInputStream extends InputStream {
+
+        /* the data packet listener to fill the data queue */
+        private final PacketListener dataPacketListener;
+
+        /* queue containing received In-Band Bytestream data packets */
+        protected final BlockingQueue<DataPacketExtension> dataQueue = new LinkedBlockingQueue<DataPacketExtension>();
+
+        /* buffer containing the data from one data packet */
+        private byte[] buffer;
+
+        /* pointer to the next byte to read from buffer */
+        private int bufferPointer = -1;
+
+        /* data packet sequence (range from 0 to 65535) */
+        private long seq = -1;
+
+        /* flag to indicate if input stream is closed */
+        private boolean isClosed = false;
+
+        /* flag to indicate if close method was invoked */
+        private boolean closeInvoked = false;
+
+        /* timeout for read operations */
+        private int readTimeout = 0;
+
+        /**
+         * Constructor.
+         */
+        public IBBInputStream() {
+            // add data packet listener to connection
+            this.dataPacketListener = getDataPacketListener();
+            connection.addPacketListener(this.dataPacketListener, getDataPacketFilter());
+        }
+
+        /**
+         * Returns the packet listener that processes In-Band Bytestream data packets.
+         * 
+         * @return the data packet listener
+         */
+        protected abstract PacketListener getDataPacketListener();
+
+        /**
+         * Returns the packet filter that accepts In-Band Bytestream data packets.
+         * 
+         * @return the data packet filter
+         */
+        protected abstract PacketFilter getDataPacketFilter();
+
+        public synchronized int read() throws IOException {
+            checkClosed();
+
+            // if nothing read yet or whole buffer has been read fill buffer
+            if (bufferPointer == -1 || bufferPointer >= buffer.length) {
+                // if no data available and stream was closed return -1
+                if (!loadBuffer()) {
+                    return -1;
+                }
+            }
+
+            // return byte and increment buffer pointer
+            return ((int) buffer[bufferPointer++]) & 0xff;
+        }
+
+        public synchronized int read(byte[] b, int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException();
+            }
+            else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)
+                            || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            }
+            else if (len == 0) {
+                return 0;
+            }
+
+            checkClosed();
+
+            // if nothing read yet or whole buffer has been read fill buffer
+            if (bufferPointer == -1 || bufferPointer >= buffer.length) {
+                // if no data available and stream was closed return -1
+                if (!loadBuffer()) {
+                    return -1;
+                }
+            }
+
+            // if more bytes wanted than available return all available
+            int bytesAvailable = buffer.length - bufferPointer;
+            if (len > bytesAvailable) {
+                len = bytesAvailable;
+            }
+
+            System.arraycopy(buffer, bufferPointer, b, off, len);
+            bufferPointer += len;
+            return len;
+        }
+
+        public synchronized int read(byte[] b) throws IOException {
+            return read(b, 0, b.length);
+        }
+
+        /**
+         * This method blocks until a data packet is received, the stream is closed or the current
+         * thread is interrupted.
+         * 
+         * @return <code>true</code> if data was received, otherwise <code>false</code>
+         * @throws IOException if data packets are out of sequence
+         */
+        private synchronized boolean loadBuffer() throws IOException {
+
+            // wait until data is available or stream is closed
+            DataPacketExtension data = null;
+            try {
+                if (this.readTimeout == 0) {
+                    while (data == null) {
+                        if (isClosed && this.dataQueue.isEmpty()) {
+                            return false;
+                        }
+                        data = this.dataQueue.poll(1000, TimeUnit.MILLISECONDS);
+                    }
+                }
+                else {
+                    data = this.dataQueue.poll(this.readTimeout, TimeUnit.MILLISECONDS);
+                    if (data == null) {
+                        throw new SocketTimeoutException();
+                    }
+                }
+            }
+            catch (InterruptedException e) {
+                // Restore the interrupted status
+                Thread.currentThread().interrupt();
+                return false;
+            }
+
+            // handle sequence overflow
+            if (this.seq == 65535) {
+                this.seq = -1;
+            }
+
+            // check if data packets sequence is successor of last seen sequence
+            long seq = data.getSeq();
+            if (seq - 1 != this.seq) {
+                // packets out of order; close stream/session
+                InBandBytestreamSession.this.close();
+                throw new IOException("Packets out of sequence");
+            }
+            else {
+                this.seq = seq;
+            }
+
+            // set buffer to decoded data
+            buffer = data.getDecodedData();
+            bufferPointer = 0;
+            return true;
+        }
+
+        /**
+         * Checks if this stream is closed and throws an IOException if necessary
+         * 
+         * @throws IOException if stream is closed and no data should be read anymore
+         */
+        private void checkClosed() throws IOException {
+            /* throw no exception if there is data available, but not if close method was invoked */
+            if ((isClosed && this.dataQueue.isEmpty()) || closeInvoked) {
+                // clear data queue in case additional data was received after stream was closed
+                this.dataQueue.clear();
+                throw new IOException("Stream is closed");
+            }
+        }
+
+        public boolean markSupported() {
+            return false;
+        }
+
+        public void close() throws IOException {
+            if (isClosed) {
+                return;
+            }
+
+            this.closeInvoked = true;
+
+            InBandBytestreamSession.this.closeByLocal(true);
+        }
+
+        /**
+         * This method sets the close flag and removes the data packet listener.
+         */
+        private void closeInternal() {
+            if (isClosed) {
+                return;
+            }
+            isClosed = true;
+        }
+
+        /**
+         * Invoked if the session is closed.
+         */
+        private void cleanup() {
+            connection.removePacketListener(this.dataPacketListener);
+        }
+
+    }
+
+    /**
+     * IQIBBInputStream class implements IBBInputStream to be used with IQ stanzas encapsulating the
+     * data packets.
+     */
+    private class IQIBBInputStream extends IBBInputStream {
+
+        protected PacketListener getDataPacketListener() {
+            return new PacketListener() {
+
+                private long lastSequence = -1;
+
+                public void processPacket(Packet packet) {
+                    // get data packet extension
+                    DataPacketExtension data = (DataPacketExtension) packet.getExtension(
+                                    DataPacketExtension.ELEMENT_NAME,
+                                    InBandBytestreamManager.NAMESPACE);
+
+                    /*
+                     * check if sequence was not used already (see XEP-0047 Section 2.2)
+                     */
+                    if (data.getSeq() <= this.lastSequence) {
+                        IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, new XMPPError(
+                                        XMPPError.Condition.unexpected_request));
+                        connection.sendPacket(unexpectedRequest);
+                        return;
+
+                    }
+
+                    // check if encoded data is valid (see XEP-0047 Section 2.2)
+                    if (data.getDecodedData() == null) {
+                        // data is invalid; respond with bad-request error
+                        IQ badRequest = IQ.createErrorResponse((IQ) packet, new XMPPError(
+                                        XMPPError.Condition.bad_request));
+                        connection.sendPacket(badRequest);
+                        return;
+                    }
+
+                    // data is valid; add to data queue
+                    dataQueue.offer(data);
+
+                    // confirm IQ
+                    IQ confirmData = IQ.createResultIQ((IQ) packet);
+                    connection.sendPacket(confirmData);
+
+                    // set last seen sequence
+                    this.lastSequence = data.getSeq();
+                    if (this.lastSequence == 65535) {
+                        this.lastSequence = -1;
+                    }
+
+                }
+
+            };
+        }
+
+        protected PacketFilter getDataPacketFilter() {
+            /*
+             * filter all IQ stanzas having type 'SET' (represented by Data class), containing a
+             * data packet extension, matching session ID and recipient
+             */
+            return new AndFilter(new PacketTypeFilter(Data.class), new IBBDataPacketFilter());
+        }
+
+    }
+
+    /**
+     * MessageIBBInputStream class implements IBBInputStream to be used with message stanzas
+     * encapsulating the data packets.
+     */
+    private class MessageIBBInputStream extends IBBInputStream {
+
+        protected PacketListener getDataPacketListener() {
+            return new PacketListener() {
+
+                public void processPacket(Packet packet) {
+                    // get data packet extension
+                    DataPacketExtension data = (DataPacketExtension) packet.getExtension(
+                                    DataPacketExtension.ELEMENT_NAME,
+                                    InBandBytestreamManager.NAMESPACE);
+
+                    // check if encoded data is valid
+                    if (data.getDecodedData() == null) {
+                        /*
+                         * TODO once a majority of XMPP server implementation support XEP-0079
+                         * Advanced Message Processing the invalid message could be answered with an
+                         * appropriate error. For now we just ignore the packet. Subsequent packets
+                         * with an increased sequence will cause the input stream to close the
+                         * stream/session.
+                         */
+                        return;
+                    }
+
+                    // data is valid; add to data queue
+                    dataQueue.offer(data);
+
+                    // TODO confirm packet once XMPP servers support XEP-0079
+                }
+
+            };
+        }
+
+        @Override
+        protected PacketFilter getDataPacketFilter() {
+            /*
+             * filter all message stanzas containing a data packet extension, matching session ID
+             * and recipient
+             */
+            return new AndFilter(new PacketTypeFilter(Message.class), new IBBDataPacketFilter());
+        }
+
+    }
+
+    /**
+     * IBBDataPacketFilter class filters all packets from the remote peer of this session,
+     * containing an In-Band Bytestream data packet extension whose session ID matches this sessions
+     * ID.
+     */
+    private class IBBDataPacketFilter implements PacketFilter {
+
+        public boolean accept(Packet packet) {
+            // sender equals remote peer
+            if (!packet.getFrom().equalsIgnoreCase(remoteJID)) {
+                return false;
+            }
+
+            // stanza contains data packet extension
+            PacketExtension packetExtension = packet.getExtension(DataPacketExtension.ELEMENT_NAME,
+                            InBandBytestreamManager.NAMESPACE);
+            if (packetExtension == null || !(packetExtension instanceof DataPacketExtension)) {
+                return false;
+            }
+
+            // session ID equals this session ID
+            DataPacketExtension data = (DataPacketExtension) packetExtension;
+            if (!data.getSessionID().equals(byteStreamRequest.getSessionID())) {
+                return false;
+            }
+
+            return true;
+        }
+
+    }
+
+    /**
+     * IBBOutputStream class is the base implementation of an In-Band Bytestream output stream.
+     * Subclasses of this output stream must provide a method to send data over XMPP stream.
+     */
+    private abstract class IBBOutputStream extends OutputStream {
+
+        /* buffer with the size of this sessions block size */
+        protected final byte[] buffer;
+
+        /* pointer to next byte to write to buffer */
+        protected int bufferPointer = 0;
+
+        /* data packet sequence (range from 0 to 65535) */
+        protected long seq = 0;
+
+        /* flag to indicate if output stream is closed */
+        protected boolean isClosed = false;
+
+        /**
+         * Constructor.
+         */
+        public IBBOutputStream() {
+            this.buffer = new byte[(byteStreamRequest.getBlockSize()/4)*3];
+        }
+
+        /**
+         * Writes the given data packet to the XMPP stream.
+         * 
+         * @param data the data packet
+         * @throws IOException if an I/O error occurred while sending or if the stream is closed
+         */
+        protected abstract void writeToXML(DataPacketExtension data) throws IOException;
+
+        public synchronized void write(int b) throws IOException {
+            if (this.isClosed) {
+                throw new IOException("Stream is closed");
+            }
+
+            // if buffer is full flush buffer
+            if (bufferPointer >= buffer.length) {
+                flushBuffer();
+            }
+
+            buffer[bufferPointer++] = (byte) b;
+        }
+
+        public synchronized void write(byte b[], int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException();
+            }
+            else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)
+                            || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            }
+            else if (len == 0) {
+                return;
+            }
+
+            if (this.isClosed) {
+                throw new IOException("Stream is closed");
+            }
+
+            // is data to send greater than buffer size
+            if (len >= buffer.length) {
+
+                // "byte" off the first chunk to write out
+                writeOut(b, off, buffer.length);
+
+                // recursively call this method with the lesser amount
+                write(b, off + buffer.length, len - buffer.length);
+            }
+            else {
+                writeOut(b, off, len);
+            }
+        }
+
+        public synchronized void write(byte[] b) throws IOException {
+            write(b, 0, b.length);
+        }
+
+        /**
+         * Fills the buffer with the given data and sends it over the XMPP stream if the buffers
+         * capacity has been reached. This method is only called from this class so it is assured
+         * that the amount of data to send is <= buffer capacity
+         * 
+         * @param b the data
+         * @param off the data
+         * @param len the number of bytes to write
+         * @throws IOException if an I/O error occurred while sending or if the stream is closed
+         */
+        private synchronized void writeOut(byte b[], int off, int len) throws IOException {
+            if (this.isClosed) {
+                throw new IOException("Stream is closed");
+            }
+
+            // set to 0 in case the next 'if' block is not executed
+            int available = 0;
+
+            // is data to send greater that buffer space left
+            if (len > buffer.length - bufferPointer) {
+                // fill buffer to capacity and send it
+                available = buffer.length - bufferPointer;
+                System.arraycopy(b, off, buffer, bufferPointer, available);
+                bufferPointer += available;
+                flushBuffer();
+            }
+
+            // copy the data left to buffer
+            System.arraycopy(b, off + available, buffer, bufferPointer, len - available);
+            bufferPointer += len - available;
+        }
+
+        public synchronized void flush() throws IOException {
+            if (this.isClosed) {
+                throw new IOException("Stream is closed");
+            }
+            flushBuffer();
+        }
+
+        private synchronized void flushBuffer() throws IOException {
+
+            // do nothing if no data to send available
+            if (bufferPointer == 0) {
+                return;
+            }
+
+            // create data packet
+            String enc = StringUtils.encodeBase64(buffer, 0, bufferPointer, false);
+            DataPacketExtension data = new DataPacketExtension(byteStreamRequest.getSessionID(),
+                            this.seq, enc);
+
+            // write to XMPP stream
+            writeToXML(data);
+
+            // reset buffer pointer
+            bufferPointer = 0;
+
+            // increment sequence, considering sequence overflow
+            this.seq = (this.seq + 1 == 65535 ? 0 : this.seq + 1);
+
+        }
+
+        public void close() throws IOException {
+            if (isClosed) {
+                return;
+            }
+            InBandBytestreamSession.this.closeByLocal(false);
+        }
+
+        /**
+         * Sets the close flag and optionally flushes the stream.
+         * 
+         * @param flush if <code>true</code> flushes the stream
+         */
+        protected void closeInternal(boolean flush) {
+            if (this.isClosed) {
+                return;
+            }
+            this.isClosed = true;
+
+            try {
+                if (flush) {
+                    flushBuffer();
+                }
+            }
+            catch (IOException e) {
+                /*
+                 * ignore, because writeToXML() will not throw an exception if stream is already
+                 * closed
+                 */
+            }
+        }
+
+    }
+
+    /**
+     * IQIBBOutputStream class implements IBBOutputStream to be used with IQ stanzas encapsulating
+     * the data packets.
+     */
+    private class IQIBBOutputStream extends IBBOutputStream {
+
+        @Override
+        protected synchronized void writeToXML(DataPacketExtension data) throws IOException {
+            // create IQ stanza containing data packet
+            IQ iq = new Data(data);
+            iq.setTo(remoteJID);
+
+            try {
+                SyncPacketSend.getReply(connection, iq);
+            }
+            catch (XMPPException e) {
+                // close session unless it is already closed
+                if (!this.isClosed) {
+                    InBandBytestreamSession.this.close();
+                    throw new IOException("Error while sending Data: " + e.getMessage());
+                }
+            }
+
+        }
+
+    }
+
+    /**
+     * MessageIBBOutputStream class implements IBBOutputStream to be used with message stanzas
+     * encapsulating the data packets.
+     */
+    private class MessageIBBOutputStream extends IBBOutputStream {
+
+        @Override
+        protected synchronized void writeToXML(DataPacketExtension data) {
+            // create message stanza containing data packet
+            Message message = new Message(remoteJID);
+            message.addExtension(data);
+
+            connection.sendPacket(message);
+
+        }
+
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java
index 0ecb081..be85032 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java
@@ -1,127 +1,127 @@
-/**

- * 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.smackx.bytestreams.ibb;

-

-import java.util.concurrent.ExecutorService;

-import java.util.concurrent.Executors;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.IQTypeFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-

-/**

- * InitiationListener handles all incoming In-Band Bytestream open requests. If there are no

- * listeners for a In-Band Bytestream request InitiationListener will always refuse the request and

- * reply with a &lt;not-acceptable/&gt; error (<a

- * href="http://xmpp.org/extensions/xep-0047.html#example-5" >XEP-0047</a> Section 2.1).

- * <p>

- * All In-Band Bytestream request having a block size greater than the maximum allowed block size

- * for this connection are rejected with an &lt;resource-constraint/&gt; error. The maximum block

- * size can be set by invoking {@link InBandBytestreamManager#setMaximumBlockSize(int)}.

- * 

- * @author Henning Staib

- */

-class InitiationListener implements PacketListener {

-

-    /* manager containing the listeners and the XMPP connection */

-    private final InBandBytestreamManager manager;

-

-    /* packet filter for all In-Band Bytestream requests */

-    private final PacketFilter initFilter = new AndFilter(new PacketTypeFilter(Open.class),

-                    new IQTypeFilter(IQ.Type.SET));

-

-    /* executor service to process incoming requests concurrently */

-    private final ExecutorService initiationListenerExecutor;

-

-    /**

-     * Constructor.

-     * 

-     * @param manager the In-Band Bytestream manager

-     */

-    protected InitiationListener(InBandBytestreamManager manager) {

-        this.manager = manager;

-        initiationListenerExecutor = Executors.newCachedThreadPool();

-    }

-

-    public void processPacket(final Packet packet) {

-        initiationListenerExecutor.execute(new Runnable() {

-

-            public void run() {

-                processRequest(packet);

-            }

-        });

-    }

-

-    private void processRequest(Packet packet) {

-        Open ibbRequest = (Open) packet;

-

-        // validate that block size is within allowed range

-        if (ibbRequest.getBlockSize() > this.manager.getMaximumBlockSize()) {

-            this.manager.replyResourceConstraintPacket(ibbRequest);

-            return;

-        }

-

-        // ignore request if in ignore list

-        if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID()))

-            return;

-

-        // build bytestream request from packet

-        InBandBytestreamRequest request = new InBandBytestreamRequest(this.manager, ibbRequest);

-

-        // notify listeners for bytestream initiation from a specific user

-        BytestreamListener userListener = this.manager.getUserListener(ibbRequest.getFrom());

-        if (userListener != null) {

-            userListener.incomingBytestreamRequest(request);

-

-        }

-        else if (!this.manager.getAllRequestListeners().isEmpty()) {

-            /*

-             * if there is no user specific listener inform listeners for all initiation requests

-             */

-            for (BytestreamListener listener : this.manager.getAllRequestListeners()) {

-                listener.incomingBytestreamRequest(request);

-            }

-

-        }

-        else {

-            /*

-             * if there is no listener for this initiation request, reply with reject message

-             */

-            this.manager.replyRejectPacket(ibbRequest);

-        }

-    }

-

-    /**

-     * Returns the packet filter for In-Band Bytestream open requests.

-     * 

-     * @return the packet filter for In-Band Bytestream open requests

-     */

-    protected PacketFilter getFilter() {

-        return this.initFilter;

-    }

-

-    /**

-     * Shuts down the listeners executor service.

-     */

-    protected void shutdown() {

-        this.initiationListenerExecutor.shutdownNow();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.IQTypeFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+
+/**
+ * InitiationListener handles all incoming In-Band Bytestream open requests. If there are no
+ * listeners for a In-Band Bytestream request InitiationListener will always refuse the request and
+ * reply with a &lt;not-acceptable/&gt; error (<a
+ * href="http://xmpp.org/extensions/xep-0047.html#example-5" >XEP-0047</a> Section 2.1).
+ * <p>
+ * All In-Band Bytestream request having a block size greater than the maximum allowed block size
+ * for this connection are rejected with an &lt;resource-constraint/&gt; error. The maximum block
+ * size can be set by invoking {@link InBandBytestreamManager#setMaximumBlockSize(int)}.
+ * 
+ * @author Henning Staib
+ */
+class InitiationListener implements PacketListener {
+
+    /* manager containing the listeners and the XMPP connection */
+    private final InBandBytestreamManager manager;
+
+    /* packet filter for all In-Band Bytestream requests */
+    private final PacketFilter initFilter = new AndFilter(new PacketTypeFilter(Open.class),
+                    new IQTypeFilter(IQ.Type.SET));
+
+    /* executor service to process incoming requests concurrently */
+    private final ExecutorService initiationListenerExecutor;
+
+    /**
+     * Constructor.
+     * 
+     * @param manager the In-Band Bytestream manager
+     */
+    protected InitiationListener(InBandBytestreamManager manager) {
+        this.manager = manager;
+        initiationListenerExecutor = Executors.newCachedThreadPool();
+    }
+
+    public void processPacket(final Packet packet) {
+        initiationListenerExecutor.execute(new Runnable() {
+
+            public void run() {
+                processRequest(packet);
+            }
+        });
+    }
+
+    private void processRequest(Packet packet) {
+        Open ibbRequest = (Open) packet;
+
+        // validate that block size is within allowed range
+        if (ibbRequest.getBlockSize() > this.manager.getMaximumBlockSize()) {
+            this.manager.replyResourceConstraintPacket(ibbRequest);
+            return;
+        }
+
+        // ignore request if in ignore list
+        if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID()))
+            return;
+
+        // build bytestream request from packet
+        InBandBytestreamRequest request = new InBandBytestreamRequest(this.manager, ibbRequest);
+
+        // notify listeners for bytestream initiation from a specific user
+        BytestreamListener userListener = this.manager.getUserListener(ibbRequest.getFrom());
+        if (userListener != null) {
+            userListener.incomingBytestreamRequest(request);
+
+        }
+        else if (!this.manager.getAllRequestListeners().isEmpty()) {
+            /*
+             * if there is no user specific listener inform listeners for all initiation requests
+             */
+            for (BytestreamListener listener : this.manager.getAllRequestListeners()) {
+                listener.incomingBytestreamRequest(request);
+            }
+
+        }
+        else {
+            /*
+             * if there is no listener for this initiation request, reply with reject message
+             */
+            this.manager.replyRejectPacket(ibbRequest);
+        }
+    }
+
+    /**
+     * Returns the packet filter for In-Band Bytestream open requests.
+     * 
+     * @return the packet filter for In-Band Bytestream open requests
+     */
+    protected PacketFilter getFilter() {
+        return this.initFilter;
+    }
+
+    /**
+     * Shuts down the listeners executor service.
+     */
+    protected void shutdown() {
+        this.initiationListenerExecutor.shutdownNow();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java
index 9a78d73..4ef73dc 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java
@@ -1,65 +1,65 @@
-/**

- * 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.smackx.bytestreams.ibb.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-

-/**

- * Represents a request to close an In-Band Bytestream.

- * 

- * @author Henning Staib

- */

-public class Close extends IQ {

-

-    /* unique session ID identifying this In-Band Bytestream */

-    private final String sessionID;

-

-    /**

-     * Creates a new In-Band Bytestream close request packet.

-     * 

-     * @param sessionID unique session ID identifying this In-Band Bytestream

-     */

-    public Close(String sessionID) {

-        if (sessionID == null || "".equals(sessionID)) {

-            throw new IllegalArgumentException("Session ID must not be null or empty");

-        }

-        this.sessionID = sessionID;

-        setType(Type.SET);

-    }

-

-    /**

-     * Returns the unique session ID identifying this In-Band Bytestream.

-     * 

-     * @return the unique session ID identifying this In-Band Bytestream

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    @Override

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<close ");

-        buf.append("xmlns=\"");

-        buf.append(InBandBytestreamManager.NAMESPACE);

-        buf.append("\" ");

-        buf.append("sid=\"");

-        buf.append(sessionID);

-        buf.append("\"");

-        buf.append("/>");

-        return buf.toString();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+
+/**
+ * Represents a request to close an In-Band Bytestream.
+ * 
+ * @author Henning Staib
+ */
+public class Close extends IQ {
+
+    /* unique session ID identifying this In-Band Bytestream */
+    private final String sessionID;
+
+    /**
+     * Creates a new In-Band Bytestream close request packet.
+     * 
+     * @param sessionID unique session ID identifying this In-Band Bytestream
+     */
+    public Close(String sessionID) {
+        if (sessionID == null || "".equals(sessionID)) {
+            throw new IllegalArgumentException("Session ID must not be null or empty");
+        }
+        this.sessionID = sessionID;
+        setType(Type.SET);
+    }
+
+    /**
+     * Returns the unique session ID identifying this In-Band Bytestream.
+     * 
+     * @return the unique session ID identifying this In-Band Bytestream
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    @Override
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<close ");
+        buf.append("xmlns=\"");
+        buf.append(InBandBytestreamManager.NAMESPACE);
+        buf.append("\" ");
+        buf.append("sid=\"");
+        buf.append(sessionID);
+        buf.append("\"");
+        buf.append("/>");
+        return buf.toString();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java
index 696fa75..d38d7e2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java
@@ -1,64 +1,64 @@
-/**

- * 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.smackx.bytestreams.ibb.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-

-/**

- * Represents a chunk of data sent over an In-Band Bytestream encapsulated in an

- * IQ stanza.

- * 

- * @author Henning Staib

- */

-public class Data extends IQ {

-

-    /* the data packet extension */

-    private final DataPacketExtension dataPacketExtension;

-

-    /**

-     * Constructor.

-     * 

-     * @param data data packet extension containing the encoded data

-     */

-    public Data(DataPacketExtension data) {

-        if (data == null) {

-            throw new IllegalArgumentException("Data must not be null");

-        }

-        this.dataPacketExtension = data;

-

-        /*

-         * also set as packet extension so that data packet extension can be

-         * retrieved from IQ stanza and message stanza in the same way

-         */

-        addExtension(data);

-        setType(IQ.Type.SET);

-    }

-

-    /**

-     * Returns the data packet extension.

-     * <p>

-     * Convenience method for <code>packet.getExtension("data",

-     * "http://jabber.org/protocol/ibb")</code>.

-     * 

-     * @return the data packet extension

-     */

-    public DataPacketExtension getDataPacketExtension() {

-        return this.dataPacketExtension;

-    }

-

-    public String getChildElementXML() {

-        return this.dataPacketExtension.toXML();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+
+/**
+ * Represents a chunk of data sent over an In-Band Bytestream encapsulated in an
+ * IQ stanza.
+ * 
+ * @author Henning Staib
+ */
+public class Data extends IQ {
+
+    /* the data packet extension */
+    private final DataPacketExtension dataPacketExtension;
+
+    /**
+     * Constructor.
+     * 
+     * @param data data packet extension containing the encoded data
+     */
+    public Data(DataPacketExtension data) {
+        if (data == null) {
+            throw new IllegalArgumentException("Data must not be null");
+        }
+        this.dataPacketExtension = data;
+
+        /*
+         * also set as packet extension so that data packet extension can be
+         * retrieved from IQ stanza and message stanza in the same way
+         */
+        addExtension(data);
+        setType(IQ.Type.SET);
+    }
+
+    /**
+     * Returns the data packet extension.
+     * <p>
+     * Convenience method for <code>packet.getExtension("data",
+     * "http://jabber.org/protocol/ibb")</code>.
+     * 
+     * @return the data packet extension
+     */
+    public DataPacketExtension getDataPacketExtension() {
+        return this.dataPacketExtension;
+    }
+
+    public String getChildElementXML() {
+        return this.dataPacketExtension.toXML();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java
index 80ed1e1..dce010f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java
@@ -1,149 +1,149 @@
-/**

- * 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.smackx.bytestreams.ibb.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-

-/**

- * Represents a chunk of data of an In-Band Bytestream within an IQ stanza or a

- * message stanza

- * 

- * @author Henning Staib

- */

-public class DataPacketExtension implements PacketExtension {

-

-    /**

-     * The element name of the data packet extension.

-     */

-    public final static String ELEMENT_NAME = "data";

-

-    /* unique session ID identifying this In-Band Bytestream */

-    private final String sessionID;

-

-    /* sequence of this packet in regard to the other data packets */

-    private final long seq;

-

-    /* the data contained in this packet */

-    private final String data;

-

-    private byte[] decodedData;

-

-    /**

-     * Creates a new In-Band Bytestream data packet.

-     * 

-     * @param sessionID unique session ID identifying this In-Band Bytestream

-     * @param seq sequence of this packet in regard to the other data packets

-     * @param data the base64 encoded data contained in this packet

-     */

-    public DataPacketExtension(String sessionID, long seq, String data) {

-        if (sessionID == null || "".equals(sessionID)) {

-            throw new IllegalArgumentException("Session ID must not be null or empty");

-        }

-        if (seq < 0 || seq > 65535) {

-            throw new IllegalArgumentException("Sequence must not be between 0 and 65535");

-        }

-        if (data == null) {

-            throw new IllegalArgumentException("Data must not be null");

-        }

-        this.sessionID = sessionID;

-        this.seq = seq;

-        this.data = data;

-    }

-

-    /**

-     * Returns the unique session ID identifying this In-Band Bytestream.

-     * 

-     * @return the unique session ID identifying this In-Band Bytestream

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    /**

-     * Returns the sequence of this packet in regard to the other data packets.

-     * 

-     * @return the sequence of this packet in regard to the other data packets.

-     */

-    public long getSeq() {

-        return seq;

-    }

-

-    /**

-     * Returns the data contained in this packet.

-     * 

-     * @return the data contained in this packet.

-     */

-    public String getData() {

-        return data;

-    }

-

-    /**

-     * Returns the decoded data or null if data could not be decoded.

-     * <p>

-     * The encoded data is invalid if it contains bad Base64 input characters or

-     * if it contains the pad ('=') character on a position other than the last

-     * character(s) of the data. See <a

-     * href="http://xmpp.org/extensions/xep-0047.html#sec">XEP-0047</a> Section

-     * 6.

-     * 

-     * @return the decoded data

-     */

-    public byte[] getDecodedData() {

-        // return cached decoded data

-        if (this.decodedData != null) {

-            return this.decodedData;

-        }

-

-        // data must not contain the pad (=) other than end of data

-        if (data.matches(".*={1,2}+.+")) {

-            return null;

-        }

-

-        // decodeBase64 will return null if bad characters are included

-        this.decodedData = StringUtils.decodeBase64(data);

-        return this.decodedData;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return InBandBytestreamManager.NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<");

-        buf.append(getElementName());

-        buf.append(" ");

-        buf.append("xmlns=\"");

-        buf.append(InBandBytestreamManager.NAMESPACE);

-        buf.append("\" ");

-        buf.append("seq=\"");

-        buf.append(seq);

-        buf.append("\" ");

-        buf.append("sid=\"");

-        buf.append(sessionID);

-        buf.append("\">");

-        buf.append(data);

-        buf.append("</");

-        buf.append(getElementName());

-        buf.append(">");

-        return buf.toString();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+
+/**
+ * Represents a chunk of data of an In-Band Bytestream within an IQ stanza or a
+ * message stanza
+ * 
+ * @author Henning Staib
+ */
+public class DataPacketExtension implements PacketExtension {
+
+    /**
+     * The element name of the data packet extension.
+     */
+    public final static String ELEMENT_NAME = "data";
+
+    /* unique session ID identifying this In-Band Bytestream */
+    private final String sessionID;
+
+    /* sequence of this packet in regard to the other data packets */
+    private final long seq;
+
+    /* the data contained in this packet */
+    private final String data;
+
+    private byte[] decodedData;
+
+    /**
+     * Creates a new In-Band Bytestream data packet.
+     * 
+     * @param sessionID unique session ID identifying this In-Band Bytestream
+     * @param seq sequence of this packet in regard to the other data packets
+     * @param data the base64 encoded data contained in this packet
+     */
+    public DataPacketExtension(String sessionID, long seq, String data) {
+        if (sessionID == null || "".equals(sessionID)) {
+            throw new IllegalArgumentException("Session ID must not be null or empty");
+        }
+        if (seq < 0 || seq > 65535) {
+            throw new IllegalArgumentException("Sequence must not be between 0 and 65535");
+        }
+        if (data == null) {
+            throw new IllegalArgumentException("Data must not be null");
+        }
+        this.sessionID = sessionID;
+        this.seq = seq;
+        this.data = data;
+    }
+
+    /**
+     * Returns the unique session ID identifying this In-Band Bytestream.
+     * 
+     * @return the unique session ID identifying this In-Band Bytestream
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    /**
+     * Returns the sequence of this packet in regard to the other data packets.
+     * 
+     * @return the sequence of this packet in regard to the other data packets.
+     */
+    public long getSeq() {
+        return seq;
+    }
+
+    /**
+     * Returns the data contained in this packet.
+     * 
+     * @return the data contained in this packet.
+     */
+    public String getData() {
+        return data;
+    }
+
+    /**
+     * Returns the decoded data or null if data could not be decoded.
+     * <p>
+     * The encoded data is invalid if it contains bad Base64 input characters or
+     * if it contains the pad ('=') character on a position other than the last
+     * character(s) of the data. See <a
+     * href="http://xmpp.org/extensions/xep-0047.html#sec">XEP-0047</a> Section
+     * 6.
+     * 
+     * @return the decoded data
+     */
+    public byte[] getDecodedData() {
+        // return cached decoded data
+        if (this.decodedData != null) {
+            return this.decodedData;
+        }
+
+        // data must not contain the pad (=) other than end of data
+        if (data.matches(".*={1,2}+.+")) {
+            return null;
+        }
+
+        // decodeBase64 will return null if bad characters are included
+        this.decodedData = StringUtils.decodeBase64(data);
+        return this.decodedData;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return InBandBytestreamManager.NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<");
+        buf.append(getElementName());
+        buf.append(" ");
+        buf.append("xmlns=\"");
+        buf.append(InBandBytestreamManager.NAMESPACE);
+        buf.append("\" ");
+        buf.append("seq=\"");
+        buf.append(seq);
+        buf.append("\" ");
+        buf.append("sid=\"");
+        buf.append(sessionID);
+        buf.append("\">");
+        buf.append(data);
+        buf.append("</");
+        buf.append(getElementName());
+        buf.append(">");
+        return buf.toString();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java
index 94a7a9b..66e9248 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java
@@ -1,126 +1,126 @@
-/**

- * 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.smackx.bytestreams.ibb.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType;

-

-/**

- * Represents a request to open an In-Band Bytestream.

- * 

- * @author Henning Staib

- */

-public class Open extends IQ {

-

-    /* unique session ID identifying this In-Band Bytestream */

-    private final String sessionID;

-

-    /* block size in which the data will be fragmented */

-    private final int blockSize;

-

-    /* stanza type used to encapsulate the data */

-    private final StanzaType stanza;

-

-    /**

-     * Creates a new In-Band Bytestream open request packet.

-     * <p>

-     * The data sent over this In-Band Bytestream will be fragmented in blocks

-     * with the given block size. The block size should not be greater than

-     * 65535. A recommended default value is 4096.

-     * <p>

-     * The data can be sent using IQ stanzas or message stanzas.

-     * 

-     * @param sessionID unique session ID identifying this In-Band Bytestream

-     * @param blockSize block size in which the data will be fragmented

-     * @param stanza stanza type used to encapsulate the data

-     */

-    public Open(String sessionID, int blockSize, StanzaType stanza) {

-        if (sessionID == null || "".equals(sessionID)) {

-            throw new IllegalArgumentException("Session ID must not be null or empty");

-        }

-        if (blockSize <= 0) {

-            throw new IllegalArgumentException("Block size must be greater than zero");

-        }

-

-        this.sessionID = sessionID;

-        this.blockSize = blockSize;

-        this.stanza = stanza;

-        setType(Type.SET);

-    }

-

-    /**

-     * Creates a new In-Band Bytestream open request packet.

-     * <p>

-     * The data sent over this In-Band Bytestream will be fragmented in blocks

-     * with the given block size. The block size should not be greater than

-     * 65535. A recommended default value is 4096.

-     * <p>

-     * The data will be sent using IQ stanzas.

-     * 

-     * @param sessionID unique session ID identifying this In-Band Bytestream

-     * @param blockSize block size in which the data will be fragmented

-     */

-    public Open(String sessionID, int blockSize) {

-        this(sessionID, blockSize, StanzaType.IQ);

-    }

-

-    /**

-     * Returns the unique session ID identifying this In-Band Bytestream.

-     * 

-     * @return the unique session ID identifying this In-Band Bytestream

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    /**

-     * Returns the block size in which the data will be fragmented.

-     * 

-     * @return the block size in which the data will be fragmented

-     */

-    public int getBlockSize() {

-        return blockSize;

-    }

-

-    /**

-     * Returns the stanza type used to encapsulate the data.

-     * 

-     * @return the stanza type used to encapsulate the data

-     */

-    public StanzaType getStanza() {

-        return stanza;

-    }

-

-    @Override

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<open ");

-        buf.append("xmlns=\"");

-        buf.append(InBandBytestreamManager.NAMESPACE);

-        buf.append("\" ");

-        buf.append("block-size=\"");

-        buf.append(blockSize);

-        buf.append("\" ");

-        buf.append("sid=\"");

-        buf.append(sessionID);

-        buf.append("\" ");

-        buf.append("stanza=\"");

-        buf.append(stanza.toString().toLowerCase());

-        buf.append("\"");

-        buf.append("/>");

-        return buf.toString();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType;
+
+/**
+ * Represents a request to open an In-Band Bytestream.
+ * 
+ * @author Henning Staib
+ */
+public class Open extends IQ {
+
+    /* unique session ID identifying this In-Band Bytestream */
+    private final String sessionID;
+
+    /* block size in which the data will be fragmented */
+    private final int blockSize;
+
+    /* stanza type used to encapsulate the data */
+    private final StanzaType stanza;
+
+    /**
+     * Creates a new In-Band Bytestream open request packet.
+     * <p>
+     * The data sent over this In-Band Bytestream will be fragmented in blocks
+     * with the given block size. The block size should not be greater than
+     * 65535. A recommended default value is 4096.
+     * <p>
+     * The data can be sent using IQ stanzas or message stanzas.
+     * 
+     * @param sessionID unique session ID identifying this In-Band Bytestream
+     * @param blockSize block size in which the data will be fragmented
+     * @param stanza stanza type used to encapsulate the data
+     */
+    public Open(String sessionID, int blockSize, StanzaType stanza) {
+        if (sessionID == null || "".equals(sessionID)) {
+            throw new IllegalArgumentException("Session ID must not be null or empty");
+        }
+        if (blockSize <= 0) {
+            throw new IllegalArgumentException("Block size must be greater than zero");
+        }
+
+        this.sessionID = sessionID;
+        this.blockSize = blockSize;
+        this.stanza = stanza;
+        setType(Type.SET);
+    }
+
+    /**
+     * Creates a new In-Band Bytestream open request packet.
+     * <p>
+     * The data sent over this In-Band Bytestream will be fragmented in blocks
+     * with the given block size. The block size should not be greater than
+     * 65535. A recommended default value is 4096.
+     * <p>
+     * The data will be sent using IQ stanzas.
+     * 
+     * @param sessionID unique session ID identifying this In-Band Bytestream
+     * @param blockSize block size in which the data will be fragmented
+     */
+    public Open(String sessionID, int blockSize) {
+        this(sessionID, blockSize, StanzaType.IQ);
+    }
+
+    /**
+     * Returns the unique session ID identifying this In-Band Bytestream.
+     * 
+     * @return the unique session ID identifying this In-Band Bytestream
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    /**
+     * Returns the block size in which the data will be fragmented.
+     * 
+     * @return the block size in which the data will be fragmented
+     */
+    public int getBlockSize() {
+        return blockSize;
+    }
+
+    /**
+     * Returns the stanza type used to encapsulate the data.
+     * 
+     * @return the stanza type used to encapsulate the data
+     */
+    public StanzaType getStanza() {
+        return stanza;
+    }
+
+    @Override
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<open ");
+        buf.append("xmlns=\"");
+        buf.append(InBandBytestreamManager.NAMESPACE);
+        buf.append("\" ");
+        buf.append("block-size=\"");
+        buf.append(blockSize);
+        buf.append("\" ");
+        buf.append("sid=\"");
+        buf.append(sessionID);
+        buf.append("\" ");
+        buf.append("stanza=\"");
+        buf.append(stanza.toString().toLowerCase());
+        buf.append("\"");
+        buf.append("/>");
+        return buf.toString();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java
index 566724c..5065819 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java
@@ -1,33 +1,33 @@
-/**

- * 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.smackx.bytestreams.ibb.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses a close In-Band Bytestream packet.

- * 

- * @author Henning Staib

- */

-public class CloseIQProvider implements IQProvider {

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        String sid = parser.getAttributeValue("", "sid");

-        return new Close(sid);

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Close;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses a close In-Band Bytestream packet.
+ * 
+ * @author Henning Staib
+ */
+public class CloseIQProvider implements IQProvider {
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        String sid = parser.getAttributeValue("", "sid");
+        return new Close(sid);
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java
index 5abed08..7c78d69 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java
@@ -1,45 +1,45 @@
-/**

- * 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.smackx.bytestreams.ibb.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses an In-Band Bytestream data packet which can be a packet extension of

- * either an IQ stanza or a message stanza.

- * 

- * @author Henning Staib

- */

-public class DataPacketProvider implements PacketExtensionProvider, IQProvider {

-

-    public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-        String sessionID = parser.getAttributeValue("", "sid");

-        long seq = Long.parseLong(parser.getAttributeValue("", "seq"));

-        String data = parser.nextText();

-        return new DataPacketExtension(sessionID, seq, data);

-    }

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        DataPacketExtension data = (DataPacketExtension) parseExtension(parser);

-        IQ iq = new Data(data);

-        return iq;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Data;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses an In-Band Bytestream data packet which can be a packet extension of
+ * either an IQ stanza or a message stanza.
+ * 
+ * @author Henning Staib
+ */
+public class DataPacketProvider implements PacketExtensionProvider, IQProvider {
+
+    public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+        String sessionID = parser.getAttributeValue("", "sid");
+        long seq = Long.parseLong(parser.getAttributeValue("", "seq"));
+        String data = parser.nextText();
+        return new DataPacketExtension(sessionID, seq, data);
+    }
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        DataPacketExtension data = (DataPacketExtension) parseExtension(parser);
+        IQ iq = new Data(data);
+        return iq;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java
index 3cc725a..4ff81e8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java
@@ -1,45 +1,45 @@
-/**

- * 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.smackx.bytestreams.ibb.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses an In-Band Bytestream open packet.

- * 

- * @author Henning Staib

- */

-public class OpenIQProvider implements IQProvider {

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        String sessionID = parser.getAttributeValue("", "sid");

-        int blockSize = Integer.parseInt(parser.getAttributeValue("", "block-size"));

-

-        String stanzaValue = parser.getAttributeValue("", "stanza");

-        StanzaType stanza = null;

-        if (stanzaValue == null) {

-            stanza = StanzaType.IQ;

-        }

-        else {

-            stanza = StanzaType.valueOf(stanzaValue.toUpperCase());

-        }

-

-        return new Open(sessionID, blockSize, stanza);

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.ibb.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses an In-Band Bytestream open packet.
+ * 
+ * @author Henning Staib
+ */
+public class OpenIQProvider implements IQProvider {
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        String sessionID = parser.getAttributeValue("", "sid");
+        int blockSize = Integer.parseInt(parser.getAttributeValue("", "block-size"));
+
+        String stanzaValue = parser.getAttributeValue("", "stanza");
+        StanzaType stanza = null;
+        if (stanzaValue == null) {
+            stanza = StanzaType.IQ;
+        }
+        else {
+            stanza = StanzaType.valueOf(stanzaValue.toUpperCase());
+        }
+
+        return new Open(sessionID, blockSize, stanza);
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java
index 2a78250..a5a8825 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java
@@ -1,119 +1,119 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.util.concurrent.ExecutorService;

-import java.util.concurrent.Executors;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.IQTypeFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-

-/**

- * InitiationListener handles all incoming SOCKS5 Bytestream initiation requests. If there are no

- * listeners for a SOCKS5 bytestream request InitiationListener will always refuse the request and

- * reply with a &lt;not-acceptable/&gt; error (<a

- * href="http://xmpp.org/extensions/xep-0065.html#usecase-alternate">XEP-0065</a> Section 5.2.A2).

- * 

- * @author Henning Staib

- */

-final class InitiationListener implements PacketListener {

-

-    /* manager containing the listeners and the XMPP connection */

-    private final Socks5BytestreamManager manager;

-

-    /* packet filter for all SOCKS5 Bytestream requests */

-    private final PacketFilter initFilter = new AndFilter(new PacketTypeFilter(Bytestream.class),

-                    new IQTypeFilter(IQ.Type.SET));

-

-    /* executor service to process incoming requests concurrently */

-    private final ExecutorService initiationListenerExecutor;

-

-    /**

-     * Constructor

-     * 

-     * @param manager the SOCKS5 Bytestream manager

-     */

-    protected InitiationListener(Socks5BytestreamManager manager) {

-        this.manager = manager;

-        initiationListenerExecutor = Executors.newCachedThreadPool();

-    }

-

-    public void processPacket(final Packet packet) {

-        initiationListenerExecutor.execute(new Runnable() {

-

-            public void run() {

-                processRequest(packet);

-            }

-        });

-    }

-

-    private void processRequest(Packet packet) {

-        Bytestream byteStreamRequest = (Bytestream) packet;

-

-        // ignore request if in ignore list

-        if (this.manager.getIgnoredBytestreamRequests().remove(byteStreamRequest.getSessionID())) {

-            return;

-        }

-

-        // build bytestream request from packet

-        Socks5BytestreamRequest request = new Socks5BytestreamRequest(this.manager,

-                        byteStreamRequest);

-

-        // notify listeners for bytestream initiation from a specific user

-        BytestreamListener userListener = this.manager.getUserListener(byteStreamRequest.getFrom());

-        if (userListener != null) {

-            userListener.incomingBytestreamRequest(request);

-

-        }

-        else if (!this.manager.getAllRequestListeners().isEmpty()) {

-            /*

-             * if there is no user specific listener inform listeners for all initiation requests

-             */

-            for (BytestreamListener listener : this.manager.getAllRequestListeners()) {

-                listener.incomingBytestreamRequest(request);

-            }

-

-        }

-        else {

-            /*

-             * if there is no listener for this initiation request, reply with reject message

-             */

-            this.manager.replyRejectPacket(byteStreamRequest);

-        }

-    }

-

-    /**

-     * Returns the packet filter for SOCKS5 Bytestream initialization requests.

-     * 

-     * @return the packet filter for SOCKS5 Bytestream initialization requests

-     */

-    protected PacketFilter getFilter() {

-        return this.initFilter;

-    }

-

-    /**

-     * Shuts down the listeners executor service.

-     */

-    protected void shutdown() {

-        this.initiationListenerExecutor.shutdownNow();

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.IQTypeFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+
+/**
+ * InitiationListener handles all incoming SOCKS5 Bytestream initiation requests. If there are no
+ * listeners for a SOCKS5 bytestream request InitiationListener will always refuse the request and
+ * reply with a &lt;not-acceptable/&gt; error (<a
+ * href="http://xmpp.org/extensions/xep-0065.html#usecase-alternate">XEP-0065</a> Section 5.2.A2).
+ * 
+ * @author Henning Staib
+ */
+final class InitiationListener implements PacketListener {
+
+    /* manager containing the listeners and the XMPP connection */
+    private final Socks5BytestreamManager manager;
+
+    /* packet filter for all SOCKS5 Bytestream requests */
+    private final PacketFilter initFilter = new AndFilter(new PacketTypeFilter(Bytestream.class),
+                    new IQTypeFilter(IQ.Type.SET));
+
+    /* executor service to process incoming requests concurrently */
+    private final ExecutorService initiationListenerExecutor;
+
+    /**
+     * Constructor
+     * 
+     * @param manager the SOCKS5 Bytestream manager
+     */
+    protected InitiationListener(Socks5BytestreamManager manager) {
+        this.manager = manager;
+        initiationListenerExecutor = Executors.newCachedThreadPool();
+    }
+
+    public void processPacket(final Packet packet) {
+        initiationListenerExecutor.execute(new Runnable() {
+
+            public void run() {
+                processRequest(packet);
+            }
+        });
+    }
+
+    private void processRequest(Packet packet) {
+        Bytestream byteStreamRequest = (Bytestream) packet;
+
+        // ignore request if in ignore list
+        if (this.manager.getIgnoredBytestreamRequests().remove(byteStreamRequest.getSessionID())) {
+            return;
+        }
+
+        // build bytestream request from packet
+        Socks5BytestreamRequest request = new Socks5BytestreamRequest(this.manager,
+                        byteStreamRequest);
+
+        // notify listeners for bytestream initiation from a specific user
+        BytestreamListener userListener = this.manager.getUserListener(byteStreamRequest.getFrom());
+        if (userListener != null) {
+            userListener.incomingBytestreamRequest(request);
+
+        }
+        else if (!this.manager.getAllRequestListeners().isEmpty()) {
+            /*
+             * if there is no user specific listener inform listeners for all initiation requests
+             */
+            for (BytestreamListener listener : this.manager.getAllRequestListeners()) {
+                listener.incomingBytestreamRequest(request);
+            }
+
+        }
+        else {
+            /*
+             * if there is no listener for this initiation request, reply with reject message
+             */
+            this.manager.replyRejectPacket(byteStreamRequest);
+        }
+    }
+
+    /**
+     * Returns the packet filter for SOCKS5 Bytestream initialization requests.
+     * 
+     * @return the packet filter for SOCKS5 Bytestream initialization requests
+     */
+    protected PacketFilter getFilter() {
+        return this.initFilter;
+    }
+
+    /**
+     * Shuts down the listeners executor service.
+     */
+    protected void shutdown() {
+        this.initiationListenerExecutor.shutdownNow();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java
index 1430b1d..39c8d33 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java
@@ -1,43 +1,43 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.BytestreamRequest;

-

-/**

- * Socks5BytestreamListener are informed if a remote user wants to initiate a SOCKS5 Bytestream.

- * Implement this interface to handle incoming SOCKS5 Bytestream requests.

- * <p>

- * There are two ways to add this listener. See

- * {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and

- * {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for

- * further details.

- * 

- * @author Henning Staib

- */

-public abstract class Socks5BytestreamListener implements BytestreamListener {

-

-    public void incomingBytestreamRequest(BytestreamRequest request) {

-        incomingBytestreamRequest((Socks5BytestreamRequest) request);

-    }

-

-    /**

-     * This listener is notified if a SOCKS5 Bytestream request from another user has been received.

-     * 

-     * @param request the incoming SOCKS5 Bytestream request

-     */

-    public abstract void incomingBytestreamRequest(Socks5BytestreamRequest request);

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
+
+/**
+ * Socks5BytestreamListener are informed if a remote user wants to initiate a SOCKS5 Bytestream.
+ * Implement this interface to handle incoming SOCKS5 Bytestream requests.
+ * <p>
+ * There are two ways to add this listener. See
+ * {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and
+ * {@link Socks5BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for
+ * further details.
+ * 
+ * @author Henning Staib
+ */
+public abstract class Socks5BytestreamListener implements BytestreamListener {
+
+    public void incomingBytestreamRequest(BytestreamRequest request) {
+        incomingBytestreamRequest((Socks5BytestreamRequest) request);
+    }
+
+    /**
+     * This listener is notified if a SOCKS5 Bytestream request from another user has been received.
+     * 
+     * @param request the incoming SOCKS5 Bytestream request
+     */
+    public abstract void incomingBytestreamRequest(Socks5BytestreamRequest request);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java
index 2860415..d78d5d1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java
@@ -1,768 +1,768 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.IOException;

-import java.net.Socket;

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.Iterator;

-import java.util.LinkedList;

-import java.util.List;

-import java.util.Map;

-import java.util.Random;

-import java.util.concurrent.ConcurrentHashMap;

-import java.util.concurrent.TimeoutException;

-

-import org.jivesoftware.smack.AbstractConnectionListener;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.ConnectionCreationListener;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.SyncPacketSend;

-import org.jivesoftware.smackx.ServiceDiscoveryManager;

-import org.jivesoftware.smackx.bytestreams.BytestreamListener;

-import org.jivesoftware.smackx.bytestreams.BytestreamManager;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHostUsed;

-import org.jivesoftware.smackx.filetransfer.FileTransferManager;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.DiscoverItems;

-import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;

-import org.jivesoftware.smackx.packet.DiscoverItems.Item;

-

-/**

- * The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the <a

- * href="http://xmpp.org/extensions/xep-0065.html">XEP-0065</a>.

- * <p>

- * A SOCKS5 Bytestream is negotiated partly over the XMPP XML stream and partly over a separate

- * socket. The actual transfer though takes place over a separately created socket.

- * <p>

- * A SOCKS5 Bytestream generally has three parties, the initiator, the target, and the stream host.

- * The stream host is a specialized SOCKS5 proxy setup on a server, or, the initiator can act as the

- * stream host.

- * <p>

- * To establish a SOCKS5 Bytestream invoke the {@link #establishSession(String)} method. This will

- * negotiate a SOCKS5 Bytestream with the given target JID and return a socket.

- * <p>

- * If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file

- * transfer) invoke {@link #establishSession(String, String)}.

- * <p>

- * To handle incoming SOCKS5 Bytestream requests add an {@link Socks5BytestreamListener} to the

- * manager. There are two ways to add this listener. If you want to be informed about incoming

- * SOCKS5 Bytestreams from a specific user add the listener by invoking

- * {@link #addIncomingBytestreamListener(BytestreamListener, String)}. If the listener should

- * respond to all SOCKS5 Bytestream requests invoke

- * {@link #addIncomingBytestreamListener(BytestreamListener)}.

- * <p>

- * Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5

- * bytestream requests sent in the context of <a

- * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

- * {@link FileTransferManager})

- * <p>

- * If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests

- * will be rejected by returning a &lt;not-acceptable/&gt; error to the initiator.

- * 

- * @author Henning Staib

- */

-public final class Socks5BytestreamManager implements BytestreamManager {

-

-    /*

-     * create a new Socks5BytestreamManager and register a shutdown listener on every established

-     * connection

-     */

-    static {

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-

-            public void connectionCreated(Connection connection) {

-                final Socks5BytestreamManager manager;

-                manager = Socks5BytestreamManager.getBytestreamManager(connection);

-

-                // register shutdown listener

-                connection.addConnectionListener(new AbstractConnectionListener() {

-

-                    public void connectionClosed() {

-                        manager.disableService();

-                    }

-

-                });

-            }

-

-        });

-    }

-

-    /**

-     * The XMPP namespace of the SOCKS5 Bytestream

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";

-

-    /* prefix used to generate session IDs */

-    private static final String SESSION_ID_PREFIX = "js5_";

-

-    /* random generator to create session IDs */

-    private final static Random randomGenerator = new Random();

-

-    /* stores one Socks5BytestreamManager for each XMPP connection */

-    private final static Map<Connection, Socks5BytestreamManager> managers = new HashMap<Connection, Socks5BytestreamManager>();

-

-    /* XMPP connection */

-    private final Connection connection;

-

-    /*

-     * assigns a user to a listener that is informed if a bytestream request for this user is

-     * received

-     */

-    private final Map<String, BytestreamListener> userListeners = new ConcurrentHashMap<String, BytestreamListener>();

-

-    /*

-     * list of listeners that respond to all bytestream requests if there are not user specific

-     * listeners for that request

-     */

-    private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());

-

-    /* listener that handles all incoming bytestream requests */

-    private final InitiationListener initiationListener;

-

-    /* timeout to wait for the response to the SOCKS5 Bytestream initialization request */

-    private int targetResponseTimeout = 10000;

-

-    /* timeout for connecting to the SOCKS5 proxy selected by the target */

-    private int proxyConnectionTimeout = 10000;

-

-    /* blacklist of errornous SOCKS5 proxies */

-    private final List<String> proxyBlacklist = Collections.synchronizedList(new LinkedList<String>());

-

-    /* remember the last proxy that worked to prioritize it */

-    private String lastWorkingProxy = null;

-

-    /* flag to enable/disable prioritization of last working proxy */

-    private boolean proxyPrioritizationEnabled = true;

-

-    /*

-     * list containing session IDs of SOCKS5 Bytestream initialization packets that should be

-     * ignored by the InitiationListener

-     */

-    private List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());

-

-    /**

-     * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given

-     * {@link Connection}.

-     * <p>

-     * If no manager exists a new is created and initialized.

-     * 

-     * @param connection the XMPP connection or <code>null</code> if given connection is

-     *        <code>null</code>

-     * @return the Socks5BytestreamManager for the given XMPP connection

-     */

-    public static synchronized Socks5BytestreamManager getBytestreamManager(Connection connection) {

-        if (connection == null) {

-            return null;

-        }

-        Socks5BytestreamManager manager = managers.get(connection);

-        if (manager == null) {

-            manager = new Socks5BytestreamManager(connection);

-            managers.put(connection, manager);

-            manager.activate();

-        }

-        return manager;

-    }

-

-    /**

-     * Private constructor.

-     * 

-     * @param connection the XMPP connection

-     */

-    private Socks5BytestreamManager(Connection connection) {

-        this.connection = connection;

-        this.initiationListener = new InitiationListener(this);

-    }

-

-    /**

-     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request unless

-     * there is a user specific BytestreamListener registered.

-     * <p>

-     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a

-     * &lt;not-acceptable/&gt; error.

-     * <p>

-     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5

-     * bytestream requests sent in the context of <a

-     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

-     * {@link FileTransferManager})

-     * 

-     * @param listener the listener to register

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener) {

-        this.allRequestListeners.add(listener);

-    }

-

-    /**

-     * Removes the given listener from the list of listeners for all incoming SOCKS5 Bytestream

-     * requests.

-     * 

-     * @param listener the listener to remove

-     */

-    public void removeIncomingBytestreamListener(BytestreamListener listener) {

-        this.allRequestListeners.remove(listener);

-    }

-

-    /**

-     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request from the

-     * given user.

-     * <p>

-     * Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific

-     * user.

-     * <p>

-     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a

-     * &lt;not-acceptable/&gt; error.

-     * <p>

-     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5

-     * bytestream requests sent in the context of <a

-     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See

-     * {@link FileTransferManager})

-     * 

-     * @param listener the listener to register

-     * @param initiatorJID the JID of the user that wants to establish a SOCKS5 Bytestream

-     */

-    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID) {

-        this.userListeners.put(initiatorJID, listener);

-    }

-

-    /**

-     * Removes the listener for the given user.

-     * 

-     * @param initiatorJID the JID of the user the listener should be removed

-     */

-    public void removeIncomingBytestreamListener(String initiatorJID) {

-        this.userListeners.remove(initiatorJID);

-    }

-

-    /**

-     * Use this method to ignore the next incoming SOCKS5 Bytestream request containing the given

-     * session ID. No listeners will be notified for this request and and no error will be returned

-     * to the initiator.

-     * <p>

-     * This method should be used if you are awaiting a SOCKS5 Bytestream request as a reply to

-     * another packet (e.g. file transfer).

-     * 

-     * @param sessionID to be ignored

-     */

-    public void ignoreBytestreamRequestOnce(String sessionID) {

-        this.ignoredBytestreamRequests.add(sessionID);

-    }

-

-    /**

-     * Disables the SOCKS5 Bytestream manager by removing the SOCKS5 Bytestream feature from the

-     * service discovery, disabling the listener for SOCKS5 Bytestream initiation requests and

-     * resetting its internal state.

-     * <p>

-     * To re-enable the SOCKS5 Bytestream feature invoke {@link #getBytestreamManager(Connection)}.

-     * Using the file transfer API will automatically re-enable the SOCKS5 Bytestream feature.

-     */

-    public synchronized void disableService() {

-

-        // remove initiation packet listener

-        this.connection.removePacketListener(this.initiationListener);

-

-        // shutdown threads

-        this.initiationListener.shutdown();

-

-        // clear listeners

-        this.allRequestListeners.clear();

-        this.userListeners.clear();

-

-        // reset internal state

-        this.lastWorkingProxy = null;

-        this.proxyBlacklist.clear();

-        this.ignoredBytestreamRequests.clear();

-

-        // remove manager from static managers map

-        managers.remove(this.connection);

-

-        // shutdown local SOCKS5 proxy if there are no more managers for other connections

-        if (managers.size() == 0) {

-            Socks5Proxy.getSocks5Proxy().stop();

-        }

-

-        // remove feature from service discovery

-        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);

-

-        // check if service discovery is not already disposed by connection shutdown

-        if (serviceDiscoveryManager != null) {

-            serviceDiscoveryManager.removeFeature(NAMESPACE);

-        }

-

-    }

-

-    /**

-     * Returns the timeout to wait for the response to the SOCKS5 Bytestream initialization request.

-     * Default is 10000ms.

-     * 

-     * @return the timeout to wait for the response to the SOCKS5 Bytestream initialization request

-     */

-    public int getTargetResponseTimeout() {

-        if (this.targetResponseTimeout <= 0) {

-            this.targetResponseTimeout = 10000;

-        }

-        return targetResponseTimeout;

-    }

-

-    /**

-     * Sets the timeout to wait for the response to the SOCKS5 Bytestream initialization request.

-     * Default is 10000ms.

-     * 

-     * @param targetResponseTimeout the timeout to set

-     */

-    public void setTargetResponseTimeout(int targetResponseTimeout) {

-        this.targetResponseTimeout = targetResponseTimeout;

-    }

-

-    /**

-     * Returns the timeout for connecting to the SOCKS5 proxy selected by the target. Default is

-     * 10000ms.

-     * 

-     * @return the timeout for connecting to the SOCKS5 proxy selected by the target

-     */

-    public int getProxyConnectionTimeout() {

-        if (this.proxyConnectionTimeout <= 0) {

-            this.proxyConnectionTimeout = 10000;

-        }

-        return proxyConnectionTimeout;

-    }

-

-    /**

-     * Sets the timeout for connecting to the SOCKS5 proxy selected by the target. Default is

-     * 10000ms.

-     * 

-     * @param proxyConnectionTimeout the timeout to set

-     */

-    public void setProxyConnectionTimeout(int proxyConnectionTimeout) {

-        this.proxyConnectionTimeout = proxyConnectionTimeout;

-    }

-

-    /**

-     * Returns if the prioritization of the last working SOCKS5 proxy on successive SOCKS5

-     * Bytestream connections is enabled. Default is <code>true</code>.

-     * 

-     * @return <code>true</code> if prioritization is enabled, <code>false</code> otherwise

-     */

-    public boolean isProxyPrioritizationEnabled() {

-        return proxyPrioritizationEnabled;

-    }

-

-    /**

-     * Enable/disable the prioritization of the last working SOCKS5 proxy on successive SOCKS5

-     * Bytestream connections.

-     * 

-     * @param proxyPrioritizationEnabled enable/disable the prioritization of the last working

-     *        SOCKS5 proxy

-     */

-    public void setProxyPrioritizationEnabled(boolean proxyPrioritizationEnabled) {

-        this.proxyPrioritizationEnabled = proxyPrioritizationEnabled;

-    }

-

-    /**

-     * Establishes a SOCKS5 Bytestream with the given user and returns the Socket to send/receive

-     * data to/from the user.

-     * <p>

-     * Use this method to establish SOCKS5 Bytestreams to users accepting all incoming Socks5

-     * bytestream requests since this method doesn't provide a way to tell the user something about

-     * the data to be sent.

-     * <p>

-     * To establish a SOCKS5 Bytestream after negotiation the kind of data to be sent (e.g. file

-     * transfer) use {@link #establishSession(String, String)}.

-     * 

-     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established

-     * @return the Socket to send/receive data to/from the user

-     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5

-     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies

-     * @throws IOException if the bytestream could not be established

-     * @throws InterruptedException if the current thread was interrupted while waiting

-     */

-    public Socks5BytestreamSession establishSession(String targetJID) throws XMPPException,

-                    IOException, InterruptedException {

-        String sessionID = getNextSessionID();

-        return establishSession(targetJID, sessionID);

-    }

-

-    /**

-     * Establishes a SOCKS5 Bytestream with the given user using the given session ID and returns

-     * the Socket to send/receive data to/from the user.

-     * 

-     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established

-     * @param sessionID the session ID for the SOCKS5 Bytestream request

-     * @return the Socket to send/receive data to/from the user

-     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5

-     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies

-     * @throws IOException if the bytestream could not be established

-     * @throws InterruptedException if the current thread was interrupted while waiting

-     */

-    public Socks5BytestreamSession establishSession(String targetJID, String sessionID)

-                    throws XMPPException, IOException, InterruptedException {

-

-        XMPPException discoveryException = null;

-        // check if target supports SOCKS5 Bytestream

-        if (!supportsSocks5(targetJID)) {

-            throw new XMPPException(targetJID + " doesn't support SOCKS5 Bytestream");

-        }

-

-        List<String> proxies = new ArrayList<String>();

-        // determine SOCKS5 proxies from XMPP-server

-        try {

-            proxies.addAll(determineProxies());

-        } catch (XMPPException e) {

-            // don't abort here, just remember the exception thrown by determineProxies()

-            // determineStreamHostInfos() will at least add the local Socks5 proxy (if enabled)

-            discoveryException = e;

-        }

-

-        // determine address and port of each proxy

-        List<StreamHost> streamHosts = determineStreamHostInfos(proxies);

-

-        if (streamHosts.isEmpty()) {

-            throw discoveryException != null ? discoveryException : new XMPPException("no SOCKS5 proxies available");

-        }

-

-        // compute digest

-        String digest = Socks5Utils.createDigest(sessionID, this.connection.getUser(), targetJID);

-

-        // prioritize last working SOCKS5 proxy if exists

-        if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {

-            StreamHost selectedStreamHost = null;

-            for (StreamHost streamHost : streamHosts) {

-                if (streamHost.getJID().equals(this.lastWorkingProxy)) {

-                    selectedStreamHost = streamHost;

-                    break;

-                }

-            }

-            if (selectedStreamHost != null) {

-                streamHosts.remove(selectedStreamHost);

-                streamHosts.add(0, selectedStreamHost);

-            }

-

-        }

-

-        Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();

-        try {

-

-            // add transfer digest to local proxy to make transfer valid

-            socks5Proxy.addTransfer(digest);

-

-            // create initiation packet

-            Bytestream initiation = createBytestreamInitiation(sessionID, targetJID, streamHosts);

-

-            // send initiation packet

-            Packet response = SyncPacketSend.getReply(this.connection, initiation,

-                            getTargetResponseTimeout());

-

-            // extract used stream host from response

-            StreamHostUsed streamHostUsed = ((Bytestream) response).getUsedHost();

-            StreamHost usedStreamHost = initiation.getStreamHost(streamHostUsed.getJID());

-

-            if (usedStreamHost == null) {

-                throw new XMPPException("Remote user responded with unknown host");

-            }

-

-            // build SOCKS5 client

-            Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest,

-                            this.connection, sessionID, targetJID);

-

-            // establish connection to proxy

-            Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());

-

-            // remember last working SOCKS5 proxy to prioritize it for next request

-            this.lastWorkingProxy = usedStreamHost.getJID();

-

-            // negotiation successful, return the output stream

-            return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(

-                            this.connection.getUser()));

-

-        }

-        catch (TimeoutException e) {

-            throw new IOException("Timeout while connecting to SOCKS5 proxy");

-        }

-        finally {

-

-            // remove transfer digest if output stream is returned or an exception

-            // occurred

-            socks5Proxy.removeTransfer(digest);

-

-        }

-    }

-

-    /**

-     * Returns <code>true</code> if the given target JID supports feature SOCKS5 Bytestream.

-     * 

-     * @param targetJID the target JID

-     * @return <code>true</code> if the given target JID supports feature SOCKS5 Bytestream

-     *         otherwise <code>false</code>

-     * @throws XMPPException if there was an error querying target for supported features

-     */

-    private boolean supportsSocks5(String targetJID) throws XMPPException {

-        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);

-        DiscoverInfo discoverInfo = serviceDiscoveryManager.discoverInfo(targetJID);

-        return discoverInfo.containsFeature(NAMESPACE);

-    }

-

-    /**

-     * Returns a list of JIDs of SOCKS5 proxies by querying the XMPP server. The SOCKS5 proxies are

-     * in the same order as returned by the XMPP server.

-     * 

-     * @return list of JIDs of SOCKS5 proxies

-     * @throws XMPPException if there was an error querying the XMPP server for SOCKS5 proxies

-     */

-    private List<String> determineProxies() throws XMPPException {

-        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);

-

-        List<String> proxies = new ArrayList<String>();

-

-        // get all items form XMPP server

-        DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName());

-        Iterator<Item> itemIterator = discoverItems.getItems();

-

-        // query all items if they are SOCKS5 proxies

-        while (itemIterator.hasNext()) {

-            Item item = itemIterator.next();

-

-            // skip blacklisted servers

-            if (this.proxyBlacklist.contains(item.getEntityID())) {

-                continue;

-            }

-

-            try {

-                DiscoverInfo proxyInfo;

-                proxyInfo = serviceDiscoveryManager.discoverInfo(item.getEntityID());

-                Iterator<Identity> identities = proxyInfo.getIdentities();

-

-                // item must have category "proxy" and type "bytestream"

-                while (identities.hasNext()) {

-                    Identity identity = identities.next();

-

-                    if ("proxy".equalsIgnoreCase(identity.getCategory())

-                                    && "bytestreams".equalsIgnoreCase(identity.getType())) {

-                        proxies.add(item.getEntityID());

-                        break;

-                    }

-

-                    /*

-                     * server is not a SOCKS5 proxy, blacklist server to skip next time a Socks5

-                     * bytestream should be established

-                     */

-                    this.proxyBlacklist.add(item.getEntityID());

-

-                }

-            }

-            catch (XMPPException e) {

-                // blacklist errornous server

-                this.proxyBlacklist.add(item.getEntityID());

-            }

-        }

-

-        return proxies;

-    }

-

-    /**

-     * Returns a list of stream hosts containing the IP address an the port for the given list of

-     * SOCKS5 proxy JIDs. The order of the returned list is the same as the given list of JIDs

-     * excluding all SOCKS5 proxies who's network settings could not be determined. If a local

-     * SOCKS5 proxy is running it will be the first item in the list returned.

-     * 

-     * @param proxies a list of SOCKS5 proxy JIDs

-     * @return a list of stream hosts containing the IP address an the port

-     */

-    private List<StreamHost> determineStreamHostInfos(List<String> proxies) {

-        List<StreamHost> streamHosts = new ArrayList<StreamHost>();

-

-        // add local proxy on first position if exists

-        List<StreamHost> localProxies = getLocalStreamHost();

-        if (localProxies != null) {

-            streamHosts.addAll(localProxies);

-        }

-

-        // query SOCKS5 proxies for network settings

-        for (String proxy : proxies) {

-            Bytestream streamHostRequest = createStreamHostRequest(proxy);

-            try {

-                Bytestream response = (Bytestream) SyncPacketSend.getReply(this.connection,

-                                streamHostRequest);

-                streamHosts.addAll(response.getStreamHosts());

-            }

-            catch (XMPPException e) {

-                // blacklist errornous proxies

-                this.proxyBlacklist.add(proxy);

-            }

-        }

-

-        return streamHosts;

-    }

-

-    /**

-     * Returns a IQ packet to query a SOCKS5 proxy its network settings.

-     * 

-     * @param proxy the proxy to query

-     * @return IQ packet to query a SOCKS5 proxy its network settings

-     */

-    private Bytestream createStreamHostRequest(String proxy) {

-        Bytestream request = new Bytestream();

-        request.setType(IQ.Type.GET);

-        request.setTo(proxy);

-        return request;

-    }

-

-    /**

-     * Returns the stream host information of the local SOCKS5 proxy containing the IP address and

-     * the port or null if local SOCKS5 proxy is not running.

-     * 

-     * @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy

-     *         is not running

-     */

-    private List<StreamHost> getLocalStreamHost() {

-

-        // get local proxy singleton

-        Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();

-

-        if (socks5Server.isRunning()) {

-            List<String> addresses = socks5Server.getLocalAddresses();

-            int port = socks5Server.getPort();

-

-            if (addresses.size() >= 1) {

-                List<StreamHost> streamHosts = new ArrayList<StreamHost>();

-                for (String address : addresses) {

-                    StreamHost streamHost = new StreamHost(this.connection.getUser(), address);

-                    streamHost.setPort(port);

-                    streamHosts.add(streamHost);

-                }

-                return streamHosts;

-            }

-

-        }

-

-        // server is not running or local address could not be determined

-        return null;

-    }

-

-    /**

-     * Returns a SOCKS5 Bytestream initialization request packet with the given session ID

-     * containing the given stream hosts for the given target JID.

-     * 

-     * @param sessionID the session ID for the SOCKS5 Bytestream

-     * @param targetJID the target JID of SOCKS5 Bytestream request

-     * @param streamHosts a list of SOCKS5 proxies the target should connect to

-     * @return a SOCKS5 Bytestream initialization request packet

-     */

-    private Bytestream createBytestreamInitiation(String sessionID, String targetJID,

-                    List<StreamHost> streamHosts) {

-        Bytestream initiation = new Bytestream(sessionID);

-

-        // add all stream hosts

-        for (StreamHost streamHost : streamHosts) {

-            initiation.addStreamHost(streamHost);

-        }

-

-        initiation.setType(IQ.Type.SET);

-        initiation.setTo(targetJID);

-

-        return initiation;

-    }

-

-    /**

-     * Responses to the given packet's sender with a XMPP error that a SOCKS5 Bytestream is not

-     * accepted.

-     * 

-     * @param packet Packet that should be answered with a not-acceptable error

-     */

-    protected void replyRejectPacket(IQ packet) {

-        XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable);

-        IQ errorIQ = IQ.createErrorResponse(packet, xmppError);

-        this.connection.sendPacket(errorIQ);

-    }

-

-    /**

-     * Activates the Socks5BytestreamManager by registering the SOCKS5 Bytestream initialization

-     * listener and enabling the SOCKS5 Bytestream feature.

-     */

-    private void activate() {

-        // register bytestream initiation packet listener

-        this.connection.addPacketListener(this.initiationListener,

-                        this.initiationListener.getFilter());

-

-        // enable SOCKS5 feature

-        enableService();

-    }

-

-    /**

-     * Adds the SOCKS5 Bytestream feature to the service discovery.

-     */

-    private void enableService() {

-        ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(this.connection);

-        if (!manager.includesFeature(NAMESPACE)) {

-            manager.addFeature(NAMESPACE);

-        }

-    }

-

-    /**

-     * Returns a new unique session ID.

-     * 

-     * @return a new unique session ID

-     */

-    private String getNextSessionID() {

-        StringBuilder buffer = new StringBuilder();

-        buffer.append(SESSION_ID_PREFIX);

-        buffer.append(Math.abs(randomGenerator.nextLong()));

-        return buffer.toString();

-    }

-

-    /**

-     * Returns the XMPP connection.

-     * 

-     * @return the XMPP connection

-     */

-    protected Connection getConnection() {

-        return this.connection;

-    }

-

-    /**

-     * Returns the {@link BytestreamListener} that should be informed if a SOCKS5 Bytestream request

-     * from the given initiator JID is received.

-     * 

-     * @param initiator the initiator's JID

-     * @return the listener

-     */

-    protected BytestreamListener getUserListener(String initiator) {

-        return this.userListeners.get(initiator);

-    }

-

-    /**

-     * Returns a list of {@link BytestreamListener} that are informed if there are no listeners for

-     * a specific initiator.

-     * 

-     * @return list of listeners

-     */

-    protected List<BytestreamListener> getAllRequestListeners() {

-        return this.allRequestListeners;

-    }

-

-    /**

-     * Returns the list of session IDs that should be ignored by the InitialtionListener

-     * 

-     * @return list of session IDs

-     */

-    protected List<String> getIgnoredBytestreamRequests() {

-        return ignoredBytestreamRequests;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeoutException;
+
+import org.jivesoftware.smack.AbstractConnectionListener;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.SyncPacketSend;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.bytestreams.BytestreamListener;
+import org.jivesoftware.smackx.bytestreams.BytestreamManager;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHostUsed;
+import org.jivesoftware.smackx.filetransfer.FileTransferManager;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.DiscoverItems;
+import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
+import org.jivesoftware.smackx.packet.DiscoverItems.Item;
+
+/**
+ * The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the <a
+ * href="http://xmpp.org/extensions/xep-0065.html">XEP-0065</a>.
+ * <p>
+ * A SOCKS5 Bytestream is negotiated partly over the XMPP XML stream and partly over a separate
+ * socket. The actual transfer though takes place over a separately created socket.
+ * <p>
+ * A SOCKS5 Bytestream generally has three parties, the initiator, the target, and the stream host.
+ * The stream host is a specialized SOCKS5 proxy setup on a server, or, the initiator can act as the
+ * stream host.
+ * <p>
+ * To establish a SOCKS5 Bytestream invoke the {@link #establishSession(String)} method. This will
+ * negotiate a SOCKS5 Bytestream with the given target JID and return a socket.
+ * <p>
+ * If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file
+ * transfer) invoke {@link #establishSession(String, String)}.
+ * <p>
+ * To handle incoming SOCKS5 Bytestream requests add an {@link Socks5BytestreamListener} to the
+ * manager. There are two ways to add this listener. If you want to be informed about incoming
+ * SOCKS5 Bytestreams from a specific user add the listener by invoking
+ * {@link #addIncomingBytestreamListener(BytestreamListener, String)}. If the listener should
+ * respond to all SOCKS5 Bytestream requests invoke
+ * {@link #addIncomingBytestreamListener(BytestreamListener)}.
+ * <p>
+ * Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5
+ * bytestream requests sent in the context of <a
+ * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+ * {@link FileTransferManager})
+ * <p>
+ * If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests
+ * will be rejected by returning a &lt;not-acceptable/&gt; error to the initiator.
+ * 
+ * @author Henning Staib
+ */
+public final class Socks5BytestreamManager implements BytestreamManager {
+
+    /*
+     * create a new Socks5BytestreamManager and register a shutdown listener on every established
+     * connection
+     */
+    static {
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+
+            public void connectionCreated(Connection connection) {
+                final Socks5BytestreamManager manager;
+                manager = Socks5BytestreamManager.getBytestreamManager(connection);
+
+                // register shutdown listener
+                connection.addConnectionListener(new AbstractConnectionListener() {
+
+                    public void connectionClosed() {
+                        manager.disableService();
+                    }
+
+                });
+            }
+
+        });
+    }
+
+    /**
+     * The XMPP namespace of the SOCKS5 Bytestream
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";
+
+    /* prefix used to generate session IDs */
+    private static final String SESSION_ID_PREFIX = "js5_";
+
+    /* random generator to create session IDs */
+    private final static Random randomGenerator = new Random();
+
+    /* stores one Socks5BytestreamManager for each XMPP connection */
+    private final static Map<Connection, Socks5BytestreamManager> managers = new HashMap<Connection, Socks5BytestreamManager>();
+
+    /* XMPP connection */
+    private final Connection connection;
+
+    /*
+     * assigns a user to a listener that is informed if a bytestream request for this user is
+     * received
+     */
+    private final Map<String, BytestreamListener> userListeners = new ConcurrentHashMap<String, BytestreamListener>();
+
+    /*
+     * list of listeners that respond to all bytestream requests if there are not user specific
+     * listeners for that request
+     */
+    private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());
+
+    /* listener that handles all incoming bytestream requests */
+    private final InitiationListener initiationListener;
+
+    /* timeout to wait for the response to the SOCKS5 Bytestream initialization request */
+    private int targetResponseTimeout = 10000;
+
+    /* timeout for connecting to the SOCKS5 proxy selected by the target */
+    private int proxyConnectionTimeout = 10000;
+
+    /* blacklist of errornous SOCKS5 proxies */
+    private final List<String> proxyBlacklist = Collections.synchronizedList(new LinkedList<String>());
+
+    /* remember the last proxy that worked to prioritize it */
+    private String lastWorkingProxy = null;
+
+    /* flag to enable/disable prioritization of last working proxy */
+    private boolean proxyPrioritizationEnabled = true;
+
+    /*
+     * list containing session IDs of SOCKS5 Bytestream initialization packets that should be
+     * ignored by the InitiationListener
+     */
+    private List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());
+
+    /**
+     * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given
+     * {@link Connection}.
+     * <p>
+     * If no manager exists a new is created and initialized.
+     * 
+     * @param connection the XMPP connection or <code>null</code> if given connection is
+     *        <code>null</code>
+     * @return the Socks5BytestreamManager for the given XMPP connection
+     */
+    public static synchronized Socks5BytestreamManager getBytestreamManager(Connection connection) {
+        if (connection == null) {
+            return null;
+        }
+        Socks5BytestreamManager manager = managers.get(connection);
+        if (manager == null) {
+            manager = new Socks5BytestreamManager(connection);
+            managers.put(connection, manager);
+            manager.activate();
+        }
+        return manager;
+    }
+
+    /**
+     * Private constructor.
+     * 
+     * @param connection the XMPP connection
+     */
+    private Socks5BytestreamManager(Connection connection) {
+        this.connection = connection;
+        this.initiationListener = new InitiationListener(this);
+    }
+
+    /**
+     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request unless
+     * there is a user specific BytestreamListener registered.
+     * <p>
+     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a
+     * &lt;not-acceptable/&gt; error.
+     * <p>
+     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
+     * bytestream requests sent in the context of <a
+     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+     * {@link FileTransferManager})
+     * 
+     * @param listener the listener to register
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener) {
+        this.allRequestListeners.add(listener);
+    }
+
+    /**
+     * Removes the given listener from the list of listeners for all incoming SOCKS5 Bytestream
+     * requests.
+     * 
+     * @param listener the listener to remove
+     */
+    public void removeIncomingBytestreamListener(BytestreamListener listener) {
+        this.allRequestListeners.remove(listener);
+    }
+
+    /**
+     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request from the
+     * given user.
+     * <p>
+     * Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific
+     * user.
+     * <p>
+     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a
+     * &lt;not-acceptable/&gt; error.
+     * <p>
+     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
+     * bytestream requests sent in the context of <a
+     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
+     * {@link FileTransferManager})
+     * 
+     * @param listener the listener to register
+     * @param initiatorJID the JID of the user that wants to establish a SOCKS5 Bytestream
+     */
+    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID) {
+        this.userListeners.put(initiatorJID, listener);
+    }
+
+    /**
+     * Removes the listener for the given user.
+     * 
+     * @param initiatorJID the JID of the user the listener should be removed
+     */
+    public void removeIncomingBytestreamListener(String initiatorJID) {
+        this.userListeners.remove(initiatorJID);
+    }
+
+    /**
+     * Use this method to ignore the next incoming SOCKS5 Bytestream request containing the given
+     * session ID. No listeners will be notified for this request and and no error will be returned
+     * to the initiator.
+     * <p>
+     * This method should be used if you are awaiting a SOCKS5 Bytestream request as a reply to
+     * another packet (e.g. file transfer).
+     * 
+     * @param sessionID to be ignored
+     */
+    public void ignoreBytestreamRequestOnce(String sessionID) {
+        this.ignoredBytestreamRequests.add(sessionID);
+    }
+
+    /**
+     * Disables the SOCKS5 Bytestream manager by removing the SOCKS5 Bytestream feature from the
+     * service discovery, disabling the listener for SOCKS5 Bytestream initiation requests and
+     * resetting its internal state.
+     * <p>
+     * To re-enable the SOCKS5 Bytestream feature invoke {@link #getBytestreamManager(Connection)}.
+     * Using the file transfer API will automatically re-enable the SOCKS5 Bytestream feature.
+     */
+    public synchronized void disableService() {
+
+        // remove initiation packet listener
+        this.connection.removePacketListener(this.initiationListener);
+
+        // shutdown threads
+        this.initiationListener.shutdown();
+
+        // clear listeners
+        this.allRequestListeners.clear();
+        this.userListeners.clear();
+
+        // reset internal state
+        this.lastWorkingProxy = null;
+        this.proxyBlacklist.clear();
+        this.ignoredBytestreamRequests.clear();
+
+        // remove manager from static managers map
+        managers.remove(this.connection);
+
+        // shutdown local SOCKS5 proxy if there are no more managers for other connections
+        if (managers.size() == 0) {
+            Socks5Proxy.getSocks5Proxy().stop();
+        }
+
+        // remove feature from service discovery
+        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
+
+        // check if service discovery is not already disposed by connection shutdown
+        if (serviceDiscoveryManager != null) {
+            serviceDiscoveryManager.removeFeature(NAMESPACE);
+        }
+
+    }
+
+    /**
+     * Returns the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
+     * Default is 10000ms.
+     * 
+     * @return the timeout to wait for the response to the SOCKS5 Bytestream initialization request
+     */
+    public int getTargetResponseTimeout() {
+        if (this.targetResponseTimeout <= 0) {
+            this.targetResponseTimeout = 10000;
+        }
+        return targetResponseTimeout;
+    }
+
+    /**
+     * Sets the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
+     * Default is 10000ms.
+     * 
+     * @param targetResponseTimeout the timeout to set
+     */
+    public void setTargetResponseTimeout(int targetResponseTimeout) {
+        this.targetResponseTimeout = targetResponseTimeout;
+    }
+
+    /**
+     * Returns the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
+     * 10000ms.
+     * 
+     * @return the timeout for connecting to the SOCKS5 proxy selected by the target
+     */
+    public int getProxyConnectionTimeout() {
+        if (this.proxyConnectionTimeout <= 0) {
+            this.proxyConnectionTimeout = 10000;
+        }
+        return proxyConnectionTimeout;
+    }
+
+    /**
+     * Sets the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
+     * 10000ms.
+     * 
+     * @param proxyConnectionTimeout the timeout to set
+     */
+    public void setProxyConnectionTimeout(int proxyConnectionTimeout) {
+        this.proxyConnectionTimeout = proxyConnectionTimeout;
+    }
+
+    /**
+     * Returns if the prioritization of the last working SOCKS5 proxy on successive SOCKS5
+     * Bytestream connections is enabled. Default is <code>true</code>.
+     * 
+     * @return <code>true</code> if prioritization is enabled, <code>false</code> otherwise
+     */
+    public boolean isProxyPrioritizationEnabled() {
+        return proxyPrioritizationEnabled;
+    }
+
+    /**
+     * Enable/disable the prioritization of the last working SOCKS5 proxy on successive SOCKS5
+     * Bytestream connections.
+     * 
+     * @param proxyPrioritizationEnabled enable/disable the prioritization of the last working
+     *        SOCKS5 proxy
+     */
+    public void setProxyPrioritizationEnabled(boolean proxyPrioritizationEnabled) {
+        this.proxyPrioritizationEnabled = proxyPrioritizationEnabled;
+    }
+
+    /**
+     * Establishes a SOCKS5 Bytestream with the given user and returns the Socket to send/receive
+     * data to/from the user.
+     * <p>
+     * Use this method to establish SOCKS5 Bytestreams to users accepting all incoming Socks5
+     * bytestream requests since this method doesn't provide a way to tell the user something about
+     * the data to be sent.
+     * <p>
+     * To establish a SOCKS5 Bytestream after negotiation the kind of data to be sent (e.g. file
+     * transfer) use {@link #establishSession(String, String)}.
+     * 
+     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established
+     * @return the Socket to send/receive data to/from the user
+     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5
+     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies
+     * @throws IOException if the bytestream could not be established
+     * @throws InterruptedException if the current thread was interrupted while waiting
+     */
+    public Socks5BytestreamSession establishSession(String targetJID) throws XMPPException,
+                    IOException, InterruptedException {
+        String sessionID = getNextSessionID();
+        return establishSession(targetJID, sessionID);
+    }
+
+    /**
+     * Establishes a SOCKS5 Bytestream with the given user using the given session ID and returns
+     * the Socket to send/receive data to/from the user.
+     * 
+     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established
+     * @param sessionID the session ID for the SOCKS5 Bytestream request
+     * @return the Socket to send/receive data to/from the user
+     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5
+     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies
+     * @throws IOException if the bytestream could not be established
+     * @throws InterruptedException if the current thread was interrupted while waiting
+     */
+    public Socks5BytestreamSession establishSession(String targetJID, String sessionID)
+                    throws XMPPException, IOException, InterruptedException {
+
+        XMPPException discoveryException = null;
+        // check if target supports SOCKS5 Bytestream
+        if (!supportsSocks5(targetJID)) {
+            throw new XMPPException(targetJID + " doesn't support SOCKS5 Bytestream");
+        }
+
+        List<String> proxies = new ArrayList<String>();
+        // determine SOCKS5 proxies from XMPP-server
+        try {
+            proxies.addAll(determineProxies());
+        } catch (XMPPException e) {
+            // don't abort here, just remember the exception thrown by determineProxies()
+            // determineStreamHostInfos() will at least add the local Socks5 proxy (if enabled)
+            discoveryException = e;
+        }
+
+        // determine address and port of each proxy
+        List<StreamHost> streamHosts = determineStreamHostInfos(proxies);
+
+        if (streamHosts.isEmpty()) {
+            throw discoveryException != null ? discoveryException : new XMPPException("no SOCKS5 proxies available");
+        }
+
+        // compute digest
+        String digest = Socks5Utils.createDigest(sessionID, this.connection.getUser(), targetJID);
+
+        // prioritize last working SOCKS5 proxy if exists
+        if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {
+            StreamHost selectedStreamHost = null;
+            for (StreamHost streamHost : streamHosts) {
+                if (streamHost.getJID().equals(this.lastWorkingProxy)) {
+                    selectedStreamHost = streamHost;
+                    break;
+                }
+            }
+            if (selectedStreamHost != null) {
+                streamHosts.remove(selectedStreamHost);
+                streamHosts.add(0, selectedStreamHost);
+            }
+
+        }
+
+        Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
+        try {
+
+            // add transfer digest to local proxy to make transfer valid
+            socks5Proxy.addTransfer(digest);
+
+            // create initiation packet
+            Bytestream initiation = createBytestreamInitiation(sessionID, targetJID, streamHosts);
+
+            // send initiation packet
+            Packet response = SyncPacketSend.getReply(this.connection, initiation,
+                            getTargetResponseTimeout());
+
+            // extract used stream host from response
+            StreamHostUsed streamHostUsed = ((Bytestream) response).getUsedHost();
+            StreamHost usedStreamHost = initiation.getStreamHost(streamHostUsed.getJID());
+
+            if (usedStreamHost == null) {
+                throw new XMPPException("Remote user responded with unknown host");
+            }
+
+            // build SOCKS5 client
+            Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest,
+                            this.connection, sessionID, targetJID);
+
+            // establish connection to proxy
+            Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());
+
+            // remember last working SOCKS5 proxy to prioritize it for next request
+            this.lastWorkingProxy = usedStreamHost.getJID();
+
+            // negotiation successful, return the output stream
+            return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(
+                            this.connection.getUser()));
+
+        }
+        catch (TimeoutException e) {
+            throw new IOException("Timeout while connecting to SOCKS5 proxy");
+        }
+        finally {
+
+            // remove transfer digest if output stream is returned or an exception
+            // occurred
+            socks5Proxy.removeTransfer(digest);
+
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the given target JID supports feature SOCKS5 Bytestream.
+     * 
+     * @param targetJID the target JID
+     * @return <code>true</code> if the given target JID supports feature SOCKS5 Bytestream
+     *         otherwise <code>false</code>
+     * @throws XMPPException if there was an error querying target for supported features
+     */
+    private boolean supportsSocks5(String targetJID) throws XMPPException {
+        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
+        DiscoverInfo discoverInfo = serviceDiscoveryManager.discoverInfo(targetJID);
+        return discoverInfo.containsFeature(NAMESPACE);
+    }
+
+    /**
+     * Returns a list of JIDs of SOCKS5 proxies by querying the XMPP server. The SOCKS5 proxies are
+     * in the same order as returned by the XMPP server.
+     * 
+     * @return list of JIDs of SOCKS5 proxies
+     * @throws XMPPException if there was an error querying the XMPP server for SOCKS5 proxies
+     */
+    private List<String> determineProxies() throws XMPPException {
+        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
+
+        List<String> proxies = new ArrayList<String>();
+
+        // get all items form XMPP server
+        DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName());
+        Iterator<Item> itemIterator = discoverItems.getItems();
+
+        // query all items if they are SOCKS5 proxies
+        while (itemIterator.hasNext()) {
+            Item item = itemIterator.next();
+
+            // skip blacklisted servers
+            if (this.proxyBlacklist.contains(item.getEntityID())) {
+                continue;
+            }
+
+            try {
+                DiscoverInfo proxyInfo;
+                proxyInfo = serviceDiscoveryManager.discoverInfo(item.getEntityID());
+                Iterator<Identity> identities = proxyInfo.getIdentities();
+
+                // item must have category "proxy" and type "bytestream"
+                while (identities.hasNext()) {
+                    Identity identity = identities.next();
+
+                    if ("proxy".equalsIgnoreCase(identity.getCategory())
+                                    && "bytestreams".equalsIgnoreCase(identity.getType())) {
+                        proxies.add(item.getEntityID());
+                        break;
+                    }
+
+                    /*
+                     * server is not a SOCKS5 proxy, blacklist server to skip next time a Socks5
+                     * bytestream should be established
+                     */
+                    this.proxyBlacklist.add(item.getEntityID());
+
+                }
+            }
+            catch (XMPPException e) {
+                // blacklist errornous server
+                this.proxyBlacklist.add(item.getEntityID());
+            }
+        }
+
+        return proxies;
+    }
+
+    /**
+     * Returns a list of stream hosts containing the IP address an the port for the given list of
+     * SOCKS5 proxy JIDs. The order of the returned list is the same as the given list of JIDs
+     * excluding all SOCKS5 proxies who's network settings could not be determined. If a local
+     * SOCKS5 proxy is running it will be the first item in the list returned.
+     * 
+     * @param proxies a list of SOCKS5 proxy JIDs
+     * @return a list of stream hosts containing the IP address an the port
+     */
+    private List<StreamHost> determineStreamHostInfos(List<String> proxies) {
+        List<StreamHost> streamHosts = new ArrayList<StreamHost>();
+
+        // add local proxy on first position if exists
+        List<StreamHost> localProxies = getLocalStreamHost();
+        if (localProxies != null) {
+            streamHosts.addAll(localProxies);
+        }
+
+        // query SOCKS5 proxies for network settings
+        for (String proxy : proxies) {
+            Bytestream streamHostRequest = createStreamHostRequest(proxy);
+            try {
+                Bytestream response = (Bytestream) SyncPacketSend.getReply(this.connection,
+                                streamHostRequest);
+                streamHosts.addAll(response.getStreamHosts());
+            }
+            catch (XMPPException e) {
+                // blacklist errornous proxies
+                this.proxyBlacklist.add(proxy);
+            }
+        }
+
+        return streamHosts;
+    }
+
+    /**
+     * Returns a IQ packet to query a SOCKS5 proxy its network settings.
+     * 
+     * @param proxy the proxy to query
+     * @return IQ packet to query a SOCKS5 proxy its network settings
+     */
+    private Bytestream createStreamHostRequest(String proxy) {
+        Bytestream request = new Bytestream();
+        request.setType(IQ.Type.GET);
+        request.setTo(proxy);
+        return request;
+    }
+
+    /**
+     * Returns the stream host information of the local SOCKS5 proxy containing the IP address and
+     * the port or null if local SOCKS5 proxy is not running.
+     * 
+     * @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
+     *         is not running
+     */
+    private List<StreamHost> getLocalStreamHost() {
+
+        // get local proxy singleton
+        Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
+
+        if (socks5Server.isRunning()) {
+            List<String> addresses = socks5Server.getLocalAddresses();
+            int port = socks5Server.getPort();
+
+            if (addresses.size() >= 1) {
+                List<StreamHost> streamHosts = new ArrayList<StreamHost>();
+                for (String address : addresses) {
+                    StreamHost streamHost = new StreamHost(this.connection.getUser(), address);
+                    streamHost.setPort(port);
+                    streamHosts.add(streamHost);
+                }
+                return streamHosts;
+            }
+
+        }
+
+        // server is not running or local address could not be determined
+        return null;
+    }
+
+    /**
+     * Returns a SOCKS5 Bytestream initialization request packet with the given session ID
+     * containing the given stream hosts for the given target JID.
+     * 
+     * @param sessionID the session ID for the SOCKS5 Bytestream
+     * @param targetJID the target JID of SOCKS5 Bytestream request
+     * @param streamHosts a list of SOCKS5 proxies the target should connect to
+     * @return a SOCKS5 Bytestream initialization request packet
+     */
+    private Bytestream createBytestreamInitiation(String sessionID, String targetJID,
+                    List<StreamHost> streamHosts) {
+        Bytestream initiation = new Bytestream(sessionID);
+
+        // add all stream hosts
+        for (StreamHost streamHost : streamHosts) {
+            initiation.addStreamHost(streamHost);
+        }
+
+        initiation.setType(IQ.Type.SET);
+        initiation.setTo(targetJID);
+
+        return initiation;
+    }
+
+    /**
+     * Responses to the given packet's sender with a XMPP error that a SOCKS5 Bytestream is not
+     * accepted.
+     * 
+     * @param packet Packet that should be answered with a not-acceptable error
+     */
+    protected void replyRejectPacket(IQ packet) {
+        XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable);
+        IQ errorIQ = IQ.createErrorResponse(packet, xmppError);
+        this.connection.sendPacket(errorIQ);
+    }
+
+    /**
+     * Activates the Socks5BytestreamManager by registering the SOCKS5 Bytestream initialization
+     * listener and enabling the SOCKS5 Bytestream feature.
+     */
+    private void activate() {
+        // register bytestream initiation packet listener
+        this.connection.addPacketListener(this.initiationListener,
+                        this.initiationListener.getFilter());
+
+        // enable SOCKS5 feature
+        enableService();
+    }
+
+    /**
+     * Adds the SOCKS5 Bytestream feature to the service discovery.
+     */
+    private void enableService() {
+        ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(this.connection);
+        if (!manager.includesFeature(NAMESPACE)) {
+            manager.addFeature(NAMESPACE);
+        }
+    }
+
+    /**
+     * Returns a new unique session ID.
+     * 
+     * @return a new unique session ID
+     */
+    private String getNextSessionID() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(SESSION_ID_PREFIX);
+        buffer.append(Math.abs(randomGenerator.nextLong()));
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the XMPP connection.
+     * 
+     * @return the XMPP connection
+     */
+    protected Connection getConnection() {
+        return this.connection;
+    }
+
+    /**
+     * Returns the {@link BytestreamListener} that should be informed if a SOCKS5 Bytestream request
+     * from the given initiator JID is received.
+     * 
+     * @param initiator the initiator's JID
+     * @return the listener
+     */
+    protected BytestreamListener getUserListener(String initiator) {
+        return this.userListeners.get(initiator);
+    }
+
+    /**
+     * Returns a list of {@link BytestreamListener} that are informed if there are no listeners for
+     * a specific initiator.
+     * 
+     * @return list of listeners
+     */
+    protected List<BytestreamListener> getAllRequestListeners() {
+        return this.allRequestListeners;
+    }
+
+    /**
+     * Returns the list of session IDs that should be ignored by the InitialtionListener
+     * 
+     * @return list of session IDs
+     */
+    protected List<String> getIgnoredBytestreamRequests() {
+        return ignoredBytestreamRequests;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java
index 0b2fdeb..5d8f002 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java
@@ -1,316 +1,316 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.IOException;

-import java.net.Socket;

-import java.util.Collection;

-import java.util.concurrent.TimeoutException;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.Cache;

-import org.jivesoftware.smackx.bytestreams.BytestreamRequest;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;

-

-/**

- * Socks5BytestreamRequest class handles incoming SOCKS5 Bytestream requests.

- * 

- * @author Henning Staib

- */

-public class Socks5BytestreamRequest implements BytestreamRequest {

-

-    /* lifetime of an Item in the blacklist */

-    private static final long BLACKLIST_LIFETIME = 60 * 1000 * 120;

-

-    /* size of the blacklist */

-    private static final int BLACKLIST_MAX_SIZE = 100;

-

-    /* blacklist of addresses of SOCKS5 proxies */

-    private static final Cache<String, Integer> ADDRESS_BLACKLIST = new Cache<String, Integer>(

-                    BLACKLIST_MAX_SIZE, BLACKLIST_LIFETIME);

-

-    /*

-     * The number of connection failures it takes for a particular SOCKS5 proxy to be blacklisted.

-     * When a proxy is blacklisted no more connection attempts will be made to it for a period of 2

-     * hours.

-     */

-    private static int CONNECTION_FAILURE_THRESHOLD = 2;

-

-    /* the bytestream initialization request */

-    private Bytestream bytestreamRequest;

-

-    /* SOCKS5 Bytestream manager containing the XMPP connection and helper methods */

-    private Socks5BytestreamManager manager;

-

-    /* timeout to connect to all SOCKS5 proxies */

-    private int totalConnectTimeout = 10000;

-

-    /* minimum timeout to connect to one SOCKS5 proxy */

-    private int minimumConnectTimeout = 2000;

-

-    /**

-     * Returns the number of connection failures it takes for a particular SOCKS5 proxy to be

-     * blacklisted. When a proxy is blacklisted no more connection attempts will be made to it for a

-     * period of 2 hours. Default is 2.

-     * 

-     * @return the number of connection failures it takes for a particular SOCKS5 proxy to be

-     *         blacklisted

-     */

-    public static int getConnectFailureThreshold() {

-        return CONNECTION_FAILURE_THRESHOLD;

-    }

-

-    /**

-     * Sets the number of connection failures it takes for a particular SOCKS5 proxy to be

-     * blacklisted. When a proxy is blacklisted no more connection attempts will be made to it for a

-     * period of 2 hours. Default is 2.

-     * <p>

-     * Setting the connection failure threshold to zero disables the blacklisting.

-     * 

-     * @param connectFailureThreshold the number of connection failures it takes for a particular

-     *        SOCKS5 proxy to be blacklisted

-     */

-    public static void setConnectFailureThreshold(int connectFailureThreshold) {

-        CONNECTION_FAILURE_THRESHOLD = connectFailureThreshold;

-    }

-

-    /**

-     * Creates a new Socks5BytestreamRequest.

-     * 

-     * @param manager the SOCKS5 Bytestream manager

-     * @param bytestreamRequest the SOCKS5 Bytestream initialization packet

-     */

-    protected Socks5BytestreamRequest(Socks5BytestreamManager manager, Bytestream bytestreamRequest) {

-        this.manager = manager;

-        this.bytestreamRequest = bytestreamRequest;

-    }

-

-    /**

-     * Returns the maximum timeout to connect to SOCKS5 proxies. Default is 10000ms.

-     * <p>

-     * When accepting a SOCKS5 Bytestream request Smack tries to connect to all SOCKS5 proxies given

-     * by the initiator until a connection is established. This timeout divided by the number of

-     * SOCKS5 proxies determines the timeout for every connection attempt.

-     * <p>

-     * You can set the minimum timeout for establishing a connection to one SOCKS5 proxy by invoking

-     * {@link #setMinimumConnectTimeout(int)}.

-     * 

-     * @return the maximum timeout to connect to SOCKS5 proxies

-     */

-    public int getTotalConnectTimeout() {

-        if (this.totalConnectTimeout <= 0) {

-            return 10000;

-        }

-        return this.totalConnectTimeout;

-    }

-

-    /**

-     * Sets the maximum timeout to connect to SOCKS5 proxies. Default is 10000ms.

-     * <p>

-     * When accepting a SOCKS5 Bytestream request Smack tries to connect to all SOCKS5 proxies given

-     * by the initiator until a connection is established. This timeout divided by the number of

-     * SOCKS5 proxies determines the timeout for every connection attempt.

-     * <p>

-     * You can set the minimum timeout for establishing a connection to one SOCKS5 proxy by invoking

-     * {@link #setMinimumConnectTimeout(int)}.

-     * 

-     * @param totalConnectTimeout the maximum timeout to connect to SOCKS5 proxies

-     */

-    public void setTotalConnectTimeout(int totalConnectTimeout) {

-        this.totalConnectTimeout = totalConnectTimeout;

-    }

-

-    /**

-     * Returns the timeout to connect to one SOCKS5 proxy while accepting the SOCKS5 Bytestream

-     * request. Default is 2000ms.

-     * 

-     * @return the timeout to connect to one SOCKS5 proxy

-     */

-    public int getMinimumConnectTimeout() {

-        if (this.minimumConnectTimeout <= 0) {

-            return 2000;

-        }

-        return this.minimumConnectTimeout;

-    }

-

-    /**

-     * Sets the timeout to connect to one SOCKS5 proxy while accepting the SOCKS5 Bytestream

-     * request. Default is 2000ms.

-     * 

-     * @param minimumConnectTimeout the timeout to connect to one SOCKS5 proxy

-     */

-    public void setMinimumConnectTimeout(int minimumConnectTimeout) {

-        this.minimumConnectTimeout = minimumConnectTimeout;

-    }

-

-    /**

-     * Returns the sender of the SOCKS5 Bytestream initialization request.

-     * 

-     * @return the sender of the SOCKS5 Bytestream initialization request.

-     */

-    public String getFrom() {

-        return this.bytestreamRequest.getFrom();

-    }

-

-    /**

-     * Returns the session ID of the SOCKS5 Bytestream initialization request.

-     * 

-     * @return the session ID of the SOCKS5 Bytestream initialization request.

-     */

-    public String getSessionID() {

-        return this.bytestreamRequest.getSessionID();

-    }

-

-    /**

-     * Accepts the SOCKS5 Bytestream initialization request and returns the socket to send/receive

-     * data.

-     * <p>

-     * Before accepting the SOCKS5 Bytestream request you can set timeouts by invoking

-     * {@link #setTotalConnectTimeout(int)} and {@link #setMinimumConnectTimeout(int)}.

-     * 

-     * @return the socket to send/receive data

-     * @throws XMPPException if connection to all SOCKS5 proxies failed or if stream is invalid.

-     * @throws InterruptedException if the current thread was interrupted while waiting

-     */

-    public Socks5BytestreamSession accept() throws XMPPException, InterruptedException {

-        Collection<StreamHost> streamHosts = this.bytestreamRequest.getStreamHosts();

-

-        // throw exceptions if request contains no stream hosts

-        if (streamHosts.size() == 0) {

-            cancelRequest();

-        }

-

-        StreamHost selectedHost = null;

-        Socket socket = null;

-

-        String digest = Socks5Utils.createDigest(this.bytestreamRequest.getSessionID(),

-                        this.bytestreamRequest.getFrom(), this.manager.getConnection().getUser());

-

-        /*

-         * determine timeout for each connection attempt; each SOCKS5 proxy has the same amount of

-         * time so that the first does not consume the whole timeout

-         */

-        int timeout = Math.max(getTotalConnectTimeout() / streamHosts.size(),

-                        getMinimumConnectTimeout());

-

-        for (StreamHost streamHost : streamHosts) {

-            String address = streamHost.getAddress() + ":" + streamHost.getPort();

-

-            // check to see if this address has been blacklisted

-            int failures = getConnectionFailures(address);

-            if (CONNECTION_FAILURE_THRESHOLD > 0 && failures >= CONNECTION_FAILURE_THRESHOLD) {

-                continue;

-            }

-

-            // establish socket

-            try {

-

-                // build SOCKS5 client

-                final Socks5Client socks5Client = new Socks5Client(streamHost, digest);

-

-                // connect to SOCKS5 proxy with a timeout

-                socket = socks5Client.getSocket(timeout);

-

-                // set selected host

-                selectedHost = streamHost;

-                break;

-

-            }

-            catch (TimeoutException e) {

-                incrementConnectionFailures(address);

-            }

-            catch (IOException e) {

-                incrementConnectionFailures(address);

-            }

-            catch (XMPPException e) {

-                incrementConnectionFailures(address);

-            }

-

-        }

-

-        // throw exception if connecting to all SOCKS5 proxies failed

-        if (selectedHost == null || socket == null) {

-            cancelRequest();

-        }

-

-        // send used-host confirmation

-        Bytestream response = createUsedHostResponse(selectedHost);

-        this.manager.getConnection().sendPacket(response);

-

-        return new Socks5BytestreamSession(socket, selectedHost.getJID().equals(

-                        this.bytestreamRequest.getFrom()));

-

-    }

-

-    /**

-     * Rejects the SOCKS5 Bytestream request by sending a reject error to the initiator.

-     */

-    public void reject() {

-        this.manager.replyRejectPacket(this.bytestreamRequest);

-    }

-

-    /**

-     * Cancels the SOCKS5 Bytestream request by sending an error to the initiator and building a

-     * XMPP exception.

-     * 

-     * @throws XMPPException XMPP exception containing the XMPP error

-     */

-    private void cancelRequest() throws XMPPException {

-        String errorMessage = "Could not establish socket with any provided host";

-        XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, errorMessage);

-        IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error);

-        this.manager.getConnection().sendPacket(errorIQ);

-        throw new XMPPException(errorMessage, error);

-    }

-

-    /**

-     * Returns the response to the SOCKS5 Bytestream request containing the SOCKS5 proxy used.

-     * 

-     * @param selectedHost the used SOCKS5 proxy

-     * @return the response to the SOCKS5 Bytestream request

-     */

-    private Bytestream createUsedHostResponse(StreamHost selectedHost) {

-        Bytestream response = new Bytestream(this.bytestreamRequest.getSessionID());

-        response.setTo(this.bytestreamRequest.getFrom());

-        response.setType(IQ.Type.RESULT);

-        response.setPacketID(this.bytestreamRequest.getPacketID());

-        response.setUsedHost(selectedHost.getJID());

-        return response;

-    }

-

-    /**

-     * Increments the connection failure counter by one for the given address.

-     * 

-     * @param address the address the connection failure counter should be increased

-     */

-    private void incrementConnectionFailures(String address) {

-        Integer count = ADDRESS_BLACKLIST.get(address);

-        ADDRESS_BLACKLIST.put(address, count == null ? 1 : count + 1);

-    }

-

-    /**

-     * Returns how often the connection to the given address failed.

-     * 

-     * @param address the address

-     * @return number of connection failures

-     */

-    private int getConnectionFailures(String address) {

-        Integer count = ADDRESS_BLACKLIST.get(address);

-        return count != null ? count : 0;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Collection;
+import java.util.concurrent.TimeoutException;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.Cache;
+import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
+
+/**
+ * Socks5BytestreamRequest class handles incoming SOCKS5 Bytestream requests.
+ * 
+ * @author Henning Staib
+ */
+public class Socks5BytestreamRequest implements BytestreamRequest {
+
+    /* lifetime of an Item in the blacklist */
+    private static final long BLACKLIST_LIFETIME = 60 * 1000 * 120;
+
+    /* size of the blacklist */
+    private static final int BLACKLIST_MAX_SIZE = 100;
+
+    /* blacklist of addresses of SOCKS5 proxies */
+    private static final Cache<String, Integer> ADDRESS_BLACKLIST = new Cache<String, Integer>(
+                    BLACKLIST_MAX_SIZE, BLACKLIST_LIFETIME);
+
+    /*
+     * The number of connection failures it takes for a particular SOCKS5 proxy to be blacklisted.
+     * When a proxy is blacklisted no more connection attempts will be made to it for a period of 2
+     * hours.
+     */
+    private static int CONNECTION_FAILURE_THRESHOLD = 2;
+
+    /* the bytestream initialization request */
+    private Bytestream bytestreamRequest;
+
+    /* SOCKS5 Bytestream manager containing the XMPP connection and helper methods */
+    private Socks5BytestreamManager manager;
+
+    /* timeout to connect to all SOCKS5 proxies */
+    private int totalConnectTimeout = 10000;
+
+    /* minimum timeout to connect to one SOCKS5 proxy */
+    private int minimumConnectTimeout = 2000;
+
+    /**
+     * Returns the number of connection failures it takes for a particular SOCKS5 proxy to be
+     * blacklisted. When a proxy is blacklisted no more connection attempts will be made to it for a
+     * period of 2 hours. Default is 2.
+     * 
+     * @return the number of connection failures it takes for a particular SOCKS5 proxy to be
+     *         blacklisted
+     */
+    public static int getConnectFailureThreshold() {
+        return CONNECTION_FAILURE_THRESHOLD;
+    }
+
+    /**
+     * Sets the number of connection failures it takes for a particular SOCKS5 proxy to be
+     * blacklisted. When a proxy is blacklisted no more connection attempts will be made to it for a
+     * period of 2 hours. Default is 2.
+     * <p>
+     * Setting the connection failure threshold to zero disables the blacklisting.
+     * 
+     * @param connectFailureThreshold the number of connection failures it takes for a particular
+     *        SOCKS5 proxy to be blacklisted
+     */
+    public static void setConnectFailureThreshold(int connectFailureThreshold) {
+        CONNECTION_FAILURE_THRESHOLD = connectFailureThreshold;
+    }
+
+    /**
+     * Creates a new Socks5BytestreamRequest.
+     * 
+     * @param manager the SOCKS5 Bytestream manager
+     * @param bytestreamRequest the SOCKS5 Bytestream initialization packet
+     */
+    protected Socks5BytestreamRequest(Socks5BytestreamManager manager, Bytestream bytestreamRequest) {
+        this.manager = manager;
+        this.bytestreamRequest = bytestreamRequest;
+    }
+
+    /**
+     * Returns the maximum timeout to connect to SOCKS5 proxies. Default is 10000ms.
+     * <p>
+     * When accepting a SOCKS5 Bytestream request Smack tries to connect to all SOCKS5 proxies given
+     * by the initiator until a connection is established. This timeout divided by the number of
+     * SOCKS5 proxies determines the timeout for every connection attempt.
+     * <p>
+     * You can set the minimum timeout for establishing a connection to one SOCKS5 proxy by invoking
+     * {@link #setMinimumConnectTimeout(int)}.
+     * 
+     * @return the maximum timeout to connect to SOCKS5 proxies
+     */
+    public int getTotalConnectTimeout() {
+        if (this.totalConnectTimeout <= 0) {
+            return 10000;
+        }
+        return this.totalConnectTimeout;
+    }
+
+    /**
+     * Sets the maximum timeout to connect to SOCKS5 proxies. Default is 10000ms.
+     * <p>
+     * When accepting a SOCKS5 Bytestream request Smack tries to connect to all SOCKS5 proxies given
+     * by the initiator until a connection is established. This timeout divided by the number of
+     * SOCKS5 proxies determines the timeout for every connection attempt.
+     * <p>
+     * You can set the minimum timeout for establishing a connection to one SOCKS5 proxy by invoking
+     * {@link #setMinimumConnectTimeout(int)}.
+     * 
+     * @param totalConnectTimeout the maximum timeout to connect to SOCKS5 proxies
+     */
+    public void setTotalConnectTimeout(int totalConnectTimeout) {
+        this.totalConnectTimeout = totalConnectTimeout;
+    }
+
+    /**
+     * Returns the timeout to connect to one SOCKS5 proxy while accepting the SOCKS5 Bytestream
+     * request. Default is 2000ms.
+     * 
+     * @return the timeout to connect to one SOCKS5 proxy
+     */
+    public int getMinimumConnectTimeout() {
+        if (this.minimumConnectTimeout <= 0) {
+            return 2000;
+        }
+        return this.minimumConnectTimeout;
+    }
+
+    /**
+     * Sets the timeout to connect to one SOCKS5 proxy while accepting the SOCKS5 Bytestream
+     * request. Default is 2000ms.
+     * 
+     * @param minimumConnectTimeout the timeout to connect to one SOCKS5 proxy
+     */
+    public void setMinimumConnectTimeout(int minimumConnectTimeout) {
+        this.minimumConnectTimeout = minimumConnectTimeout;
+    }
+
+    /**
+     * Returns the sender of the SOCKS5 Bytestream initialization request.
+     * 
+     * @return the sender of the SOCKS5 Bytestream initialization request.
+     */
+    public String getFrom() {
+        return this.bytestreamRequest.getFrom();
+    }
+
+    /**
+     * Returns the session ID of the SOCKS5 Bytestream initialization request.
+     * 
+     * @return the session ID of the SOCKS5 Bytestream initialization request.
+     */
+    public String getSessionID() {
+        return this.bytestreamRequest.getSessionID();
+    }
+
+    /**
+     * Accepts the SOCKS5 Bytestream initialization request and returns the socket to send/receive
+     * data.
+     * <p>
+     * Before accepting the SOCKS5 Bytestream request you can set timeouts by invoking
+     * {@link #setTotalConnectTimeout(int)} and {@link #setMinimumConnectTimeout(int)}.
+     * 
+     * @return the socket to send/receive data
+     * @throws XMPPException if connection to all SOCKS5 proxies failed or if stream is invalid.
+     * @throws InterruptedException if the current thread was interrupted while waiting
+     */
+    public Socks5BytestreamSession accept() throws XMPPException, InterruptedException {
+        Collection<StreamHost> streamHosts = this.bytestreamRequest.getStreamHosts();
+
+        // throw exceptions if request contains no stream hosts
+        if (streamHosts.size() == 0) {
+            cancelRequest();
+        }
+
+        StreamHost selectedHost = null;
+        Socket socket = null;
+
+        String digest = Socks5Utils.createDigest(this.bytestreamRequest.getSessionID(),
+                        this.bytestreamRequest.getFrom(), this.manager.getConnection().getUser());
+
+        /*
+         * determine timeout for each connection attempt; each SOCKS5 proxy has the same amount of
+         * time so that the first does not consume the whole timeout
+         */
+        int timeout = Math.max(getTotalConnectTimeout() / streamHosts.size(),
+                        getMinimumConnectTimeout());
+
+        for (StreamHost streamHost : streamHosts) {
+            String address = streamHost.getAddress() + ":" + streamHost.getPort();
+
+            // check to see if this address has been blacklisted
+            int failures = getConnectionFailures(address);
+            if (CONNECTION_FAILURE_THRESHOLD > 0 && failures >= CONNECTION_FAILURE_THRESHOLD) {
+                continue;
+            }
+
+            // establish socket
+            try {
+
+                // build SOCKS5 client
+                final Socks5Client socks5Client = new Socks5Client(streamHost, digest);
+
+                // connect to SOCKS5 proxy with a timeout
+                socket = socks5Client.getSocket(timeout);
+
+                // set selected host
+                selectedHost = streamHost;
+                break;
+
+            }
+            catch (TimeoutException e) {
+                incrementConnectionFailures(address);
+            }
+            catch (IOException e) {
+                incrementConnectionFailures(address);
+            }
+            catch (XMPPException e) {
+                incrementConnectionFailures(address);
+            }
+
+        }
+
+        // throw exception if connecting to all SOCKS5 proxies failed
+        if (selectedHost == null || socket == null) {
+            cancelRequest();
+        }
+
+        // send used-host confirmation
+        Bytestream response = createUsedHostResponse(selectedHost);
+        this.manager.getConnection().sendPacket(response);
+
+        return new Socks5BytestreamSession(socket, selectedHost.getJID().equals(
+                        this.bytestreamRequest.getFrom()));
+
+    }
+
+    /**
+     * Rejects the SOCKS5 Bytestream request by sending a reject error to the initiator.
+     */
+    public void reject() {
+        this.manager.replyRejectPacket(this.bytestreamRequest);
+    }
+
+    /**
+     * Cancels the SOCKS5 Bytestream request by sending an error to the initiator and building a
+     * XMPP exception.
+     * 
+     * @throws XMPPException XMPP exception containing the XMPP error
+     */
+    private void cancelRequest() throws XMPPException {
+        String errorMessage = "Could not establish socket with any provided host";
+        XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, errorMessage);
+        IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error);
+        this.manager.getConnection().sendPacket(errorIQ);
+        throw new XMPPException(errorMessage, error);
+    }
+
+    /**
+     * Returns the response to the SOCKS5 Bytestream request containing the SOCKS5 proxy used.
+     * 
+     * @param selectedHost the used SOCKS5 proxy
+     * @return the response to the SOCKS5 Bytestream request
+     */
+    private Bytestream createUsedHostResponse(StreamHost selectedHost) {
+        Bytestream response = new Bytestream(this.bytestreamRequest.getSessionID());
+        response.setTo(this.bytestreamRequest.getFrom());
+        response.setType(IQ.Type.RESULT);
+        response.setPacketID(this.bytestreamRequest.getPacketID());
+        response.setUsedHost(selectedHost.getJID());
+        return response;
+    }
+
+    /**
+     * Increments the connection failure counter by one for the given address.
+     * 
+     * @param address the address the connection failure counter should be increased
+     */
+    private void incrementConnectionFailures(String address) {
+        Integer count = ADDRESS_BLACKLIST.get(address);
+        ADDRESS_BLACKLIST.put(address, count == null ? 1 : count + 1);
+    }
+
+    /**
+     * Returns how often the connection to the given address failed.
+     * 
+     * @param address the address
+     * @return number of connection failures
+     */
+    private int getConnectionFailures(String address) {
+        Integer count = ADDRESS_BLACKLIST.get(address);
+        return count != null ? count : 0;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java
index 41ab142..267dafc 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java
@@ -15,84 +15,84 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx.bytestreams.socks5;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.net.Socket;

-import java.net.SocketException;

-

-import org.jivesoftware.smackx.bytestreams.BytestreamSession;

-

-/**

- * Socks5BytestreamSession class represents a SOCKS5 Bytestream session.

- * 

- * @author Henning Staib

- */

-public class Socks5BytestreamSession implements BytestreamSession {

-

-    /* the underlying socket of the SOCKS5 Bytestream */

-    private final Socket socket;

-

-    /* flag to indicate if this session is a direct or mediated connection */

-    private final boolean isDirect;

-

-    protected Socks5BytestreamSession(Socket socket, boolean isDirect) {

-        this.socket = socket;

-        this.isDirect = isDirect;

-    }

-

-    /**

-     * Returns <code>true</code> if the session is established through a direct connection between

-     * the initiator and target, <code>false</code> if the session is mediated over a SOCKS proxy.

-     * 

-     * @return <code>true</code> if session is a direct connection, <code>false</code> if session is

-     *         mediated over a SOCKS5 proxy

-     */

-    public boolean isDirect() {

-        return this.isDirect;

-    }

-

-    /**

-     * Returns <code>true</code> if the session is mediated over a SOCKS proxy, <code>false</code>

-     * if this session is established through a direct connection between the initiator and target.

-     * 

-     * @return <code>true</code> if session is mediated over a SOCKS5 proxy, <code>false</code> if

-     *         session is a direct connection

-     */

-    public boolean isMediated() {

-        return !this.isDirect;

-    }

-

-    public InputStream getInputStream() throws IOException {

-        return this.socket.getInputStream();

-    }

-

-    public OutputStream getOutputStream() throws IOException {

-        return this.socket.getOutputStream();

-    }

-

-    public int getReadTimeout() throws IOException {

-        try {

-            return this.socket.getSoTimeout();

-        }

-        catch (SocketException e) {

-            throw new IOException("Error on underlying Socket");

-        }

-    }

-

-    public void setReadTimeout(int timeout) throws IOException {

-        try {

-            this.socket.setSoTimeout(timeout);

-        }

-        catch (SocketException e) {

-            throw new IOException("Error on underlying Socket");

-        }

-    }

-

-    public void close() throws IOException {

-        this.socket.close();

-    }

-

-}

+package org.jivesoftware.smackx.bytestreams.socks5;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.jivesoftware.smackx.bytestreams.BytestreamSession;
+
+/**
+ * Socks5BytestreamSession class represents a SOCKS5 Bytestream session.
+ * 
+ * @author Henning Staib
+ */
+public class Socks5BytestreamSession implements BytestreamSession {
+
+    /* the underlying socket of the SOCKS5 Bytestream */
+    private final Socket socket;
+
+    /* flag to indicate if this session is a direct or mediated connection */
+    private final boolean isDirect;
+
+    protected Socks5BytestreamSession(Socket socket, boolean isDirect) {
+        this.socket = socket;
+        this.isDirect = isDirect;
+    }
+
+    /**
+     * Returns <code>true</code> if the session is established through a direct connection between
+     * the initiator and target, <code>false</code> if the session is mediated over a SOCKS proxy.
+     * 
+     * @return <code>true</code> if session is a direct connection, <code>false</code> if session is
+     *         mediated over a SOCKS5 proxy
+     */
+    public boolean isDirect() {
+        return this.isDirect;
+    }
+
+    /**
+     * Returns <code>true</code> if the session is mediated over a SOCKS proxy, <code>false</code>
+     * if this session is established through a direct connection between the initiator and target.
+     * 
+     * @return <code>true</code> if session is mediated over a SOCKS5 proxy, <code>false</code> if
+     *         session is a direct connection
+     */
+    public boolean isMediated() {
+        return !this.isDirect;
+    }
+
+    public InputStream getInputStream() throws IOException {
+        return this.socket.getInputStream();
+    }
+
+    public OutputStream getOutputStream() throws IOException {
+        return this.socket.getOutputStream();
+    }
+
+    public int getReadTimeout() throws IOException {
+        try {
+            return this.socket.getSoTimeout();
+        }
+        catch (SocketException e) {
+            throw new IOException("Error on underlying Socket");
+        }
+    }
+
+    public void setReadTimeout(int timeout) throws IOException {
+        try {
+            this.socket.setSoTimeout(timeout);
+        }
+        catch (SocketException e) {
+            throw new IOException("Error on underlying Socket");
+        }
+    }
+
+    public void close() throws IOException {
+        this.socket.close();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java
index 664ea59..62c925a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java
@@ -1,204 +1,204 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.DataInputStream;

-import java.io.DataOutputStream;

-import java.io.IOException;

-import java.net.InetSocketAddress;

-import java.net.Socket;

-import java.net.SocketAddress;

-import java.util.Arrays;

-import java.util.concurrent.Callable;

-import java.util.concurrent.ExecutionException;

-import java.util.concurrent.FutureTask;

-import java.util.concurrent.TimeUnit;

-import java.util.concurrent.TimeoutException;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;

-

-/**

- * The SOCKS5 client class handles establishing a connection to a SOCKS5 proxy. Connecting to a

- * SOCKS5 proxy requires authentication. This implementation only supports the no-authentication

- * authentication method.

- * 

- * @author Henning Staib

- */

-class Socks5Client {

-

-    /* stream host containing network settings and name of the SOCKS5 proxy */

-    protected StreamHost streamHost;

-

-    /* SHA-1 digest identifying the SOCKS5 stream */

-    protected String digest;

-

-    /**

-     * Constructor for a SOCKS5 client.

-     * 

-     * @param streamHost containing network settings of the SOCKS5 proxy

-     * @param digest identifying the SOCKS5 Bytestream

-     */

-    public Socks5Client(StreamHost streamHost, String digest) {

-        this.streamHost = streamHost;

-        this.digest = digest;

-    }

-

-    /**

-     * Returns the initialized socket that can be used to transfer data between peers via the SOCKS5

-     * proxy.

-     * 

-     * @param timeout timeout to connect to SOCKS5 proxy in milliseconds

-     * @return socket the initialized socket

-     * @throws IOException if initializing the socket failed due to a network error

-     * @throws XMPPException if establishing connection to SOCKS5 proxy failed

-     * @throws TimeoutException if connecting to SOCKS5 proxy timed out

-     * @throws InterruptedException if the current thread was interrupted while waiting

-     */

-    public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException,

-                    TimeoutException {

-

-        // wrap connecting in future for timeout

-        FutureTask<Socket> futureTask = new FutureTask<Socket>(new Callable<Socket>() {

-

-            public Socket call() throws Exception {

-

-                // initialize socket

-                Socket socket = new Socket();

-                SocketAddress socketAddress = new InetSocketAddress(streamHost.getAddress(),

-                                streamHost.getPort());

-                socket.connect(socketAddress);

-

-                // initialize connection to SOCKS5 proxy

-                if (!establish(socket)) {

-

-                    // initialization failed, close socket

-                    socket.close();

-                    throw new XMPPException("establishing connection to SOCKS5 proxy failed");

-

-                }

-

-                return socket;

-            }

-

-        });

-        Thread executor = new Thread(futureTask);

-        executor.start();

-

-        // get connection to initiator with timeout

-        try {

-            return futureTask.get(timeout, TimeUnit.MILLISECONDS);

-        }

-        catch (ExecutionException e) {

-            Throwable cause = e.getCause();

-            if (cause != null) {

-                // case exceptions to comply with method signature

-                if (cause instanceof IOException) {

-                    throw (IOException) cause;

-                }

-                if (cause instanceof XMPPException) {

-                    throw (XMPPException) cause;

-                }

-            }

-

-            // throw generic IO exception if unexpected exception was thrown

-            throw new IOException("Error while connection to SOCKS5 proxy");

-        }

-

-    }

-

-    /**

-     * Initializes the connection to the SOCKS5 proxy by negotiating authentication method and

-     * requesting a stream for the given digest. Currently only the no-authentication method is

-     * supported by the Socks5Client.

-     * <p>

-     * Returns <code>true</code> if a stream could be established, otherwise <code>false</code>. If

-     * <code>false</code> is returned the given Socket should be closed.

-     * 

-     * @param socket connected to a SOCKS5 proxy

-     * @return <code>true</code> if if a stream could be established, otherwise <code>false</code>.

-     *         If <code>false</code> is returned the given Socket should be closed.

-     * @throws IOException if a network error occurred

-     */

-    protected boolean establish(Socket socket) throws IOException {

-

-        /*

-         * use DataInputStream/DataOutpuStream to assure read and write is completed in a single

-         * statement

-         */

-        DataInputStream in = new DataInputStream(socket.getInputStream());

-        DataOutputStream out = new DataOutputStream(socket.getOutputStream());

-

-        // authentication negotiation

-        byte[] cmd = new byte[3];

-

-        cmd[0] = (byte) 0x05; // protocol version 5

-        cmd[1] = (byte) 0x01; // number of authentication methods supported

-        cmd[2] = (byte) 0x00; // authentication method: no-authentication required

-

-        out.write(cmd);

-        out.flush();

-

-        byte[] response = new byte[2];

-        in.readFully(response);

-

-        // check if server responded with correct version and no-authentication method

-        if (response[0] != (byte) 0x05 || response[1] != (byte) 0x00) {

-            return false;

-        }

-

-        // request SOCKS5 connection with given address/digest

-        byte[] connectionRequest = createSocks5ConnectRequest();

-        out.write(connectionRequest);

-        out.flush();

-

-        // receive response

-        byte[] connectionResponse;

-        try {

-            connectionResponse = Socks5Utils.receiveSocks5Message(in);

-        }

-        catch (XMPPException e) {

-            return false; // server answered in an unsupported way

-        }

-

-        // verify response

-        connectionRequest[1] = (byte) 0x00; // set expected return status to 0

-        return Arrays.equals(connectionRequest, connectionResponse);

-    }

-

-    /**

-     * Returns a SOCKS5 connection request message. It contains the command "connect", the address

-     * type "domain" and the digest as address.

-     * <p>

-     * (see <a href="http://tools.ietf.org/html/rfc1928">RFC1928</a>)

-     * 

-     * @return SOCKS5 connection request message

-     */

-    private byte[] createSocks5ConnectRequest() {

-        byte addr[] = this.digest.getBytes();

-

-        byte[] data = new byte[7 + addr.length];

-        data[0] = (byte) 0x05; // version (SOCKS5)

-        data[1] = (byte) 0x01; // command (1 - connect)

-        data[2] = (byte) 0x00; // reserved byte (always 0)

-        data[3] = (byte) 0x03; // address type (3 - domain name)

-        data[4] = (byte) addr.length; // address length

-        System.arraycopy(addr, 0, data, 5, addr.length); // address

-        data[data.length - 2] = (byte) 0; // address port (2 bytes always 0)

-        data[data.length - 1] = (byte) 0;

-

-        return data;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
+
+/**
+ * The SOCKS5 client class handles establishing a connection to a SOCKS5 proxy. Connecting to a
+ * SOCKS5 proxy requires authentication. This implementation only supports the no-authentication
+ * authentication method.
+ * 
+ * @author Henning Staib
+ */
+class Socks5Client {
+
+    /* stream host containing network settings and name of the SOCKS5 proxy */
+    protected StreamHost streamHost;
+
+    /* SHA-1 digest identifying the SOCKS5 stream */
+    protected String digest;
+
+    /**
+     * Constructor for a SOCKS5 client.
+     * 
+     * @param streamHost containing network settings of the SOCKS5 proxy
+     * @param digest identifying the SOCKS5 Bytestream
+     */
+    public Socks5Client(StreamHost streamHost, String digest) {
+        this.streamHost = streamHost;
+        this.digest = digest;
+    }
+
+    /**
+     * Returns the initialized socket that can be used to transfer data between peers via the SOCKS5
+     * proxy.
+     * 
+     * @param timeout timeout to connect to SOCKS5 proxy in milliseconds
+     * @return socket the initialized socket
+     * @throws IOException if initializing the socket failed due to a network error
+     * @throws XMPPException if establishing connection to SOCKS5 proxy failed
+     * @throws TimeoutException if connecting to SOCKS5 proxy timed out
+     * @throws InterruptedException if the current thread was interrupted while waiting
+     */
+    public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException,
+                    TimeoutException {
+
+        // wrap connecting in future for timeout
+        FutureTask<Socket> futureTask = new FutureTask<Socket>(new Callable<Socket>() {
+
+            public Socket call() throws Exception {
+
+                // initialize socket
+                Socket socket = new Socket();
+                SocketAddress socketAddress = new InetSocketAddress(streamHost.getAddress(),
+                                streamHost.getPort());
+                socket.connect(socketAddress);
+
+                // initialize connection to SOCKS5 proxy
+                if (!establish(socket)) {
+
+                    // initialization failed, close socket
+                    socket.close();
+                    throw new XMPPException("establishing connection to SOCKS5 proxy failed");
+
+                }
+
+                return socket;
+            }
+
+        });
+        Thread executor = new Thread(futureTask);
+        executor.start();
+
+        // get connection to initiator with timeout
+        try {
+            return futureTask.get(timeout, TimeUnit.MILLISECONDS);
+        }
+        catch (ExecutionException e) {
+            Throwable cause = e.getCause();
+            if (cause != null) {
+                // case exceptions to comply with method signature
+                if (cause instanceof IOException) {
+                    throw (IOException) cause;
+                }
+                if (cause instanceof XMPPException) {
+                    throw (XMPPException) cause;
+                }
+            }
+
+            // throw generic IO exception if unexpected exception was thrown
+            throw new IOException("Error while connection to SOCKS5 proxy");
+        }
+
+    }
+
+    /**
+     * Initializes the connection to the SOCKS5 proxy by negotiating authentication method and
+     * requesting a stream for the given digest. Currently only the no-authentication method is
+     * supported by the Socks5Client.
+     * <p>
+     * Returns <code>true</code> if a stream could be established, otherwise <code>false</code>. If
+     * <code>false</code> is returned the given Socket should be closed.
+     * 
+     * @param socket connected to a SOCKS5 proxy
+     * @return <code>true</code> if if a stream could be established, otherwise <code>false</code>.
+     *         If <code>false</code> is returned the given Socket should be closed.
+     * @throws IOException if a network error occurred
+     */
+    protected boolean establish(Socket socket) throws IOException {
+
+        /*
+         * use DataInputStream/DataOutpuStream to assure read and write is completed in a single
+         * statement
+         */
+        DataInputStream in = new DataInputStream(socket.getInputStream());
+        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+        // authentication negotiation
+        byte[] cmd = new byte[3];
+
+        cmd[0] = (byte) 0x05; // protocol version 5
+        cmd[1] = (byte) 0x01; // number of authentication methods supported
+        cmd[2] = (byte) 0x00; // authentication method: no-authentication required
+
+        out.write(cmd);
+        out.flush();
+
+        byte[] response = new byte[2];
+        in.readFully(response);
+
+        // check if server responded with correct version and no-authentication method
+        if (response[0] != (byte) 0x05 || response[1] != (byte) 0x00) {
+            return false;
+        }
+
+        // request SOCKS5 connection with given address/digest
+        byte[] connectionRequest = createSocks5ConnectRequest();
+        out.write(connectionRequest);
+        out.flush();
+
+        // receive response
+        byte[] connectionResponse;
+        try {
+            connectionResponse = Socks5Utils.receiveSocks5Message(in);
+        }
+        catch (XMPPException e) {
+            return false; // server answered in an unsupported way
+        }
+
+        // verify response
+        connectionRequest[1] = (byte) 0x00; // set expected return status to 0
+        return Arrays.equals(connectionRequest, connectionResponse);
+    }
+
+    /**
+     * Returns a SOCKS5 connection request message. It contains the command "connect", the address
+     * type "domain" and the digest as address.
+     * <p>
+     * (see <a href="http://tools.ietf.org/html/rfc1928">RFC1928</a>)
+     * 
+     * @return SOCKS5 connection request message
+     */
+    private byte[] createSocks5ConnectRequest() {
+        byte addr[] = this.digest.getBytes();
+
+        byte[] data = new byte[7 + addr.length];
+        data[0] = (byte) 0x05; // version (SOCKS5)
+        data[1] = (byte) 0x01; // command (1 - connect)
+        data[2] = (byte) 0x00; // reserved byte (always 0)
+        data[3] = (byte) 0x03; // address type (3 - domain name)
+        data[4] = (byte) addr.length; // address length
+        System.arraycopy(addr, 0, data, 5, addr.length); // address
+        data[data.length - 2] = (byte) 0; // address port (2 bytes always 0)
+        data[data.length - 1] = (byte) 0;
+
+        return data;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java
index 0d90791..94a7ac7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java
@@ -1,117 +1,117 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.IOException;

-import java.net.Socket;

-import java.util.concurrent.TimeoutException;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.util.SyncPacketSend;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;

-

-/**

- * Implementation of a SOCKS5 client used on the initiators side. This is needed because connecting

- * to the local SOCKS5 proxy differs form the regular way to connect to a SOCKS5 proxy. Additionally

- * a remote SOCKS5 proxy has to be activated by the initiator before data can be transferred between

- * the peers.

- * 

- * @author Henning Staib

- */

-class Socks5ClientForInitiator extends Socks5Client {

-

-    /* the XMPP connection used to communicate with the SOCKS5 proxy */

-    private Connection connection;

-

-    /* the session ID used to activate SOCKS5 stream */

-    private String sessionID;

-

-    /* the target JID used to activate SOCKS5 stream */

-    private String target;

-

-    /**

-     * Creates a new SOCKS5 client for the initiators side.

-     * 

-     * @param streamHost containing network settings of the SOCKS5 proxy

-     * @param digest identifying the SOCKS5 Bytestream

-     * @param connection the XMPP connection

-     * @param sessionID the session ID of the SOCKS5 Bytestream

-     * @param target the target JID of the SOCKS5 Bytestream

-     */

-    public Socks5ClientForInitiator(StreamHost streamHost, String digest, Connection connection,

-                    String sessionID, String target) {

-        super(streamHost, digest);

-        this.connection = connection;

-        this.sessionID = sessionID;

-        this.target = target;

-    }

-

-    public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException,

-                    TimeoutException {

-        Socket socket = null;

-

-        // check if stream host is the local SOCKS5 proxy

-        if (this.streamHost.getJID().equals(this.connection.getUser())) {

-            Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();

-            socket = socks5Server.getSocket(this.digest);

-            if (socket == null) {

-                throw new XMPPException("target is not connected to SOCKS5 proxy");

-            }

-        }

-        else {

-            socket = super.getSocket(timeout);

-

-            try {

-                activate();

-            }

-            catch (XMPPException e) {

-                socket.close();

-                throw new XMPPException("activating SOCKS5 Bytestream failed", e);

-            }

-

-        }

-

-        return socket;

-    }

-

-    /**

-     * Activates the SOCKS5 Bytestream by sending a XMPP SOCKS5 Bytestream activation packet to the

-     * SOCKS5 proxy.

-     */

-    private void activate() throws XMPPException {

-        Bytestream activate = createStreamHostActivation();

-        // if activation fails #getReply throws an exception

-        SyncPacketSend.getReply(this.connection, activate);

-    }

-

-    /**

-     * Returns a SOCKS5 Bytestream activation packet.

-     * 

-     * @return SOCKS5 Bytestream activation packet

-     */

-    private Bytestream createStreamHostActivation() {

-        Bytestream activate = new Bytestream(this.sessionID);

-        activate.setMode(null);

-        activate.setType(IQ.Type.SET);

-        activate.setTo(this.streamHost.getJID());

-

-        activate.setToActivate(this.target);

-

-        return activate;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.concurrent.TimeoutException;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.util.SyncPacketSend;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
+
+/**
+ * Implementation of a SOCKS5 client used on the initiators side. This is needed because connecting
+ * to the local SOCKS5 proxy differs form the regular way to connect to a SOCKS5 proxy. Additionally
+ * a remote SOCKS5 proxy has to be activated by the initiator before data can be transferred between
+ * the peers.
+ * 
+ * @author Henning Staib
+ */
+class Socks5ClientForInitiator extends Socks5Client {
+
+    /* the XMPP connection used to communicate with the SOCKS5 proxy */
+    private Connection connection;
+
+    /* the session ID used to activate SOCKS5 stream */
+    private String sessionID;
+
+    /* the target JID used to activate SOCKS5 stream */
+    private String target;
+
+    /**
+     * Creates a new SOCKS5 client for the initiators side.
+     * 
+     * @param streamHost containing network settings of the SOCKS5 proxy
+     * @param digest identifying the SOCKS5 Bytestream
+     * @param connection the XMPP connection
+     * @param sessionID the session ID of the SOCKS5 Bytestream
+     * @param target the target JID of the SOCKS5 Bytestream
+     */
+    public Socks5ClientForInitiator(StreamHost streamHost, String digest, Connection connection,
+                    String sessionID, String target) {
+        super(streamHost, digest);
+        this.connection = connection;
+        this.sessionID = sessionID;
+        this.target = target;
+    }
+
+    public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException,
+                    TimeoutException {
+        Socket socket = null;
+
+        // check if stream host is the local SOCKS5 proxy
+        if (this.streamHost.getJID().equals(this.connection.getUser())) {
+            Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
+            socket = socks5Server.getSocket(this.digest);
+            if (socket == null) {
+                throw new XMPPException("target is not connected to SOCKS5 proxy");
+            }
+        }
+        else {
+            socket = super.getSocket(timeout);
+
+            try {
+                activate();
+            }
+            catch (XMPPException e) {
+                socket.close();
+                throw new XMPPException("activating SOCKS5 Bytestream failed", e);
+            }
+
+        }
+
+        return socket;
+    }
+
+    /**
+     * Activates the SOCKS5 Bytestream by sending a XMPP SOCKS5 Bytestream activation packet to the
+     * SOCKS5 proxy.
+     */
+    private void activate() throws XMPPException {
+        Bytestream activate = createStreamHostActivation();
+        // if activation fails #getReply throws an exception
+        SyncPacketSend.getReply(this.connection, activate);
+    }
+
+    /**
+     * Returns a SOCKS5 Bytestream activation packet.
+     * 
+     * @return SOCKS5 Bytestream activation packet
+     */
+    private Bytestream createStreamHostActivation() {
+        Bytestream activate = new Bytestream(this.sessionID);
+        activate.setMode(null);
+        activate.setType(IQ.Type.SET);
+        activate.setTo(this.streamHost.getJID());
+
+        activate.setToActivate(this.target);
+
+        return activate;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
index 11ef7a9..d45dd6d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java
@@ -1,423 +1,423 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.DataInputStream;

-import java.io.DataOutputStream;

-import java.io.IOException;

-import java.net.InetAddress;

-import java.net.ServerSocket;

-import java.net.Socket;

-import java.net.SocketException;

-import java.net.UnknownHostException;

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.LinkedHashSet;

-import java.util.LinkedList;

-import java.util.List;

-import java.util.Map;

-import java.util.Set;

-import java.util.concurrent.ConcurrentHashMap;

-

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.XMPPException;

-

-/**

- * The Socks5Proxy class represents a local SOCKS5 proxy server. It can be enabled/disabled by

- * setting the <code>localSocks5ProxyEnabled</code> flag in the <code>smack-config.xml</code> or by

- * invoking {@link SmackConfiguration#setLocalSocks5ProxyEnabled(boolean)}. The proxy is enabled by

- * default.

- * <p>

- * The port of the local SOCKS5 proxy can be configured by setting <code>localSocks5ProxyPort</code>

- * in the <code>smack-config.xml</code> or by invoking

- * {@link SmackConfiguration#setLocalSocks5ProxyPort(int)}. Default port is 7777. If you set the

- * port to a negative value Smack tries to the absolute value and all following until it finds an

- * open port.

- * <p>

- * If your application is running on a machine with multiple network interfaces or if you want to

- * provide your public address in case you are behind a NAT router, invoke

- * {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(List)} to modify the list of

- * local network addresses used for outgoing SOCKS5 Bytestream requests.

- * <p>

- * The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed

- * in the process of establishing a SOCKS5 Bytestream (

- * {@link Socks5BytestreamManager#establishSession(String)}).

- * <p>

- * This Implementation has the following limitations:

- * <ul>

- * <li>only supports the no-authentication authentication method</li>

- * <li>only supports the <code>connect</code> command and will not answer correctly to other

- * commands</li>

- * <li>only supports requests with the domain address type and will not correctly answer to requests

- * with other address types</li>

- * </ul>

- * (see <a href="http://tools.ietf.org/html/rfc1928">RFC 1928</a>)

- * 

- * @author Henning Staib

- */

-public class Socks5Proxy {

-

-    /* SOCKS5 proxy singleton */

-    private static Socks5Proxy socks5Server;

-

-    /* reusable implementation of a SOCKS5 proxy server process */

-    private Socks5ServerProcess serverProcess;

-

-    /* thread running the SOCKS5 server process */

-    private Thread serverThread;

-

-    /* server socket to accept SOCKS5 connections */

-    private ServerSocket serverSocket;

-

-    /* assigns a connection to a digest */

-    private final Map<String, Socket> connectionMap = new ConcurrentHashMap<String, Socket>();

-

-    /* list of digests connections should be stored */

-    private final List<String> allowedConnections = Collections.synchronizedList(new LinkedList<String>());

-

-    private final Set<String> localAddresses = Collections.synchronizedSet(new LinkedHashSet<String>());

-

-    /**

-     * Private constructor.

-     */

-    private Socks5Proxy() {

-        this.serverProcess = new Socks5ServerProcess();

-

-        // add default local address

-        try {

-            this.localAddresses.add(InetAddress.getLocalHost().getHostAddress());

-        }

-        catch (UnknownHostException e) {

-            // do nothing

-        }

-

-    }

-

-    /**

-     * Returns the local SOCKS5 proxy server.

-     * 

-     * @return the local SOCKS5 proxy server

-     */

-    public static synchronized Socks5Proxy getSocks5Proxy() {

-        if (socks5Server == null) {

-            socks5Server = new Socks5Proxy();

-        }

-        if (SmackConfiguration.isLocalSocks5ProxyEnabled()) {

-            socks5Server.start();

-        }

-        return socks5Server;

-    }

-

-    /**

-     * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing.

-     */

-    public synchronized void start() {

-        if (isRunning()) {

-            return;

-        }

-        try {

-            if (SmackConfiguration.getLocalSocks5ProxyPort() < 0) {

-                int port = Math.abs(SmackConfiguration.getLocalSocks5ProxyPort());

-                for (int i = 0; i < 65535 - port; i++) {

-                    try {

-                        this.serverSocket = new ServerSocket(port + i);

-                        break;

-                    }

-                    catch (IOException e) {

-                        // port is used, try next one

-                    }

-                }

-            }

-            else {

-                this.serverSocket = new ServerSocket(SmackConfiguration.getLocalSocks5ProxyPort());

-            }

-

-            if (this.serverSocket != null) {

-                this.serverThread = new Thread(this.serverProcess);

-                this.serverThread.start();

-            }

-        }

-        catch (IOException e) {

-            // couldn't setup server

-            System.err.println("couldn't setup local SOCKS5 proxy on port "

-                            + SmackConfiguration.getLocalSocks5ProxyPort() + ": " + e.getMessage());

-        }

-    }

-

-    /**

-     * Stops the local SOCKS5 proxy server. If it is not running this method does nothing.

-     */

-    public synchronized void stop() {

-        if (!isRunning()) {

-            return;

-        }

-

-        try {

-            this.serverSocket.close();

-        }

-        catch (IOException e) {

-            // do nothing

-        }

-

-        if (this.serverThread != null && this.serverThread.isAlive()) {

-            try {

-                this.serverThread.interrupt();

-                this.serverThread.join();

-            }

-            catch (InterruptedException e) {

-                // do nothing

-            }

-        }

-        this.serverThread = null;

-        this.serverSocket = null;

-

-    }

-

-    /**

-     * Adds the given address to the list of local network addresses.

-     * <p>

-     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request.

-     * This may be necessary if your application is running on a machine with multiple network

-     * interfaces or if you want to provide your public address in case you are behind a NAT router.

-     * <p>

-     * The order of the addresses used is determined by the order you add addresses.

-     * <p>

-     * Note that the list of addresses initially contains the address returned by

-     * <code>InetAddress.getLocalHost().getHostAddress()</code>. You can replace the list of

-     * addresses by invoking {@link #replaceLocalAddresses(List)}.

-     * 

-     * @param address the local network address to add

-     */

-    public void addLocalAddress(String address) {

-        if (address == null) {

-            throw new IllegalArgumentException("address may not be null");

-        }

-        this.localAddresses.add(address);

-    }

-

-    /**

-     * Removes the given address from the list of local network addresses. This address will then no

-     * longer be used of outgoing SOCKS5 Bytestream requests.

-     * 

-     * @param address the local network address to remove

-     */

-    public void removeLocalAddress(String address) {

-        this.localAddresses.remove(address);

-    }

-

-    /**

-     * Returns an unmodifiable list of the local network addresses that will be used for streamhost

-     * candidates of outgoing SOCKS5 Bytestream requests.

-     * 

-     * @return unmodifiable list of the local network addresses

-     */

-    public List<String> getLocalAddresses() {

-        return Collections.unmodifiableList(new ArrayList<String>(this.localAddresses));

-    }

-

-    /**

-     * Replaces the list of local network addresses.

-     * <p>

-     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request and

-     * want to define their order. This may be necessary if your application is running on a machine

-     * with multiple network interfaces or if you want to provide your public address in case you

-     * are behind a NAT router.

-     * 

-     * @param addresses the new list of local network addresses

-     */

-    public void replaceLocalAddresses(List<String> addresses) {

-        if (addresses == null) {

-            throw new IllegalArgumentException("list must not be null");

-        }

-        this.localAddresses.clear();

-        this.localAddresses.addAll(addresses);

-

-    }

-

-    /**

-     * Returns the port of the local SOCKS5 proxy server. If it is not running -1 will be returned.

-     * 

-     * @return the port of the local SOCKS5 proxy server or -1 if proxy is not running

-     */

-    public int getPort() {

-        if (!isRunning()) {

-            return -1;

-        }

-        return this.serverSocket.getLocalPort();

-    }

-

-    /**

-     * Returns the socket for the given digest. A socket will be returned if the given digest has

-     * been in the list of allowed transfers (see {@link #addTransfer(String)}) while the peer

-     * connected to the SOCKS5 proxy.

-     * 

-     * @param digest identifying the connection

-     * @return socket or null if there is no socket for the given digest

-     */

-    protected Socket getSocket(String digest) {

-        return this.connectionMap.get(digest);

-    }

-

-    /**

-     * Add the given digest to the list of allowed transfers. Only connections for allowed transfers

-     * are stored and can be retrieved by invoking {@link #getSocket(String)}. All connections to

-     * the local SOCKS5 proxy that don't contain an allowed digest are discarded.

-     * 

-     * @param digest to be added to the list of allowed transfers

-     */

-    protected void addTransfer(String digest) {

-        this.allowedConnections.add(digest);

-    }

-

-    /**

-     * Removes the given digest from the list of allowed transfers. After invoking this method

-     * already stored connections with the given digest will be removed.

-     * <p>

-     * The digest should be removed after establishing the SOCKS5 Bytestream is finished, an error

-     * occurred while establishing the connection or if the connection is not allowed anymore.

-     * 

-     * @param digest to be removed from the list of allowed transfers

-     */

-    protected void removeTransfer(String digest) {

-        this.allowedConnections.remove(digest);

-        this.connectionMap.remove(digest);

-    }

-

-    /**

-     * Returns <code>true</code> if the local SOCKS5 proxy server is running, otherwise

-     * <code>false</code>.

-     * 

-     * @return <code>true</code> if the local SOCKS5 proxy server is running, otherwise

-     *         <code>false</code>

-     */

-    public boolean isRunning() {

-        return this.serverSocket != null;

-    }

-

-    /**

-     * Implementation of a simplified SOCKS5 proxy server.

-     */

-    private class Socks5ServerProcess implements Runnable {

-

-        public void run() {

-            while (true) {

-                Socket socket = null;

-

-                try {

-

-                    if (Socks5Proxy.this.serverSocket.isClosed()

-                                    || Thread.currentThread().isInterrupted()) {

-                        return;

-                    }

-

-                    // accept connection

-                    socket = Socks5Proxy.this.serverSocket.accept();

-

-                    // initialize connection

-                    establishConnection(socket);

-

-                }

-                catch (SocketException e) {

-                    /*

-                     * do nothing, if caused by closing the server socket, thread will terminate in

-                     * next loop

-                     */

-                }

-                catch (Exception e) {

-                    try {

-                        if (socket != null) {

-                            socket.close();

-                        }

-                    }

-                    catch (IOException e1) {

-                        /* do nothing */

-                    }

-                }

-            }

-

-        }

-

-        /**

-         * Negotiates a SOCKS5 connection and stores it on success.

-         * 

-         * @param socket connection to the client

-         * @throws XMPPException if client requests a connection in an unsupported way

-         * @throws IOException if a network error occurred

-         */

-        private void establishConnection(Socket socket) throws XMPPException, IOException {

-            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

-            DataInputStream in = new DataInputStream(socket.getInputStream());

-

-            // first byte is version should be 5

-            int b = in.read();

-            if (b != 5) {

-                throw new XMPPException("Only SOCKS5 supported");

-            }

-

-            // second byte number of authentication methods supported

-            b = in.read();

-

-            // read list of supported authentication methods

-            byte[] auth = new byte[b];

-            in.readFully(auth);

-

-            byte[] authMethodSelectionResponse = new byte[2];

-            authMethodSelectionResponse[0] = (byte) 0x05; // protocol version

-

-            // only authentication method 0, no authentication, supported

-            boolean noAuthMethodFound = false;

-            for (int i = 0; i < auth.length; i++) {

-                if (auth[i] == (byte) 0x00) {

-                    noAuthMethodFound = true;

-                    break;

-                }

-            }

-

-            if (!noAuthMethodFound) {

-                authMethodSelectionResponse[1] = (byte) 0xFF; // no acceptable methods

-                out.write(authMethodSelectionResponse);

-                out.flush();

-                throw new XMPPException("Authentication method not supported");

-            }

-

-            authMethodSelectionResponse[1] = (byte) 0x00; // no-authentication method

-            out.write(authMethodSelectionResponse);

-            out.flush();

-

-            // receive connection request

-            byte[] connectionRequest = Socks5Utils.receiveSocks5Message(in);

-

-            // extract digest

-            String responseDigest = new String(connectionRequest, 5, connectionRequest[4]);

-

-            // return error if digest is not allowed

-            if (!Socks5Proxy.this.allowedConnections.contains(responseDigest)) {

-                connectionRequest[1] = (byte) 0x05; // set return status to 5 (connection refused)

-                out.write(connectionRequest);

-                out.flush();

-

-                throw new XMPPException("Connection is not allowed");

-            }

-

-            connectionRequest[1] = (byte) 0x00; // set return status to 0 (success)

-            out.write(connectionRequest);

-            out.flush();

-

-            // store connection

-            Socks5Proxy.this.connectionMap.put(responseDigest, socket);

-        }

-

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.XMPPException;
+
+/**
+ * The Socks5Proxy class represents a local SOCKS5 proxy server. It can be enabled/disabled by
+ * setting the <code>localSocks5ProxyEnabled</code> flag in the <code>smack-config.xml</code> or by
+ * invoking {@link SmackConfiguration#setLocalSocks5ProxyEnabled(boolean)}. The proxy is enabled by
+ * default.
+ * <p>
+ * The port of the local SOCKS5 proxy can be configured by setting <code>localSocks5ProxyPort</code>
+ * in the <code>smack-config.xml</code> or by invoking
+ * {@link SmackConfiguration#setLocalSocks5ProxyPort(int)}. Default port is 7777. If you set the
+ * port to a negative value Smack tries to the absolute value and all following until it finds an
+ * open port.
+ * <p>
+ * If your application is running on a machine with multiple network interfaces or if you want to
+ * provide your public address in case you are behind a NAT router, invoke
+ * {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(List)} to modify the list of
+ * local network addresses used for outgoing SOCKS5 Bytestream requests.
+ * <p>
+ * The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed
+ * in the process of establishing a SOCKS5 Bytestream (
+ * {@link Socks5BytestreamManager#establishSession(String)}).
+ * <p>
+ * This Implementation has the following limitations:
+ * <ul>
+ * <li>only supports the no-authentication authentication method</li>
+ * <li>only supports the <code>connect</code> command and will not answer correctly to other
+ * commands</li>
+ * <li>only supports requests with the domain address type and will not correctly answer to requests
+ * with other address types</li>
+ * </ul>
+ * (see <a href="http://tools.ietf.org/html/rfc1928">RFC 1928</a>)
+ * 
+ * @author Henning Staib
+ */
+public class Socks5Proxy {
+
+    /* SOCKS5 proxy singleton */
+    private static Socks5Proxy socks5Server;
+
+    /* reusable implementation of a SOCKS5 proxy server process */
+    private Socks5ServerProcess serverProcess;
+
+    /* thread running the SOCKS5 server process */
+    private Thread serverThread;
+
+    /* server socket to accept SOCKS5 connections */
+    private ServerSocket serverSocket;
+
+    /* assigns a connection to a digest */
+    private final Map<String, Socket> connectionMap = new ConcurrentHashMap<String, Socket>();
+
+    /* list of digests connections should be stored */
+    private final List<String> allowedConnections = Collections.synchronizedList(new LinkedList<String>());
+
+    private final Set<String> localAddresses = Collections.synchronizedSet(new LinkedHashSet<String>());
+
+    /**
+     * Private constructor.
+     */
+    private Socks5Proxy() {
+        this.serverProcess = new Socks5ServerProcess();
+
+        // add default local address
+        try {
+            this.localAddresses.add(InetAddress.getLocalHost().getHostAddress());
+        }
+        catch (UnknownHostException e) {
+            // do nothing
+        }
+
+    }
+
+    /**
+     * Returns the local SOCKS5 proxy server.
+     * 
+     * @return the local SOCKS5 proxy server
+     */
+    public static synchronized Socks5Proxy getSocks5Proxy() {
+        if (socks5Server == null) {
+            socks5Server = new Socks5Proxy();
+        }
+        if (SmackConfiguration.isLocalSocks5ProxyEnabled()) {
+            socks5Server.start();
+        }
+        return socks5Server;
+    }
+
+    /**
+     * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing.
+     */
+    public synchronized void start() {
+        if (isRunning()) {
+            return;
+        }
+        try {
+            if (SmackConfiguration.getLocalSocks5ProxyPort() < 0) {
+                int port = Math.abs(SmackConfiguration.getLocalSocks5ProxyPort());
+                for (int i = 0; i < 65535 - port; i++) {
+                    try {
+                        this.serverSocket = new ServerSocket(port + i);
+                        break;
+                    }
+                    catch (IOException e) {
+                        // port is used, try next one
+                    }
+                }
+            }
+            else {
+                this.serverSocket = new ServerSocket(SmackConfiguration.getLocalSocks5ProxyPort());
+            }
+
+            if (this.serverSocket != null) {
+                this.serverThread = new Thread(this.serverProcess);
+                this.serverThread.start();
+            }
+        }
+        catch (IOException e) {
+            // couldn't setup server
+            System.err.println("couldn't setup local SOCKS5 proxy on port "
+                            + SmackConfiguration.getLocalSocks5ProxyPort() + ": " + e.getMessage());
+        }
+    }
+
+    /**
+     * Stops the local SOCKS5 proxy server. If it is not running this method does nothing.
+     */
+    public synchronized void stop() {
+        if (!isRunning()) {
+            return;
+        }
+
+        try {
+            this.serverSocket.close();
+        }
+        catch (IOException e) {
+            // do nothing
+        }
+
+        if (this.serverThread != null && this.serverThread.isAlive()) {
+            try {
+                this.serverThread.interrupt();
+                this.serverThread.join();
+            }
+            catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+        this.serverThread = null;
+        this.serverSocket = null;
+
+    }
+
+    /**
+     * Adds the given address to the list of local network addresses.
+     * <p>
+     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request.
+     * This may be necessary if your application is running on a machine with multiple network
+     * interfaces or if you want to provide your public address in case you are behind a NAT router.
+     * <p>
+     * The order of the addresses used is determined by the order you add addresses.
+     * <p>
+     * Note that the list of addresses initially contains the address returned by
+     * <code>InetAddress.getLocalHost().getHostAddress()</code>. You can replace the list of
+     * addresses by invoking {@link #replaceLocalAddresses(List)}.
+     * 
+     * @param address the local network address to add
+     */
+    public void addLocalAddress(String address) {
+        if (address == null) {
+            throw new IllegalArgumentException("address may not be null");
+        }
+        this.localAddresses.add(address);
+    }
+
+    /**
+     * Removes the given address from the list of local network addresses. This address will then no
+     * longer be used of outgoing SOCKS5 Bytestream requests.
+     * 
+     * @param address the local network address to remove
+     */
+    public void removeLocalAddress(String address) {
+        this.localAddresses.remove(address);
+    }
+
+    /**
+     * Returns an unmodifiable list of the local network addresses that will be used for streamhost
+     * candidates of outgoing SOCKS5 Bytestream requests.
+     * 
+     * @return unmodifiable list of the local network addresses
+     */
+    public List<String> getLocalAddresses() {
+        return Collections.unmodifiableList(new ArrayList<String>(this.localAddresses));
+    }
+
+    /**
+     * Replaces the list of local network addresses.
+     * <p>
+     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request and
+     * want to define their order. This may be necessary if your application is running on a machine
+     * with multiple network interfaces or if you want to provide your public address in case you
+     * are behind a NAT router.
+     * 
+     * @param addresses the new list of local network addresses
+     */
+    public void replaceLocalAddresses(List<String> addresses) {
+        if (addresses == null) {
+            throw new IllegalArgumentException("list must not be null");
+        }
+        this.localAddresses.clear();
+        this.localAddresses.addAll(addresses);
+
+    }
+
+    /**
+     * Returns the port of the local SOCKS5 proxy server. If it is not running -1 will be returned.
+     * 
+     * @return the port of the local SOCKS5 proxy server or -1 if proxy is not running
+     */
+    public int getPort() {
+        if (!isRunning()) {
+            return -1;
+        }
+        return this.serverSocket.getLocalPort();
+    }
+
+    /**
+     * Returns the socket for the given digest. A socket will be returned if the given digest has
+     * been in the list of allowed transfers (see {@link #addTransfer(String)}) while the peer
+     * connected to the SOCKS5 proxy.
+     * 
+     * @param digest identifying the connection
+     * @return socket or null if there is no socket for the given digest
+     */
+    protected Socket getSocket(String digest) {
+        return this.connectionMap.get(digest);
+    }
+
+    /**
+     * Add the given digest to the list of allowed transfers. Only connections for allowed transfers
+     * are stored and can be retrieved by invoking {@link #getSocket(String)}. All connections to
+     * the local SOCKS5 proxy that don't contain an allowed digest are discarded.
+     * 
+     * @param digest to be added to the list of allowed transfers
+     */
+    protected void addTransfer(String digest) {
+        this.allowedConnections.add(digest);
+    }
+
+    /**
+     * Removes the given digest from the list of allowed transfers. After invoking this method
+     * already stored connections with the given digest will be removed.
+     * <p>
+     * The digest should be removed after establishing the SOCKS5 Bytestream is finished, an error
+     * occurred while establishing the connection or if the connection is not allowed anymore.
+     * 
+     * @param digest to be removed from the list of allowed transfers
+     */
+    protected void removeTransfer(String digest) {
+        this.allowedConnections.remove(digest);
+        this.connectionMap.remove(digest);
+    }
+
+    /**
+     * Returns <code>true</code> if the local SOCKS5 proxy server is running, otherwise
+     * <code>false</code>.
+     * 
+     * @return <code>true</code> if the local SOCKS5 proxy server is running, otherwise
+     *         <code>false</code>
+     */
+    public boolean isRunning() {
+        return this.serverSocket != null;
+    }
+
+    /**
+     * Implementation of a simplified SOCKS5 proxy server.
+     */
+    private class Socks5ServerProcess implements Runnable {
+
+        public void run() {
+            while (true) {
+                Socket socket = null;
+
+                try {
+
+                    if (Socks5Proxy.this.serverSocket.isClosed()
+                                    || Thread.currentThread().isInterrupted()) {
+                        return;
+                    }
+
+                    // accept connection
+                    socket = Socks5Proxy.this.serverSocket.accept();
+
+                    // initialize connection
+                    establishConnection(socket);
+
+                }
+                catch (SocketException e) {
+                    /*
+                     * do nothing, if caused by closing the server socket, thread will terminate in
+                     * next loop
+                     */
+                }
+                catch (Exception e) {
+                    try {
+                        if (socket != null) {
+                            socket.close();
+                        }
+                    }
+                    catch (IOException e1) {
+                        /* do nothing */
+                    }
+                }
+            }
+
+        }
+
+        /**
+         * Negotiates a SOCKS5 connection and stores it on success.
+         * 
+         * @param socket connection to the client
+         * @throws XMPPException if client requests a connection in an unsupported way
+         * @throws IOException if a network error occurred
+         */
+        private void establishConnection(Socket socket) throws XMPPException, IOException {
+            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+            DataInputStream in = new DataInputStream(socket.getInputStream());
+
+            // first byte is version should be 5
+            int b = in.read();
+            if (b != 5) {
+                throw new XMPPException("Only SOCKS5 supported");
+            }
+
+            // second byte number of authentication methods supported
+            b = in.read();
+
+            // read list of supported authentication methods
+            byte[] auth = new byte[b];
+            in.readFully(auth);
+
+            byte[] authMethodSelectionResponse = new byte[2];
+            authMethodSelectionResponse[0] = (byte) 0x05; // protocol version
+
+            // only authentication method 0, no authentication, supported
+            boolean noAuthMethodFound = false;
+            for (int i = 0; i < auth.length; i++) {
+                if (auth[i] == (byte) 0x00) {
+                    noAuthMethodFound = true;
+                    break;
+                }
+            }
+
+            if (!noAuthMethodFound) {
+                authMethodSelectionResponse[1] = (byte) 0xFF; // no acceptable methods
+                out.write(authMethodSelectionResponse);
+                out.flush();
+                throw new XMPPException("Authentication method not supported");
+            }
+
+            authMethodSelectionResponse[1] = (byte) 0x00; // no-authentication method
+            out.write(authMethodSelectionResponse);
+            out.flush();
+
+            // receive connection request
+            byte[] connectionRequest = Socks5Utils.receiveSocks5Message(in);
+
+            // extract digest
+            String responseDigest = new String(connectionRequest, 5, connectionRequest[4]);
+
+            // return error if digest is not allowed
+            if (!Socks5Proxy.this.allowedConnections.contains(responseDigest)) {
+                connectionRequest[1] = (byte) 0x05; // set return status to 5 (connection refused)
+                out.write(connectionRequest);
+                out.flush();
+
+                throw new XMPPException("Connection is not allowed");
+            }
+
+            connectionRequest[1] = (byte) 0x00; // set return status to 0 (success)
+            out.write(connectionRequest);
+            out.flush();
+
+            // store connection
+            Socks5Proxy.this.connectionMap.put(responseDigest, socket);
+        }
+
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java
index 9c92563..73402a5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java
@@ -1,73 +1,73 @@
-/**

- * 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.smackx.bytestreams.socks5;

-

-import java.io.DataInputStream;

-import java.io.IOException;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.util.StringUtils;

-

-/**

- * A collection of utility methods for SOcKS5 messages.

- * 

- * @author Henning Staib

- */

-class Socks5Utils {

-

-    /**

-     * Returns a SHA-1 digest of the given parameters as specified in <a

-     * href="http://xmpp.org/extensions/xep-0065.html#impl-socks5">XEP-0065</a>.

-     * 

-     * @param sessionID for the SOCKS5 Bytestream

-     * @param initiatorJID JID of the initiator of a SOCKS5 Bytestream

-     * @param targetJID JID of the target of a SOCKS5 Bytestream

-     * @return SHA-1 digest of the given parameters

-     */

-    public static String createDigest(String sessionID, String initiatorJID, String targetJID) {

-        StringBuilder b = new StringBuilder();

-        b.append(sessionID).append(initiatorJID).append(targetJID);

-        return StringUtils.hash(b.toString());

-    }

-

-    /**

-     * Reads a SOCKS5 message from the given InputStream. The message can either be a SOCKS5 request

-     * message or a SOCKS5 response message.

-     * <p>

-     * (see <a href="http://tools.ietf.org/html/rfc1928">RFC1928</a>)

-     * 

-     * @param in the DataInputStream to read the message from

-     * @return the SOCKS5 message

-     * @throws IOException if a network error occurred

-     * @throws XMPPException if the SOCKS5 message contains an unsupported address type

-     */

-    public static byte[] receiveSocks5Message(DataInputStream in) throws IOException, XMPPException {

-        byte[] header = new byte[5];

-        in.readFully(header, 0, 5);

-

-        if (header[3] != (byte) 0x03) {

-            throw new XMPPException("Unsupported SOCKS5 address type");

-        }

-

-        int addressLength = header[4];

-

-        byte[] response = new byte[7 + addressLength];

-        System.arraycopy(header, 0, response, 0, header.length);

-

-        in.readFully(response, header.length, addressLength + 2);

-

-        return response;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * A collection of utility methods for SOcKS5 messages.
+ * 
+ * @author Henning Staib
+ */
+class Socks5Utils {
+
+    /**
+     * Returns a SHA-1 digest of the given parameters as specified in <a
+     * href="http://xmpp.org/extensions/xep-0065.html#impl-socks5">XEP-0065</a>.
+     * 
+     * @param sessionID for the SOCKS5 Bytestream
+     * @param initiatorJID JID of the initiator of a SOCKS5 Bytestream
+     * @param targetJID JID of the target of a SOCKS5 Bytestream
+     * @return SHA-1 digest of the given parameters
+     */
+    public static String createDigest(String sessionID, String initiatorJID, String targetJID) {
+        StringBuilder b = new StringBuilder();
+        b.append(sessionID).append(initiatorJID).append(targetJID);
+        return StringUtils.hash(b.toString());
+    }
+
+    /**
+     * Reads a SOCKS5 message from the given InputStream. The message can either be a SOCKS5 request
+     * message or a SOCKS5 response message.
+     * <p>
+     * (see <a href="http://tools.ietf.org/html/rfc1928">RFC1928</a>)
+     * 
+     * @param in the DataInputStream to read the message from
+     * @return the SOCKS5 message
+     * @throws IOException if a network error occurred
+     * @throws XMPPException if the SOCKS5 message contains an unsupported address type
+     */
+    public static byte[] receiveSocks5Message(DataInputStream in) throws IOException, XMPPException {
+        byte[] header = new byte[5];
+        in.readFully(header, 0, 5);
+
+        if (header[3] != (byte) 0x03) {
+            throw new XMPPException("Unsupported SOCKS5 address type");
+        }
+
+        int addressLength = header[4];
+
+        byte[] response = new byte[7 + addressLength];
+        System.arraycopy(header, 0, response, 0, header.length);
+
+        in.readFully(response, header.length, addressLength + 2);
+
+        return response;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java
index 9e07fc3..303d5b8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java
@@ -1,474 +1,474 @@
-/**

- * 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.smackx.bytestreams.socks5.packet;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Collections;

-import java.util.List;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * A packet representing part of a SOCKS5 Bytestream negotiation.

- * 

- * @author Alexander Wenckus

- */

-public class Bytestream extends IQ {

-

-    private String sessionID;

-

-    private Mode mode = Mode.tcp;

-

-    private final List<StreamHost> streamHosts = new ArrayList<StreamHost>();

-

-    private StreamHostUsed usedHost;

-

-    private Activate toActivate;

-

-    /**

-     * The default constructor

-     */

-    public Bytestream() {

-        super();

-    }

-

-    /**

-     * A constructor where the session ID can be specified.

-     * 

-     * @param SID The session ID related to the negotiation.

-     * @see #setSessionID(String)

-     */

-    public Bytestream(final String SID) {

-        super();

-        setSessionID(SID);

-    }

-

-    /**

-     * Set the session ID related to the bytestream. The session ID is a unique identifier used to

-     * differentiate between stream negotiations.

-     * 

-     * @param sessionID the unique session ID that identifies the transfer.

-     */

-    public void setSessionID(final String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-    /**

-     * Returns the session ID related to the bytestream negotiation.

-     * 

-     * @return Returns the session ID related to the bytestream negotiation.

-     * @see #setSessionID(String)

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    /**

-     * Set the transport mode. This should be put in the initiation of the interaction.

-     * 

-     * @param mode the transport mode, either UDP or TCP

-     * @see Mode

-     */

-    public void setMode(final Mode mode) {

-        this.mode = mode;

-    }

-

-    /**

-     * Returns the transport mode.

-     * 

-     * @return Returns the transport mode.

-     * @see #setMode(Mode)

-     */

-    public Mode getMode() {

-        return mode;

-    }

-

-    /**

-     * Adds a potential stream host that the remote user can connect to to receive the file.

-     * 

-     * @param JID The JID of the stream host.

-     * @param address The internet address of the stream host.

-     * @return The added stream host.

-     */

-    public StreamHost addStreamHost(final String JID, final String address) {

-        return addStreamHost(JID, address, 0);

-    }

-

-    /**

-     * Adds a potential stream host that the remote user can connect to to receive the file.

-     * 

-     * @param JID The JID of the stream host.

-     * @param address The internet address of the stream host.

-     * @param port The port on which the remote host is seeking connections.

-     * @return The added stream host.

-     */

-    public StreamHost addStreamHost(final String JID, final String address, final int port) {

-        StreamHost host = new StreamHost(JID, address);

-        host.setPort(port);

-        addStreamHost(host);

-

-        return host;

-    }

-

-    /**

-     * Adds a potential stream host that the remote user can transfer the file through.

-     * 

-     * @param host The potential stream host.

-     */

-    public void addStreamHost(final StreamHost host) {

-        streamHosts.add(host);

-    }

-

-    /**

-     * Returns the list of stream hosts contained in the packet.

-     * 

-     * @return Returns the list of stream hosts contained in the packet.

-     */

-    public Collection<StreamHost> getStreamHosts() {

-        return Collections.unmodifiableCollection(streamHosts);

-    }

-

-    /**

-     * Returns the stream host related to the given JID, or null if there is none.

-     * 

-     * @param JID The JID of the desired stream host.

-     * @return Returns the stream host related to the given JID, or null if there is none.

-     */

-    public StreamHost getStreamHost(final String JID) {

-        if (JID == null) {

-            return null;

-        }

-        for (StreamHost host : streamHosts) {

-            if (host.getJID().equals(JID)) {

-                return host;

-            }

-        }

-

-        return null;

-    }

-

-    /**

-     * Returns the count of stream hosts contained in this packet.

-     * 

-     * @return Returns the count of stream hosts contained in this packet.

-     */

-    public int countStreamHosts() {

-        return streamHosts.size();

-    }

-

-    /**

-     * Upon connecting to the stream host the target of the stream replies to the initiator with the

-     * JID of the SOCKS5 host that they used.

-     * 

-     * @param JID The JID of the used host.

-     */

-    public void setUsedHost(final String JID) {

-        this.usedHost = new StreamHostUsed(JID);

-    }

-

-    /**

-     * Returns the SOCKS5 host connected to by the remote user.

-     * 

-     * @return Returns the SOCKS5 host connected to by the remote user.

-     */

-    public StreamHostUsed getUsedHost() {

-        return usedHost;

-    }

-

-    /**

-     * Returns the activate element of the packet sent to the proxy host to verify the identity of

-     * the initiator and match them to the appropriate stream.

-     * 

-     * @return Returns the activate element of the packet sent to the proxy host to verify the

-     *         identity of the initiator and match them to the appropriate stream.

-     */

-    public Activate getToActivate() {

-        return toActivate;

-    }

-

-    /**

-     * Upon the response from the target of the used host the activate packet is sent to the SOCKS5

-     * proxy. The proxy will activate the stream or return an error after verifying the identity of

-     * the initiator, using the activate packet.

-     * 

-     * @param targetID The JID of the target of the file transfer.

-     */

-    public void setToActivate(final String targetID) {

-        this.toActivate = new Activate(targetID);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<query xmlns=\"http://jabber.org/protocol/bytestreams\"");

-        if (this.getType().equals(IQ.Type.SET)) {

-            if (getSessionID() != null) {

-                buf.append(" sid=\"").append(getSessionID()).append("\"");

-            }

-            if (getMode() != null) {

-                buf.append(" mode = \"").append(getMode()).append("\"");

-            }

-            buf.append(">");

-            if (getToActivate() == null) {

-                for (StreamHost streamHost : getStreamHosts()) {

-                    buf.append(streamHost.toXML());

-                }

-            }

-            else {

-                buf.append(getToActivate().toXML());

-            }

-        }

-        else if (this.getType().equals(IQ.Type.RESULT)) {

-            buf.append(">");

-            if (getUsedHost() != null) {

-                buf.append(getUsedHost().toXML());

-            }

-            // A result from the server can also contain stream hosts

-            else if (countStreamHosts() > 0) {

-                for (StreamHost host : streamHosts) {

-                    buf.append(host.toXML());

-                }

-            }

-        }

-        else if (this.getType().equals(IQ.Type.GET)) {

-            return buf.append("/>").toString();

-        }

-        else {

-            return null;

-        }

-        buf.append("</query>");

-

-        return buf.toString();

-    }

-

-    /**

-     * Packet extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts

-     * are forwarded to the target of the file transfer who then chooses and connects to one.

-     * 

-     * @author Alexander Wenckus

-     */

-    public static class StreamHost implements PacketExtension {

-

-        public static String NAMESPACE = "";

-

-        public static String ELEMENTNAME = "streamhost";

-

-        private final String JID;

-

-        private final String addy;

-

-        private int port = 0;

-

-        /**

-         * Default constructor.

-         * 

-         * @param JID The JID of the stream host.

-         * @param address The internet address of the stream host.

-         */

-        public StreamHost(final String JID, final String address) {

-            this.JID = JID;

-            this.addy = address;

-        }

-

-        /**

-         * Returns the JID of the stream host.

-         * 

-         * @return Returns the JID of the stream host.

-         */

-        public String getJID() {

-            return JID;

-        }

-

-        /**

-         * Returns the internet address of the stream host.

-         * 

-         * @return Returns the internet address of the stream host.

-         */

-        public String getAddress() {

-            return addy;

-        }

-

-        /**

-         * Sets the port of the stream host.

-         * 

-         * @param port The port on which the potential stream host would accept the connection.

-         */

-        public void setPort(final int port) {

-            this.port = port;

-        }

-

-        /**

-         * Returns the port on which the potential stream host would accept the connection.

-         * 

-         * @return Returns the port on which the potential stream host would accept the connection.

-         */

-        public int getPort() {

-            return port;

-        }

-

-        public String getNamespace() {

-            return NAMESPACE;

-        }

-

-        public String getElementName() {

-            return ELEMENTNAME;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<").append(getElementName()).append(" ");

-            buf.append("jid=\"").append(getJID()).append("\" ");

-            buf.append("host=\"").append(getAddress()).append("\" ");

-            if (getPort() != 0) {

-                buf.append("port=\"").append(getPort()).append("\"");

-            }

-            else {

-                buf.append("zeroconf=\"_jabber.bytestreams\"");

-            }

-            buf.append("/>");

-

-            return buf.toString();

-        }

-    }

-

-    /**

-     * After selected a SOCKS5 stream host and successfully connecting, the target of the file

-     * transfer returns a byte stream packet with the stream host used extension.

-     * 

-     * @author Alexander Wenckus

-     */

-    public static class StreamHostUsed implements PacketExtension {

-

-        public String NAMESPACE = "";

-

-        public static String ELEMENTNAME = "streamhost-used";

-

-        private final String JID;

-

-        /**

-         * Default constructor.

-         * 

-         * @param JID The JID of the selected stream host.

-         */

-        public StreamHostUsed(final String JID) {

-            this.JID = JID;

-        }

-

-        /**

-         * Returns the JID of the selected stream host.

-         * 

-         * @return Returns the JID of the selected stream host.

-         */

-        public String getJID() {

-            return JID;

-        }

-

-        public String getNamespace() {

-            return NAMESPACE;

-        }

-

-        public String getElementName() {

-            return ELEMENTNAME;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-            buf.append("<").append(getElementName()).append(" ");

-            buf.append("jid=\"").append(getJID()).append("\" ");

-            buf.append("/>");

-            return buf.toString();

-        }

-    }

-

-    /**

-     * The packet sent by the stream initiator to the stream proxy to activate the connection.

-     * 

-     * @author Alexander Wenckus

-     */

-    public static class Activate implements PacketExtension {

-

-        public String NAMESPACE = "";

-

-        public static String ELEMENTNAME = "activate";

-

-        private final String target;

-

-        /**

-         * Default constructor specifying the target of the stream.

-         * 

-         * @param target The target of the stream.

-         */

-        public Activate(final String target) {

-            this.target = target;

-        }

-

-        /**

-         * Returns the target of the activation.

-         * 

-         * @return Returns the target of the activation.

-         */

-        public String getTarget() {

-            return target;

-        }

-

-        public String getNamespace() {

-            return NAMESPACE;

-        }

-

-        public String getElementName() {

-            return ELEMENTNAME;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-            buf.append("<").append(getElementName()).append(">");

-            buf.append(getTarget());

-            buf.append("</").append(getElementName()).append(">");

-            return buf.toString();

-        }

-    }

-

-    /**

-     * The stream can be either a TCP stream or a UDP stream.

-     * 

-     * @author Alexander Wenckus

-     */

-    public enum Mode {

-

-        /**

-         * A TCP based stream.

-         */

-        tcp,

-

-        /**

-         * A UDP based stream.

-         */

-        udp;

-

-        public static Mode fromName(String name) {

-            Mode mode;

-            try {

-                mode = Mode.valueOf(name);

-            }

-            catch (Exception ex) {

-                mode = tcp;

-            }

-

-            return mode;

-        }

-    }

-}

+/**
+ * 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.smackx.bytestreams.socks5.packet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * A packet representing part of a SOCKS5 Bytestream negotiation.
+ * 
+ * @author Alexander Wenckus
+ */
+public class Bytestream extends IQ {
+
+    private String sessionID;
+
+    private Mode mode = Mode.tcp;
+
+    private final List<StreamHost> streamHosts = new ArrayList<StreamHost>();
+
+    private StreamHostUsed usedHost;
+
+    private Activate toActivate;
+
+    /**
+     * The default constructor
+     */
+    public Bytestream() {
+        super();
+    }
+
+    /**
+     * A constructor where the session ID can be specified.
+     * 
+     * @param SID The session ID related to the negotiation.
+     * @see #setSessionID(String)
+     */
+    public Bytestream(final String SID) {
+        super();
+        setSessionID(SID);
+    }
+
+    /**
+     * Set the session ID related to the bytestream. The session ID is a unique identifier used to
+     * differentiate between stream negotiations.
+     * 
+     * @param sessionID the unique session ID that identifies the transfer.
+     */
+    public void setSessionID(final String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+    /**
+     * Returns the session ID related to the bytestream negotiation.
+     * 
+     * @return Returns the session ID related to the bytestream negotiation.
+     * @see #setSessionID(String)
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    /**
+     * Set the transport mode. This should be put in the initiation of the interaction.
+     * 
+     * @param mode the transport mode, either UDP or TCP
+     * @see Mode
+     */
+    public void setMode(final Mode mode) {
+        this.mode = mode;
+    }
+
+    /**
+     * Returns the transport mode.
+     * 
+     * @return Returns the transport mode.
+     * @see #setMode(Mode)
+     */
+    public Mode getMode() {
+        return mode;
+    }
+
+    /**
+     * Adds a potential stream host that the remote user can connect to to receive the file.
+     * 
+     * @param JID The JID of the stream host.
+     * @param address The internet address of the stream host.
+     * @return The added stream host.
+     */
+    public StreamHost addStreamHost(final String JID, final String address) {
+        return addStreamHost(JID, address, 0);
+    }
+
+    /**
+     * Adds a potential stream host that the remote user can connect to to receive the file.
+     * 
+     * @param JID The JID of the stream host.
+     * @param address The internet address of the stream host.
+     * @param port The port on which the remote host is seeking connections.
+     * @return The added stream host.
+     */
+    public StreamHost addStreamHost(final String JID, final String address, final int port) {
+        StreamHost host = new StreamHost(JID, address);
+        host.setPort(port);
+        addStreamHost(host);
+
+        return host;
+    }
+
+    /**
+     * Adds a potential stream host that the remote user can transfer the file through.
+     * 
+     * @param host The potential stream host.
+     */
+    public void addStreamHost(final StreamHost host) {
+        streamHosts.add(host);
+    }
+
+    /**
+     * Returns the list of stream hosts contained in the packet.
+     * 
+     * @return Returns the list of stream hosts contained in the packet.
+     */
+    public Collection<StreamHost> getStreamHosts() {
+        return Collections.unmodifiableCollection(streamHosts);
+    }
+
+    /**
+     * Returns the stream host related to the given JID, or null if there is none.
+     * 
+     * @param JID The JID of the desired stream host.
+     * @return Returns the stream host related to the given JID, or null if there is none.
+     */
+    public StreamHost getStreamHost(final String JID) {
+        if (JID == null) {
+            return null;
+        }
+        for (StreamHost host : streamHosts) {
+            if (host.getJID().equals(JID)) {
+                return host;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the count of stream hosts contained in this packet.
+     * 
+     * @return Returns the count of stream hosts contained in this packet.
+     */
+    public int countStreamHosts() {
+        return streamHosts.size();
+    }
+
+    /**
+     * Upon connecting to the stream host the target of the stream replies to the initiator with the
+     * JID of the SOCKS5 host that they used.
+     * 
+     * @param JID The JID of the used host.
+     */
+    public void setUsedHost(final String JID) {
+        this.usedHost = new StreamHostUsed(JID);
+    }
+
+    /**
+     * Returns the SOCKS5 host connected to by the remote user.
+     * 
+     * @return Returns the SOCKS5 host connected to by the remote user.
+     */
+    public StreamHostUsed getUsedHost() {
+        return usedHost;
+    }
+
+    /**
+     * Returns the activate element of the packet sent to the proxy host to verify the identity of
+     * the initiator and match them to the appropriate stream.
+     * 
+     * @return Returns the activate element of the packet sent to the proxy host to verify the
+     *         identity of the initiator and match them to the appropriate stream.
+     */
+    public Activate getToActivate() {
+        return toActivate;
+    }
+
+    /**
+     * Upon the response from the target of the used host the activate packet is sent to the SOCKS5
+     * proxy. The proxy will activate the stream or return an error after verifying the identity of
+     * the initiator, using the activate packet.
+     * 
+     * @param targetID The JID of the target of the file transfer.
+     */
+    public void setToActivate(final String targetID) {
+        this.toActivate = new Activate(targetID);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<query xmlns=\"http://jabber.org/protocol/bytestreams\"");
+        if (this.getType().equals(IQ.Type.SET)) {
+            if (getSessionID() != null) {
+                buf.append(" sid=\"").append(getSessionID()).append("\"");
+            }
+            if (getMode() != null) {
+                buf.append(" mode = \"").append(getMode()).append("\"");
+            }
+            buf.append(">");
+            if (getToActivate() == null) {
+                for (StreamHost streamHost : getStreamHosts()) {
+                    buf.append(streamHost.toXML());
+                }
+            }
+            else {
+                buf.append(getToActivate().toXML());
+            }
+        }
+        else if (this.getType().equals(IQ.Type.RESULT)) {
+            buf.append(">");
+            if (getUsedHost() != null) {
+                buf.append(getUsedHost().toXML());
+            }
+            // A result from the server can also contain stream hosts
+            else if (countStreamHosts() > 0) {
+                for (StreamHost host : streamHosts) {
+                    buf.append(host.toXML());
+                }
+            }
+        }
+        else if (this.getType().equals(IQ.Type.GET)) {
+            return buf.append("/>").toString();
+        }
+        else {
+            return null;
+        }
+        buf.append("</query>");
+
+        return buf.toString();
+    }
+
+    /**
+     * Packet extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
+     * are forwarded to the target of the file transfer who then chooses and connects to one.
+     * 
+     * @author Alexander Wenckus
+     */
+    public static class StreamHost implements PacketExtension {
+
+        public static String NAMESPACE = "";
+
+        public static String ELEMENTNAME = "streamhost";
+
+        private final String JID;
+
+        private final String addy;
+
+        private int port = 0;
+
+        /**
+         * Default constructor.
+         * 
+         * @param JID The JID of the stream host.
+         * @param address The internet address of the stream host.
+         */
+        public StreamHost(final String JID, final String address) {
+            this.JID = JID;
+            this.addy = address;
+        }
+
+        /**
+         * Returns the JID of the stream host.
+         * 
+         * @return Returns the JID of the stream host.
+         */
+        public String getJID() {
+            return JID;
+        }
+
+        /**
+         * Returns the internet address of the stream host.
+         * 
+         * @return Returns the internet address of the stream host.
+         */
+        public String getAddress() {
+            return addy;
+        }
+
+        /**
+         * Sets the port of the stream host.
+         * 
+         * @param port The port on which the potential stream host would accept the connection.
+         */
+        public void setPort(final int port) {
+            this.port = port;
+        }
+
+        /**
+         * Returns the port on which the potential stream host would accept the connection.
+         * 
+         * @return Returns the port on which the potential stream host would accept the connection.
+         */
+        public int getPort() {
+            return port;
+        }
+
+        public String getNamespace() {
+            return NAMESPACE;
+        }
+
+        public String getElementName() {
+            return ELEMENTNAME;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<").append(getElementName()).append(" ");
+            buf.append("jid=\"").append(getJID()).append("\" ");
+            buf.append("host=\"").append(getAddress()).append("\" ");
+            if (getPort() != 0) {
+                buf.append("port=\"").append(getPort()).append("\"");
+            }
+            else {
+                buf.append("zeroconf=\"_jabber.bytestreams\"");
+            }
+            buf.append("/>");
+
+            return buf.toString();
+        }
+    }
+
+    /**
+     * After selected a SOCKS5 stream host and successfully connecting, the target of the file
+     * transfer returns a byte stream packet with the stream host used extension.
+     * 
+     * @author Alexander Wenckus
+     */
+    public static class StreamHostUsed implements PacketExtension {
+
+        public String NAMESPACE = "";
+
+        public static String ELEMENTNAME = "streamhost-used";
+
+        private final String JID;
+
+        /**
+         * Default constructor.
+         * 
+         * @param JID The JID of the selected stream host.
+         */
+        public StreamHostUsed(final String JID) {
+            this.JID = JID;
+        }
+
+        /**
+         * Returns the JID of the selected stream host.
+         * 
+         * @return Returns the JID of the selected stream host.
+         */
+        public String getJID() {
+            return JID;
+        }
+
+        public String getNamespace() {
+            return NAMESPACE;
+        }
+
+        public String getElementName() {
+            return ELEMENTNAME;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+            buf.append("<").append(getElementName()).append(" ");
+            buf.append("jid=\"").append(getJID()).append("\" ");
+            buf.append("/>");
+            return buf.toString();
+        }
+    }
+
+    /**
+     * The packet sent by the stream initiator to the stream proxy to activate the connection.
+     * 
+     * @author Alexander Wenckus
+     */
+    public static class Activate implements PacketExtension {
+
+        public String NAMESPACE = "";
+
+        public static String ELEMENTNAME = "activate";
+
+        private final String target;
+
+        /**
+         * Default constructor specifying the target of the stream.
+         * 
+         * @param target The target of the stream.
+         */
+        public Activate(final String target) {
+            this.target = target;
+        }
+
+        /**
+         * Returns the target of the activation.
+         * 
+         * @return Returns the target of the activation.
+         */
+        public String getTarget() {
+            return target;
+        }
+
+        public String getNamespace() {
+            return NAMESPACE;
+        }
+
+        public String getElementName() {
+            return ELEMENTNAME;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+            buf.append("<").append(getElementName()).append(">");
+            buf.append(getTarget());
+            buf.append("</").append(getElementName()).append(">");
+            return buf.toString();
+        }
+    }
+
+    /**
+     * The stream can be either a TCP stream or a UDP stream.
+     * 
+     * @author Alexander Wenckus
+     */
+    public enum Mode {
+
+        /**
+         * A TCP based stream.
+         */
+        tcp,
+
+        /**
+         * A UDP based stream.
+         */
+        udp;
+
+        public static Mode fromName(String name) {
+            Mode mode;
+            try {
+                mode = Mode.valueOf(name);
+            }
+            catch (Exception ex) {
+                mode = tcp;
+            }
+
+            return mode;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java
index 76f9b0c..fca8b27 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java
@@ -1,82 +1,82 @@
-/**

- * 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.smackx.bytestreams.socks5.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses a bytestream packet.

- * 

- * @author Alexander Wenckus

- */

-public class BytestreamsProvider implements IQProvider {

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        boolean done = false;

-

-        Bytestream toReturn = new Bytestream();

-

-        String id = parser.getAttributeValue("", "sid");

-        String mode = parser.getAttributeValue("", "mode");

-

-        // streamhost

-        String JID = null;

-        String host = null;

-        String port = null;

-

-        int eventType;

-        String elementName;

-        while (!done) {

-            eventType = parser.next();

-            elementName = parser.getName();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (elementName.equals(Bytestream.StreamHost.ELEMENTNAME)) {

-                    JID = parser.getAttributeValue("", "jid");

-                    host = parser.getAttributeValue("", "host");

-                    port = parser.getAttributeValue("", "port");

-                }

-                else if (elementName.equals(Bytestream.StreamHostUsed.ELEMENTNAME)) {

-                    toReturn.setUsedHost(parser.getAttributeValue("", "jid"));

-                }

-                else if (elementName.equals(Bytestream.Activate.ELEMENTNAME)) {

-                    toReturn.setToActivate(parser.getAttributeValue("", "jid"));

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (elementName.equals("streamhost")) {

-                    if (port == null) {

-                        toReturn.addStreamHost(JID, host);

-                    }

-                    else {

-                        toReturn.addStreamHost(JID, host, Integer.parseInt(port));

-                    }

-                    JID = null;

-                    host = null;

-                    port = null;

-                }

-                else if (elementName.equals("query")) {

-                    done = true;

-                }

-            }

-        }

-

-        toReturn.setMode((Bytestream.Mode.fromName(mode)));

-        toReturn.setSessionID(id);

-        return toReturn;

-    }

-

-}

+/**
+ * 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.smackx.bytestreams.socks5.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses a bytestream packet.
+ * 
+ * @author Alexander Wenckus
+ */
+public class BytestreamsProvider implements IQProvider {
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        boolean done = false;
+
+        Bytestream toReturn = new Bytestream();
+
+        String id = parser.getAttributeValue("", "sid");
+        String mode = parser.getAttributeValue("", "mode");
+
+        // streamhost
+        String JID = null;
+        String host = null;
+        String port = null;
+
+        int eventType;
+        String elementName;
+        while (!done) {
+            eventType = parser.next();
+            elementName = parser.getName();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (elementName.equals(Bytestream.StreamHost.ELEMENTNAME)) {
+                    JID = parser.getAttributeValue("", "jid");
+                    host = parser.getAttributeValue("", "host");
+                    port = parser.getAttributeValue("", "port");
+                }
+                else if (elementName.equals(Bytestream.StreamHostUsed.ELEMENTNAME)) {
+                    toReturn.setUsedHost(parser.getAttributeValue("", "jid"));
+                }
+                else if (elementName.equals(Bytestream.Activate.ELEMENTNAME)) {
+                    toReturn.setToActivate(parser.getAttributeValue("", "jid"));
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (elementName.equals("streamhost")) {
+                    if (port == null) {
+                        toReturn.addStreamHost(JID, host);
+                    }
+                    else {
+                        toReturn.addStreamHost(JID, host, Integer.parseInt(port));
+                    }
+                    JID = null;
+                    host = null;
+                    port = null;
+                }
+                else if (elementName.equals("query")) {
+                    done = true;
+                }
+            }
+        }
+
+        toReturn.setMode((Bytestream.Mode.fromName(mode)));
+        toReturn.setSessionID(id);
+        return toReturn;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommand.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommand.java
index 3077d08..6aa0110 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommand.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommand.java
@@ -1,450 +1,450 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-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.smackx.commands;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.packet.AdHocCommandData;

-

-import java.util.List;

-

-/**

- * An ad-hoc command is responsible for executing the provided service and

- * storing the result of the execution. Each new request will create a new

- * instance of the command, allowing information related to executions to be

- * stored in it. For example suppose that a command that retrieves the list of

- * users on a server is implemented. When the command is executed it gets that

- * list and the result is stored as a form in the command instance, i.e. the

- * <code>getForm</code> method retrieves a form with all the users.

- * <p>

- * Each command has a <tt>node</tt> that should be unique within a given JID.

- * <p>

- * Commands may have zero or more stages. Each stage is usually used for

- * gathering information required for the command execution. Users are able to

- * move forward or backward across the different stages. Commands may not be

- * cancelled while they are being executed. However, users may request the

- * "cancel" action when submitting a stage response indicating that the command

- * execution should be aborted. Thus, releasing any collected information.

- * Commands that require user interaction (i.e. have more than one stage) will

- * have to provide the data forms the user must complete in each stage and the

- * allowed actions the user might perform during each stage (e.g. go to the

- * previous stage or go to the next stage).

- * <p>

- * All the actions may throw an XMPPException if there is a problem executing

- * them. The <code>XMPPError</code> of that exception may have some specific

- * information about the problem. The possible extensions are:

- * 

- * <li><i>malformed-action</i>. Extension of a <i>bad-request</i> error.</li>

- * <li><i>bad-action</i>. Extension of a <i>bad-request</i> error.</li>

- * <li><i>bad-locale</i>. Extension of a <i>bad-request</i> error.</li>

- * <li><i>bad-payload</i>. Extension of a <i>bad-request</i> error.</li>

- * <li><i>bad-sessionid</i>. Extension of a <i>bad-request</i> error.</li>

- * <li><i>session-expired</i>. Extension of a <i>not-allowed</i> error.</li>

- * <p>

- * See the <code>SpecificErrorCondition</code> class for detailed description

- * of each one.

- * <p>

- * Use the <code>getSpecificErrorConditionFrom</code> to obtain the specific

- * information from an <code>XMPPError</code>.

- * 

- * @author Gabriel Guardincerri

- * 

- */

-public abstract class AdHocCommand {

-    // TODO: Analyze the redesign of command by having an ExecutionResponse as a

-    // TODO: result to the execution of every action. That result should have all the

-    // TODO: information related to the execution, e.g. the form to fill. Maybe this

-    // TODO: design is more intuitive and simpler than the current one that has all in

-    // TODO: one class.

-

-    private AdHocCommandData data;

-

-    public AdHocCommand() {

-        super();

-        data = new AdHocCommandData();

-    }

-

-    /**

-     * Returns the specific condition of the <code>error</code> or <tt>null</tt> if the

-     * error doesn't have any.

-     * 

-     * @param error the error the get the specific condition from.

-     * @return the specific condition of this error, or null if it doesn't have

-     *         any.

-     */

-    public static SpecificErrorCondition getSpecificErrorCondition(XMPPError error) {

-        // This method is implemented to provide an easy way of getting a packet

-        // extension of the XMPPError.

-        for (SpecificErrorCondition condition : SpecificErrorCondition.values()) {

-            if (error.getExtension(condition.toString(),

-                    AdHocCommandData.SpecificError.namespace) != null) {

-                return condition;

-            }

-        }

-        return null;

-    }

-

-    /**

-     * Set the the human readable name of the command, usually used for

-     * displaying in a UI.

-     * 

-     * @param name the name.

-     */

-    public void setName(String name) {

-        data.setName(name);

-    }

-

-    /**

-     * Returns the human readable name of the command.

-     * 

-     * @return the human readable name of the command

-     */

-    public String getName() {

-        return data.getName();

-    }

-

-    /**

-     * Sets the unique identifier of the command. This value must be unique for

-     * the <code>OwnerJID</code>.

-     * 

-     * @param node the unique identifier of the command.

-     */

-    public void setNode(String node) {

-        data.setNode(node);

-    }

-

-    /**

-     * Returns the unique identifier of the command. It is unique for the

-     * <code>OwnerJID</code>.

-     * 

-     * @return the unique identifier of the command.

-     */

-    public String getNode() {

-        return data.getNode();

-    }

-

-    /**

-     * Returns the full JID of the owner of this command. This JID is the "to" of a

-     * execution request.

-     * 

-     * @return the owner JID.

-     */

-    public abstract String getOwnerJID();

-

-    /**

-     * Returns the notes that the command has at the current stage.

-     * 

-     * @return a list of notes.

-     */

-    public List<AdHocCommandNote> getNotes() {

-        return data.getNotes();

-    }

-

-    /**

-     * Adds a note to the current stage. This should be used when setting a

-     * response to the execution of an action. All the notes added here are

-     * returned by the {@link #getNotes} method during the current stage.

-     * Once the stage changes all the notes are discarded.

-     * 

-     * @param note the note.

-     */

-    protected void addNote(AdHocCommandNote note) {

-        data.addNote(note);

-    }

-

-    public String getRaw() {

-        return data.getChildElementXML();

-    }

-

-    /**

-     * Returns the form of the current stage. Usually it is the form that must

-     * be answered to execute the next action. If that is the case it should be

-     * used by the requester to fill all the information that the executor needs

-     * to continue to the next stage. It can also be the result of the

-     * execution.

-     * 

-     * @return the form of the current stage to fill out or the result of the

-     *         execution.

-     */

-    public Form getForm() {

-        if (data.getForm() == null) {

-            return null;

-        }

-        else {

-            return new Form(data.getForm());

-        }

-    }

-

-    /**

-     * Sets the form of the current stage. This should be used when setting a

-     * response. It could be a form to fill out the information needed to go to

-     * the next stage or the result of an execution.

-     * 

-     * @param form the form of the current stage to fill out or the result of the

-     *      execution.

-     */

-    protected void setForm(Form form) {

-        data.setForm(form.getDataFormToSend());

-    }

-

-    /**

-     * Executes the command. This is invoked only on the first stage of the

-     * command. It is invoked on every command. If there is a problem executing

-     * the command it throws an XMPPException.

-     * 

-     * @throws XMPPException if there is an error executing the command.

-     */

-    public abstract void execute() throws XMPPException;

-

-    /**

-     * Executes the next action of the command with the information provided in

-     * the <code>response</code>. This form must be the answer form of the

-     * previous stage. This method will be only invoked for commands that have one

-     * or more stages. If there is a problem executing the command it throws an

-     * XMPPException.

-     * 

-     * @param response the form answer of the previous stage.

-     * @throws XMPPException if there is a problem executing the command.

-     */

-    public abstract void next(Form response) throws XMPPException;

-

-    /**

-     * Completes the command execution with the information provided in the

-     * <code>response</code>. This form must be the answer form of the

-     * previous stage. This method will be only invoked for commands that have one

-     * or more stages. If there is a problem executing the command it throws an

-     * XMPPException.

-     * 

-     * @param response the form answer of the previous stage.

-     * @throws XMPPException if there is a problem executing the command.

-     */

-    public abstract void complete(Form response) throws XMPPException;

-

-    /**

-     * Goes to the previous stage. The requester is asking to re-send the

-     * information of the previous stage. The command must change it state to

-     * the previous one. If there is a problem executing the command it throws

-     * an XMPPException.

-     * 

-     * @throws XMPPException if there is a problem executing the command.

-     */

-    public abstract void prev() throws XMPPException;

-

-    /**

-     * Cancels the execution of the command. This can be invoked on any stage of

-     * the execution. If there is a problem executing the command it throws an

-     * XMPPException.

-     * 

-     * @throws XMPPException if there is a problem executing the command.

-     */

-    public abstract void cancel() throws XMPPException;

-

-    /**

-     * Returns a collection with the allowed actions based on the current stage.

-     * Possible actions are: {@link Action#prev prev}, {@link Action#next next} and

-     * {@link Action#complete complete}. This method will be only invoked for commands that

-     * have one or more stages.

-     * 

-     * @return a collection with the allowed actions based on the current stage

-     *      as defined in the SessionData.

-     */

-    protected List<Action> getActions() {

-        return data.getActions();

-    }

-

-    /**

-     * Add an action to the current stage available actions. This should be used

-     * when creating a response.

-     * 

-     * @param action the action.

-     */

-    protected void addActionAvailable(Action action) {

-        data.addAction(action);

-    }

-

-    /**

-     * Returns the action available for the current stage which is

-     * considered the equivalent to "execute". When the requester sends his

-     * reply, if no action was defined in the command then the action will be

-     * assumed "execute" thus assuming the action returned by this method. This

-     * method will never be invoked for commands that have no stages.

-     * 

-     * @return the action available for the current stage which is considered

-     *      the equivalent to "execute".

-     */

-    protected Action getExecuteAction() {

-        return data.getExecuteAction();

-    }

-

-    /**

-     * Sets which of the actions available for the current stage is

-     * considered the equivalent to "execute". This should be used when setting

-     * a response. When the requester sends his reply, if no action was defined

-     * in the command then the action will be assumed "execute" thus assuming

-     * the action returned by this method.

-     * 

-     * @param action the action.

-     */

-    protected void setExecuteAction(Action action) {

-        data.setExecuteAction(action);

-    }

-

-    /**

-     * Returns the status of the current stage.

-     * 

-     * @return the current status.

-     */

-    public Status getStatus() {

-        return data.getStatus();

-    }

-

-    /**

-     * Sets the data of the current stage. This should not used.

-     * 

-     * @param data the data.

-     */

-    void setData(AdHocCommandData data) {

-        this.data = data;

-    }

-

-    /**

-     * Gets the data of the current stage. This should not used.

-     *

-     * @return the data.

-     */

-    AdHocCommandData getData() {

-        return data;

-    }

-

-    /**

-     * Returns true if the <code>action</code> is available in the current stage.

-     * The {@link Action#cancel cancel} action is always allowed. To define the

-     * available actions use the <code>addActionAvailable</code> method.

-     * 

-     * @param action

-     *            The action to check if it is available.

-     * @return True if the action is available for the current stage.

-     */

-    protected boolean isValidAction(Action action) {

-        return getActions().contains(action) || Action.cancel.equals(action);

-    }

-

-    /**

-     * The status of the stage in the adhoc command.

-     */

-    public enum Status {

-

-        /**

-         * The command is being executed.

-         */

-        executing,

-

-        /**

-         * The command has completed. The command session has ended.

-         */

-        completed,

-

-        /**

-         * The command has been canceled. The command session has ended.

-         */

-        canceled

-    }

-

-    public enum Action {

-

-        /**

-         * The command should be executed or continue to be executed. This is

-         * the default value.

-         */

-        execute,

-

-        /**

-         * The command should be canceled.

-         */

-        cancel,

-

-        /**

-         * The command should be digress to the previous stage of execution.

-         */

-        prev,

-

-        /**

-         * The command should progress to the next stage of execution.

-         */

-        next,

-

-        /**

-         * The command should be completed (if possible).

-         */

-        complete,

-

-        /**

-         * The action is unknow. This is used when a recieved message has an

-         * unknown action. It must not be used to send an execution request.

-         */

-        unknown

-    }

-

-    public enum SpecificErrorCondition {

-

-        /**

-         * The responding JID cannot accept the specified action.

-         */

-        badAction("bad-action"),

-

-        /**

-         * The responding JID does not understand the specified action.

-         */

-        malformedAction("malformed-action"),

-

-        /**

-         * The responding JID cannot accept the specified language/locale.

-         */

-        badLocale("bad-locale"),

-

-        /**

-         * The responding JID cannot accept the specified payload (e.g. the data

-         * form did not provide one or more required fields).

-         */

-        badPayload("bad-payload"),

-

-        /**

-         * The responding JID cannot accept the specified sessionid.

-         */

-        badSessionid("bad-sessionid"),

-

-        /**

-         * The requesting JID specified a sessionid that is no longer active

-         * (either because it was completed, canceled, or timed out).

-         */

-        sessionExpired("session-expired");

-

-        private String value;

-

-        SpecificErrorCondition(String value) {

-            this.value = value;

-        }

-

-        public String toString() {

-            return value;

-        }

-    }

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-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.smackx.commands;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.packet.AdHocCommandData;
+
+import java.util.List;
+
+/**
+ * An ad-hoc command is responsible for executing the provided service and
+ * storing the result of the execution. Each new request will create a new
+ * instance of the command, allowing information related to executions to be
+ * stored in it. For example suppose that a command that retrieves the list of
+ * users on a server is implemented. When the command is executed it gets that
+ * list and the result is stored as a form in the command instance, i.e. the
+ * <code>getForm</code> method retrieves a form with all the users.
+ * <p>
+ * Each command has a <tt>node</tt> that should be unique within a given JID.
+ * <p>
+ * Commands may have zero or more stages. Each stage is usually used for
+ * gathering information required for the command execution. Users are able to
+ * move forward or backward across the different stages. Commands may not be
+ * cancelled while they are being executed. However, users may request the
+ * "cancel" action when submitting a stage response indicating that the command
+ * execution should be aborted. Thus, releasing any collected information.
+ * Commands that require user interaction (i.e. have more than one stage) will
+ * have to provide the data forms the user must complete in each stage and the
+ * allowed actions the user might perform during each stage (e.g. go to the
+ * previous stage or go to the next stage).
+ * <p>
+ * All the actions may throw an XMPPException if there is a problem executing
+ * them. The <code>XMPPError</code> of that exception may have some specific
+ * information about the problem. The possible extensions are:
+ * 
+ * <li><i>malformed-action</i>. Extension of a <i>bad-request</i> error.</li>
+ * <li><i>bad-action</i>. Extension of a <i>bad-request</i> error.</li>
+ * <li><i>bad-locale</i>. Extension of a <i>bad-request</i> error.</li>
+ * <li><i>bad-payload</i>. Extension of a <i>bad-request</i> error.</li>
+ * <li><i>bad-sessionid</i>. Extension of a <i>bad-request</i> error.</li>
+ * <li><i>session-expired</i>. Extension of a <i>not-allowed</i> error.</li>
+ * <p>
+ * See the <code>SpecificErrorCondition</code> class for detailed description
+ * of each one.
+ * <p>
+ * Use the <code>getSpecificErrorConditionFrom</code> to obtain the specific
+ * information from an <code>XMPPError</code>.
+ * 
+ * @author Gabriel Guardincerri
+ * 
+ */
+public abstract class AdHocCommand {
+    // TODO: Analyze the redesign of command by having an ExecutionResponse as a
+    // TODO: result to the execution of every action. That result should have all the
+    // TODO: information related to the execution, e.g. the form to fill. Maybe this
+    // TODO: design is more intuitive and simpler than the current one that has all in
+    // TODO: one class.
+
+    private AdHocCommandData data;
+
+    public AdHocCommand() {
+        super();
+        data = new AdHocCommandData();
+    }
+
+    /**
+     * Returns the specific condition of the <code>error</code> or <tt>null</tt> if the
+     * error doesn't have any.
+     * 
+     * @param error the error the get the specific condition from.
+     * @return the specific condition of this error, or null if it doesn't have
+     *         any.
+     */
+    public static SpecificErrorCondition getSpecificErrorCondition(XMPPError error) {
+        // This method is implemented to provide an easy way of getting a packet
+        // extension of the XMPPError.
+        for (SpecificErrorCondition condition : SpecificErrorCondition.values()) {
+            if (error.getExtension(condition.toString(),
+                    AdHocCommandData.SpecificError.namespace) != null) {
+                return condition;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Set the the human readable name of the command, usually used for
+     * displaying in a UI.
+     * 
+     * @param name the name.
+     */
+    public void setName(String name) {
+        data.setName(name);
+    }
+
+    /**
+     * Returns the human readable name of the command.
+     * 
+     * @return the human readable name of the command
+     */
+    public String getName() {
+        return data.getName();
+    }
+
+    /**
+     * Sets the unique identifier of the command. This value must be unique for
+     * the <code>OwnerJID</code>.
+     * 
+     * @param node the unique identifier of the command.
+     */
+    public void setNode(String node) {
+        data.setNode(node);
+    }
+
+    /**
+     * Returns the unique identifier of the command. It is unique for the
+     * <code>OwnerJID</code>.
+     * 
+     * @return the unique identifier of the command.
+     */
+    public String getNode() {
+        return data.getNode();
+    }
+
+    /**
+     * Returns the full JID of the owner of this command. This JID is the "to" of a
+     * execution request.
+     * 
+     * @return the owner JID.
+     */
+    public abstract String getOwnerJID();
+
+    /**
+     * Returns the notes that the command has at the current stage.
+     * 
+     * @return a list of notes.
+     */
+    public List<AdHocCommandNote> getNotes() {
+        return data.getNotes();
+    }
+
+    /**
+     * Adds a note to the current stage. This should be used when setting a
+     * response to the execution of an action. All the notes added here are
+     * returned by the {@link #getNotes} method during the current stage.
+     * Once the stage changes all the notes are discarded.
+     * 
+     * @param note the note.
+     */
+    protected void addNote(AdHocCommandNote note) {
+        data.addNote(note);
+    }
+
+    public String getRaw() {
+        return data.getChildElementXML();
+    }
+
+    /**
+     * Returns the form of the current stage. Usually it is the form that must
+     * be answered to execute the next action. If that is the case it should be
+     * used by the requester to fill all the information that the executor needs
+     * to continue to the next stage. It can also be the result of the
+     * execution.
+     * 
+     * @return the form of the current stage to fill out or the result of the
+     *         execution.
+     */
+    public Form getForm() {
+        if (data.getForm() == null) {
+            return null;
+        }
+        else {
+            return new Form(data.getForm());
+        }
+    }
+
+    /**
+     * Sets the form of the current stage. This should be used when setting a
+     * response. It could be a form to fill out the information needed to go to
+     * the next stage or the result of an execution.
+     * 
+     * @param form the form of the current stage to fill out or the result of the
+     *      execution.
+     */
+    protected void setForm(Form form) {
+        data.setForm(form.getDataFormToSend());
+    }
+
+    /**
+     * Executes the command. This is invoked only on the first stage of the
+     * command. It is invoked on every command. If there is a problem executing
+     * the command it throws an XMPPException.
+     * 
+     * @throws XMPPException if there is an error executing the command.
+     */
+    public abstract void execute() throws XMPPException;
+
+    /**
+     * Executes the next action of the command with the information provided in
+     * the <code>response</code>. This form must be the answer form of the
+     * previous stage. This method will be only invoked for commands that have one
+     * or more stages. If there is a problem executing the command it throws an
+     * XMPPException.
+     * 
+     * @param response the form answer of the previous stage.
+     * @throws XMPPException if there is a problem executing the command.
+     */
+    public abstract void next(Form response) throws XMPPException;
+
+    /**
+     * Completes the command execution with the information provided in the
+     * <code>response</code>. This form must be the answer form of the
+     * previous stage. This method will be only invoked for commands that have one
+     * or more stages. If there is a problem executing the command it throws an
+     * XMPPException.
+     * 
+     * @param response the form answer of the previous stage.
+     * @throws XMPPException if there is a problem executing the command.
+     */
+    public abstract void complete(Form response) throws XMPPException;
+
+    /**
+     * Goes to the previous stage. The requester is asking to re-send the
+     * information of the previous stage. The command must change it state to
+     * the previous one. If there is a problem executing the command it throws
+     * an XMPPException.
+     * 
+     * @throws XMPPException if there is a problem executing the command.
+     */
+    public abstract void prev() throws XMPPException;
+
+    /**
+     * Cancels the execution of the command. This can be invoked on any stage of
+     * the execution. If there is a problem executing the command it throws an
+     * XMPPException.
+     * 
+     * @throws XMPPException if there is a problem executing the command.
+     */
+    public abstract void cancel() throws XMPPException;
+
+    /**
+     * Returns a collection with the allowed actions based on the current stage.
+     * Possible actions are: {@link Action#prev prev}, {@link Action#next next} and
+     * {@link Action#complete complete}. This method will be only invoked for commands that
+     * have one or more stages.
+     * 
+     * @return a collection with the allowed actions based on the current stage
+     *      as defined in the SessionData.
+     */
+    protected List<Action> getActions() {
+        return data.getActions();
+    }
+
+    /**
+     * Add an action to the current stage available actions. This should be used
+     * when creating a response.
+     * 
+     * @param action the action.
+     */
+    protected void addActionAvailable(Action action) {
+        data.addAction(action);
+    }
+
+    /**
+     * Returns the action available for the current stage which is
+     * considered the equivalent to "execute". When the requester sends his
+     * reply, if no action was defined in the command then the action will be
+     * assumed "execute" thus assuming the action returned by this method. This
+     * method will never be invoked for commands that have no stages.
+     * 
+     * @return the action available for the current stage which is considered
+     *      the equivalent to "execute".
+     */
+    protected Action getExecuteAction() {
+        return data.getExecuteAction();
+    }
+
+    /**
+     * Sets which of the actions available for the current stage is
+     * considered the equivalent to "execute". This should be used when setting
+     * a response. When the requester sends his reply, if no action was defined
+     * in the command then the action will be assumed "execute" thus assuming
+     * the action returned by this method.
+     * 
+     * @param action the action.
+     */
+    protected void setExecuteAction(Action action) {
+        data.setExecuteAction(action);
+    }
+
+    /**
+     * Returns the status of the current stage.
+     * 
+     * @return the current status.
+     */
+    public Status getStatus() {
+        return data.getStatus();
+    }
+
+    /**
+     * Sets the data of the current stage. This should not used.
+     * 
+     * @param data the data.
+     */
+    void setData(AdHocCommandData data) {
+        this.data = data;
+    }
+
+    /**
+     * Gets the data of the current stage. This should not used.
+     *
+     * @return the data.
+     */
+    AdHocCommandData getData() {
+        return data;
+    }
+
+    /**
+     * Returns true if the <code>action</code> is available in the current stage.
+     * The {@link Action#cancel cancel} action is always allowed. To define the
+     * available actions use the <code>addActionAvailable</code> method.
+     * 
+     * @param action
+     *            The action to check if it is available.
+     * @return True if the action is available for the current stage.
+     */
+    protected boolean isValidAction(Action action) {
+        return getActions().contains(action) || Action.cancel.equals(action);
+    }
+
+    /**
+     * The status of the stage in the adhoc command.
+     */
+    public enum Status {
+
+        /**
+         * The command is being executed.
+         */
+        executing,
+
+        /**
+         * The command has completed. The command session has ended.
+         */
+        completed,
+
+        /**
+         * The command has been canceled. The command session has ended.
+         */
+        canceled
+    }
+
+    public enum Action {
+
+        /**
+         * The command should be executed or continue to be executed. This is
+         * the default value.
+         */
+        execute,
+
+        /**
+         * The command should be canceled.
+         */
+        cancel,
+
+        /**
+         * The command should be digress to the previous stage of execution.
+         */
+        prev,
+
+        /**
+         * The command should progress to the next stage of execution.
+         */
+        next,
+
+        /**
+         * The command should be completed (if possible).
+         */
+        complete,
+
+        /**
+         * The action is unknow. This is used when a recieved message has an
+         * unknown action. It must not be used to send an execution request.
+         */
+        unknown
+    }
+
+    public enum SpecificErrorCondition {
+
+        /**
+         * The responding JID cannot accept the specified action.
+         */
+        badAction("bad-action"),
+
+        /**
+         * The responding JID does not understand the specified action.
+         */
+        malformedAction("malformed-action"),
+
+        /**
+         * The responding JID cannot accept the specified language/locale.
+         */
+        badLocale("bad-locale"),
+
+        /**
+         * The responding JID cannot accept the specified payload (e.g. the data
+         * form did not provide one or more required fields).
+         */
+        badPayload("bad-payload"),
+
+        /**
+         * The responding JID cannot accept the specified sessionid.
+         */
+        badSessionid("bad-sessionid"),
+
+        /**
+         * The requesting JID specified a sessionid that is no longer active
+         * (either because it was completed, canceled, or timed out).
+         */
+        sessionExpired("session-expired");
+
+        private String value;
+
+        SpecificErrorCondition(String value) {
+            this.value = value;
+        }
+
+        public String toString() {
+            return value;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandManager.java
index 8f4eb65..737a204 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandManager.java
@@ -1,750 +1,750 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-2008 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.smackx.commands;

-

-import org.jivesoftware.smack.*;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.NodeInformationProvider;

-import org.jivesoftware.smackx.ServiceDiscoveryManager;

-import org.jivesoftware.smackx.commands.AdHocCommand.Action;

-import org.jivesoftware.smackx.commands.AdHocCommand.Status;

-import org.jivesoftware.smackx.packet.AdHocCommandData;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;

-import org.jivesoftware.smackx.packet.DiscoverItems;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.List;

-import java.util.Map;

-import java.util.concurrent.ConcurrentHashMap;

-

-/**

- * An AdHocCommandManager is responsible for keeping the list of available

- * commands offered by a service and for processing commands requests.

- *

- * Pass in a Connection instance to

- * {@link #getAddHocCommandsManager(org.jivesoftware.smack.Connection)} in order to

- * get an instance of this class. 

- * 

- * @author Gabriel Guardincerri

- */

-public class AdHocCommandManager {

-

-    private static final String DISCO_NAMESPACE = "http://jabber.org/protocol/commands";

-

-    private static final String discoNode = DISCO_NAMESPACE;

-

-    /**

-     * The session time out in seconds.

-     */

-    private static final int SESSION_TIMEOUT = 2 * 60;

-

-    /**

-     * Map a Connection with it AdHocCommandManager. This map have a key-value

-     * pair for every active connection.

-     */

-    private static Map<Connection, AdHocCommandManager> instances =

-            new ConcurrentHashMap<Connection, AdHocCommandManager>();

-

-    /**

-     * Register the listener for all the connection creations. When a new

-     * connection is created a new AdHocCommandManager is also created and

-     * related to that connection.

-     */

-    static {

-        Connection.addConnectionCreationListener(new ConnectionCreationListener() {

-            public void connectionCreated(Connection connection) {

-                new AdHocCommandManager(connection);

-            }

-        });

-    }

-

-    /**

-     * Returns the <code>AdHocCommandManager</code> related to the

-     * <code>connection</code>.

-     *

-     * @param connection the XMPP connection.

-     * @return the AdHocCommandManager associated with the connection.

-     */

-    public static AdHocCommandManager getAddHocCommandsManager(Connection connection) {

-        return instances.get(connection);

-    }

-

-    /**

-     * Thread that reaps stale sessions.

-     */

-    private Thread sessionsSweeper;

-

-    /**

-     * The Connection that this instances of AdHocCommandManager manages

-     */

-    private Connection connection;

-

-    /**

-     * Map a command node with its AdHocCommandInfo. Note: Key=command node,

-     * Value=command. Command node matches the node attribute sent by command

-     * requesters.

-     */

-    private Map<String, AdHocCommandInfo> commands = new ConcurrentHashMap<String, AdHocCommandInfo>();

-

-    /**

-     * Map a command session ID with the instance LocalCommand. The LocalCommand

-     * is the an objects that has all the information of the current state of

-     * the command execution. Note: Key=session ID, Value=LocalCommand. Session

-     * ID matches the sessionid attribute sent by command responders.

-     */

-    private Map<String, LocalCommand> executingCommands = new ConcurrentHashMap<String, LocalCommand>();

-

-    private AdHocCommandManager(Connection connection) {

-        super();

-        this.connection = connection;

-        init();

-    }

-

-    /**

-     * Registers a new command with this command manager, which is related to a

-     * connection. The <tt>node</tt> is an unique identifier of that command for

-     * the connection related to this command manager. The <tt>name</tt> is the

-     * human readable name of the command. The <tt>class</tt> is the class of

-     * the command, which must extend {@link LocalCommand} and have a default

-     * constructor.

-     *

-     * @param node the unique identifier of the command.

-     * @param name the human readable name of the command.

-     * @param clazz the class of the command, which must extend {@link LocalCommand}.

-     */

-    public void registerCommand(String node, String name, final Class<? extends LocalCommand> clazz) {

-        registerCommand(node, name, new LocalCommandFactory() {

-            public LocalCommand getInstance() throws InstantiationException, IllegalAccessException  {

-                return clazz.newInstance();

-            }

-        });

-    }

-

-    /**

-     * Registers a new command with this command manager, which is related to a

-     * connection. The <tt>node</tt> is an unique identifier of that

-     * command for the connection related to this command manager. The <tt>name</tt>

-     * is the human readeale name of the command. The <tt>factory</tt> generates

-     * new instances of the command.

-     *

-     * @param node the unique identifier of the command.

-     * @param name the human readable name of the command.

-     * @param factory a factory to create new instances of the command.

-     */

-    public void registerCommand(String node, final String name, LocalCommandFactory factory) {

-        AdHocCommandInfo commandInfo = new AdHocCommandInfo(node, name, connection.getUser(), factory);

-

-        commands.put(node, commandInfo);

-        // Set the NodeInformationProvider that will provide information about

-        // the added command

-        ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider(node,

-                new NodeInformationProvider() {

-                    public List<DiscoverItems.Item> getNodeItems() {

-                        return null;

-                    }

-

-                    public List<String> getNodeFeatures() {

-                        List<String> answer = new ArrayList<String>();

-                        answer.add(DISCO_NAMESPACE);

-                        // TODO: check if this service is provided by the

-                        // TODO: current connection.

-                        answer.add("jabber:x:data");

-                        return answer;

-                    }

-

-                    public List<DiscoverInfo.Identity> getNodeIdentities() {

-                        List<DiscoverInfo.Identity> answer = new ArrayList<DiscoverInfo.Identity>();

-                        DiscoverInfo.Identity identity = new DiscoverInfo.Identity(

-                                "automation", name, "command-node");

-                        answer.add(identity);

-                        return answer;

-                    }

-

-                    @Override

-                    public List<PacketExtension> getNodePacketExtensions() {

-                        return null;

-                    }

-

-                });

-    }

-

-    /**

-     * Discover the commands of an specific JID. The <code>jid</code> is a

-     * full JID.

-     *

-     * @param jid the full JID to retrieve the commands for.

-     * @return the discovered items.

-     * @throws XMPPException if the operation failed for some reason.

-     */

-    public DiscoverItems discoverCommands(String jid) throws XMPPException {

-        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager

-                .getInstanceFor(connection);

-        return serviceDiscoveryManager.discoverItems(jid, discoNode);

-    }

-

-    /**

-     * Publish the commands to an specific JID.

-     *

-     * @param jid the full JID to publish the commands to.

-     * @throws XMPPException if the operation failed for some reason.

-     */

-    public void publishCommands(String jid) throws XMPPException {

-        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager

-                .getInstanceFor(connection);

-

-        // Collects the commands to publish as items

-        DiscoverItems discoverItems = new DiscoverItems();

-        Collection<AdHocCommandInfo> xCommandsList = getRegisteredCommands();

-

-        for (AdHocCommandInfo info : xCommandsList) {

-            DiscoverItems.Item item = new DiscoverItems.Item(info.getOwnerJID());

-            item.setName(info.getName());

-            item.setNode(info.getNode());

-            discoverItems.addItem(item);

-        }

-

-        serviceDiscoveryManager.publishItems(jid, discoNode, discoverItems);

-    }

-

-    /**

-     * Returns a command that represents an instance of a command in a remote

-     * host. It is used to execute remote commands. The concept is similar to

-     * RMI. Every invocation on this command is equivalent to an invocation in

-     * the remote command.

-     *

-     * @param jid the full JID of the host of the remote command

-     * @param node the identifier of the command

-     * @return a local instance equivalent to the remote command.

-     */

-    public RemoteCommand getRemoteCommand(String jid, String node) {

-        return new RemoteCommand(connection, node, jid);

-    }

-

-    /**

-     * <ul>

-     * <li>Adds listeners to the connection</li>

-     * <li>Registers the ad-hoc command feature to the ServiceDiscoveryManager</li>

-     * <li>Registers the items of the feature</li>

-     * <li>Adds packet listeners to handle execution requests</li>

-     * <li>Creates and start the session sweeper</li>

-     * </ul>

-     */

-    private void init() {

-        // Register the new instance and associate it with the connection

-        instances.put(connection, this);

-

-        // Add a listener to the connection that removes the registered instance

-        // when the connection is closed

-        connection.addConnectionListener(new ConnectionListener() {

-            public void connectionClosed() {

-                // Unregister this instance since the connection has been closed

-                instances.remove(connection);

-            }

-

-            public void connectionClosedOnError(Exception e) {

-                // Unregister this instance since the connection has been closed

-                instances.remove(connection);

-            }

-

-            public void reconnectionSuccessful() {

-                // Register this instance since the connection has been

-                // reestablished

-                instances.put(connection, AdHocCommandManager.this);

-            }

-

-            public void reconnectingIn(int seconds) {

-                // Nothing to do

-            }

-

-            public void reconnectionFailed(Exception e) {

-                // Nothing to do

-            }

-        });

-

-        // Add the feature to the service discovery manage to show that this

-        // connection supports the AdHoc-Commands protocol.

-        // This information will be used when another client tries to

-        // discover whether this client supports AdHoc-Commands or not.

-        ServiceDiscoveryManager.getInstanceFor(connection).addFeature(

-                DISCO_NAMESPACE);

-

-        // Set the NodeInformationProvider that will provide information about

-        // which AdHoc-Commands are registered, whenever a disco request is

-        // received

-        ServiceDiscoveryManager.getInstanceFor(connection)

-                .setNodeInformationProvider(discoNode,

-                        new NodeInformationProvider() {

-                            public List<DiscoverItems.Item> getNodeItems() {

-

-                                List<DiscoverItems.Item> answer = new ArrayList<DiscoverItems.Item>();

-                                Collection<AdHocCommandInfo> commandsList = getRegisteredCommands();

-

-                                for (AdHocCommandInfo info : commandsList) {

-                                    DiscoverItems.Item item = new DiscoverItems.Item(

-                                            info.getOwnerJID());

-                                    item.setName(info.getName());

-                                    item.setNode(info.getNode());

-                                    answer.add(item);

-                                }

-

-                                return answer;

-                            }

-

-                            public List<String> getNodeFeatures() {

-                                return null;

-                            }

-

-                            public List<Identity> getNodeIdentities() {

-                                return null;

-                            }

-

-                            @Override

-                            public List<PacketExtension> getNodePacketExtensions() {

-                                return null;

-                            }

-                        });

-

-        // The packet listener and the filter for processing some AdHoc Commands

-        // Packets

-        PacketListener listener = new PacketListener() {

-            public void processPacket(Packet packet) {

-                AdHocCommandData requestData = (AdHocCommandData) packet;

-                processAdHocCommand(requestData);

-            }

-        };

-

-        PacketFilter filter = new PacketTypeFilter(AdHocCommandData.class);

-        connection.addPacketListener(listener, filter);

-

-        sessionsSweeper = null;

-    }

-

-    /**

-     * Process the AdHoc-Command packet that request the execution of some

-     * action of a command. If this is the first request, this method checks,

-     * before executing the command, if:

-     * <ul>

-     *  <li>The requested command exists</li>

-     *  <li>The requester has permissions to execute it</li>

-     *  <li>The command has more than one stage, if so, it saves the command and

-     *      session ID for further use</li>

-     * </ul>

-     * 

-     * <br>

-     * <br>

-     * If this is not the first request, this method checks, before executing

-     * the command, if:

-     * <ul>

-     *  <li>The session ID of the request was stored</li>

-     *  <li>The session life do not exceed the time out</li>

-     *  <li>The action to execute is one of the available actions</li>

-     * </ul>

-     *

-     * @param requestData

-     *            the packet to process.

-     */

-    private void processAdHocCommand(AdHocCommandData requestData) {

-        // Only process requests of type SET

-        if (requestData.getType() != IQ.Type.SET) {

-            return;

-        }

-

-        // Creates the response with the corresponding data

-        AdHocCommandData response = new AdHocCommandData();

-        response.setTo(requestData.getFrom());

-        response.setPacketID(requestData.getPacketID());

-        response.setNode(requestData.getNode());

-        response.setId(requestData.getTo());

-

-        String sessionId = requestData.getSessionID();

-        String commandNode = requestData.getNode();

-

-        if (sessionId == null) {

-            // A new execution request has been received. Check that the

-            // command exists

-            if (!commands.containsKey(commandNode)) {

-                // Requested command does not exist so return

-                // item_not_found error.

-                respondError(response, XMPPError.Condition.item_not_found);

-                return;

-            }

-

-            // Create new session ID

-            sessionId = StringUtils.randomString(15);

-

-            try {

-                // Create a new instance of the command with the

-                // corresponding sessioid

-                LocalCommand command = newInstanceOfCmd(commandNode, sessionId);

-

-                response.setType(IQ.Type.RESULT);

-                command.setData(response);

-

-                // Check that the requester has enough permission.

-                // Answer forbidden error if requester permissions are not

-                // enough to execute the requested command

-                if (!command.hasPermission(requestData.getFrom())) {

-                    respondError(response, XMPPError.Condition.forbidden);

-                    return;

-                }

-

-                Action action = requestData.getAction();

-

-                // If the action is unknown then respond an error.

-                if (action != null && action.equals(Action.unknown)) {

-                    respondError(response, XMPPError.Condition.bad_request,

-                            AdHocCommand.SpecificErrorCondition.malformedAction);

-                    return;

-                }

-

-                // If the action is not execute, then it is an invalid action.

-                if (action != null && !action.equals(Action.execute)) {

-                    respondError(response, XMPPError.Condition.bad_request,

-                            AdHocCommand.SpecificErrorCondition.badAction);

-                    return;

-                }

-

-                // Increase the state number, so the command knows in witch

-                // stage it is

-                command.incrementStage();

-                // Executes the command

-                command.execute();

-

-                if (command.isLastStage()) {

-                    // If there is only one stage then the command is completed

-                    response.setStatus(Status.completed);

-                }

-                else {

-                    // Else it is still executing, and is registered to be

-                    // available for the next call

-                    response.setStatus(Status.executing);

-                    executingCommands.put(sessionId, command);

-                    // See if the session reaping thread is started. If not, start it.

-                    if (sessionsSweeper == null) {

-                        sessionsSweeper = new Thread(new Runnable() {

-                            public void run() {

-                                while (true) {

-                                    for (String sessionId : executingCommands.keySet()) {

-                                        LocalCommand command = executingCommands.get(sessionId);

-                                        // Since the command could be removed in the meanwhile

-                                        // of getting the key and getting the value - by a

-                                        // processed packet. We must check if it still in the

-                                        // map.

-                                        if (command != null) {

-                                            long creationStamp = command.getCreationDate();

-                                            // Check if the Session data has expired (default is

-                                            // 10 minutes)

-                                            // To remove it from the session list it waits for

-                                            // the double of the of time out time. This is to

-                                            // let

-                                            // the requester know why his execution request is

-                                            // not accepted. If the session is removed just

-                                            // after the time out, then whe the user request to

-                                            // continue the execution he will recieved an

-                                            // invalid session error and not a time out error.

-                                            if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000 * 2) {

-                                                // Remove the expired session

-                                                executingCommands.remove(sessionId);

-                                            }

-                                        }

-                                    }

-                                    try {

-                                        Thread.sleep(1000);

-                                    }

-                                    catch (InterruptedException ie) {

-                                        // Ignore.

-                                    }

-                                }

-                            }

-

-                        });

-                        sessionsSweeper.setDaemon(true);

-                        sessionsSweeper.start();

-                    }

-                }

-

-                // Sends the response packet

-                connection.sendPacket(response);

-

-            }

-            catch (XMPPException e) {

-                // If there is an exception caused by the next, complete,

-                // prev or cancel method, then that error is returned to the

-                // requester.

-                XMPPError error = e.getXMPPError();

-

-                // If the error type is cancel, then the execution is

-                // canceled therefore the status must show that, and the

-                // command be removed from the executing list.

-                if (XMPPError.Type.CANCEL.equals(error.getType())) {

-                    response.setStatus(Status.canceled);

-                    executingCommands.remove(sessionId);

-                }

-                respondError(response, error);

-                e.printStackTrace();

-            }

-        }

-        else {

-            LocalCommand command = executingCommands.get(sessionId);

-

-            // Check that a command exists for the specified sessionID

-            // This also handles if the command was removed in the meanwhile

-            // of getting the key and the value of the map.

-            if (command == null) {

-                respondError(response, XMPPError.Condition.bad_request,

-                        AdHocCommand.SpecificErrorCondition.badSessionid);

-                return;

-            }

-

-            // Check if the Session data has expired (default is 10 minutes)

-            long creationStamp = command.getCreationDate();

-            if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000) {

-                // Remove the expired session

-                executingCommands.remove(sessionId);

-

-                // Answer a not_allowed error (session-expired)

-                respondError(response, XMPPError.Condition.not_allowed,

-                        AdHocCommand.SpecificErrorCondition.sessionExpired);

-                return;

-            }

-

-            /*

-             * Since the requester could send two requests for the same

-             * executing command i.e. the same session id, all the execution of

-             * the action must be synchronized to avoid inconsistencies.

-             */

-            synchronized (command) {

-                Action action = requestData.getAction();

-

-                // If the action is unknown the respond an error

-                if (action != null && action.equals(Action.unknown)) {

-                    respondError(response, XMPPError.Condition.bad_request,

-                            AdHocCommand.SpecificErrorCondition.malformedAction);

-                    return;

-                }

-

-                // If the user didn't specify an action or specify the execute

-                // action then follow the actual default execute action

-                if (action == null || Action.execute.equals(action)) {

-                    action = command.getExecuteAction();

-                }

-

-                // Check that the specified action was previously

-                // offered

-                if (!command.isValidAction(action)) {

-                    respondError(response, XMPPError.Condition.bad_request,

-                            AdHocCommand.SpecificErrorCondition.badAction);

-                    return;

-                }

-

-                try {

-                    // TODO: Check that all the requierd fields of the form are

-                    // TODO: filled, if not throw an exception. This will simplify the

-                    // TODO: construction of new commands

-

-                    // Since all errors were passed, the response is now a

-                    // result

-                    response.setType(IQ.Type.RESULT);

-

-                    // Set the new data to the command.

-                    command.setData(response);

-

-                    if (Action.next.equals(action)) {

-                        command.incrementStage();

-                        command.next(new Form(requestData.getForm()));

-                        if (command.isLastStage()) {

-                            // If it is the last stage then the command is

-                            // completed

-                            response.setStatus(Status.completed);

-                        }

-                        else {

-                            // Otherwise it is still executing

-                            response.setStatus(Status.executing);

-                        }

-                    }

-                    else if (Action.complete.equals(action)) {

-                        command.incrementStage();

-                        command.complete(new Form(requestData.getForm()));

-                        response.setStatus(Status.completed);

-                        // Remove the completed session

-                        executingCommands.remove(sessionId);

-                    }

-                    else if (Action.prev.equals(action)) {

-                        command.decrementStage();

-                        command.prev();

-                    }

-                    else if (Action.cancel.equals(action)) {

-                        command.cancel();

-                        response.setStatus(Status.canceled);

-                        // Remove the canceled session

-                        executingCommands.remove(sessionId);

-                    }

-

-                    connection.sendPacket(response);

-                }

-                catch (XMPPException e) {

-                    // If there is an exception caused by the next, complete,

-                    // prev or cancel method, then that error is returned to the

-                    // requester.

-                    XMPPError error = e.getXMPPError();

-

-                    // If the error type is cancel, then the execution is

-                    // canceled therefore the status must show that, and the

-                    // command be removed from the executing list.

-                    if (XMPPError.Type.CANCEL.equals(error.getType())) {

-                        response.setStatus(Status.canceled);

-                        executingCommands.remove(sessionId);

-                    }

-                    respondError(response, error);

-

-                    e.printStackTrace();

-                }

-            }

-        }

-    }

-

-    /**

-     * Responds an error with an specific condition.

-     * 

-     * @param response the response to send.

-     * @param condition the condition of the error.

-     */

-    private void respondError(AdHocCommandData response,

-            XMPPError.Condition condition) {

-        respondError(response, new XMPPError(condition));

-    }

-

-    /**

-     * Responds an error with an specific condition.

-     * 

-     * @param response the response to send.

-     * @param condition the condition of the error.

-     * @param specificCondition the adhoc command error condition.

-     */

-    private void respondError(AdHocCommandData response, XMPPError.Condition condition,

-            AdHocCommand.SpecificErrorCondition specificCondition)

-    {

-        XMPPError error = new XMPPError(condition);

-        error.addExtension(new AdHocCommandData.SpecificError(specificCondition));

-        respondError(response, error);

-    }

-

-    /**

-     * Responds an error with an specific error.

-     * 

-     * @param response the response to send.

-     * @param error the error to send.

-     */

-    private void respondError(AdHocCommandData response, XMPPError error) {

-        response.setType(IQ.Type.ERROR);

-        response.setError(error);

-        connection.sendPacket(response);

-    }

-

-    /**

-     * Creates a new instance of a command to be used by a new execution request

-     * 

-     * @param commandNode the command node that identifies it.

-     * @param sessionID the session id of this execution.

-     * @return the command instance to execute.

-     * @throws XMPPException if there is problem creating the new instance.

-     */

-    private LocalCommand newInstanceOfCmd(String commandNode, String sessionID)

-            throws XMPPException

-    {

-        AdHocCommandInfo commandInfo = commands.get(commandNode);

-        LocalCommand command;

-        try {

-            command = (LocalCommand) commandInfo.getCommandInstance();

-            command.setSessionID(sessionID);

-            command.setName(commandInfo.getName());

-            command.setNode(commandInfo.getNode());

-        }

-        catch (InstantiationException e) {

-            e.printStackTrace();

-            throw new XMPPException(new XMPPError(

-                    XMPPError.Condition.interna_server_error));

-        }

-        catch (IllegalAccessException e) {

-            e.printStackTrace();

-            throw new XMPPException(new XMPPError(

-                    XMPPError.Condition.interna_server_error));

-        }

-        return command;

-    }

-

-    /**

-     * Returns the registered commands of this command manager, which is related

-     * to a connection.

-     * 

-     * @return the registered commands.

-     */

-    private Collection<AdHocCommandInfo> getRegisteredCommands() {

-        return commands.values();

-    }

-

-    /**

-     * Stores ad-hoc command information.

-     */

-    private static class AdHocCommandInfo {

-

-        private String node;

-        private String name;

-        private String ownerJID;

-        private LocalCommandFactory factory;

-

-        public AdHocCommandInfo(String node, String name, String ownerJID,

-                LocalCommandFactory factory)

-        {

-            this.node = node;

-            this.name = name;

-            this.ownerJID = ownerJID;

-            this.factory = factory;

-        }

-

-        public LocalCommand getCommandInstance() throws InstantiationException,

-                IllegalAccessException

-        {

-            return factory.getInstance();

-        }

-

-        public String getName() {

-            return name;

-        }

-

-        public String getNode() {

-            return node;

-        }

-

-        public String getOwnerJID() {

-            return ownerJID;

-        }

-    }

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-2008 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.smackx.commands;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.NodeInformationProvider;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.commands.AdHocCommand.Action;
+import org.jivesoftware.smackx.commands.AdHocCommand.Status;
+import org.jivesoftware.smackx.packet.AdHocCommandData;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
+import org.jivesoftware.smackx.packet.DiscoverItems;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * An AdHocCommandManager is responsible for keeping the list of available
+ * commands offered by a service and for processing commands requests.
+ *
+ * Pass in a Connection instance to
+ * {@link #getAddHocCommandsManager(org.jivesoftware.smack.Connection)} in order to
+ * get an instance of this class. 
+ * 
+ * @author Gabriel Guardincerri
+ */
+public class AdHocCommandManager {
+
+    private static final String DISCO_NAMESPACE = "http://jabber.org/protocol/commands";
+
+    private static final String discoNode = DISCO_NAMESPACE;
+
+    /**
+     * The session time out in seconds.
+     */
+    private static final int SESSION_TIMEOUT = 2 * 60;
+
+    /**
+     * Map a Connection with it AdHocCommandManager. This map have a key-value
+     * pair for every active connection.
+     */
+    private static Map<Connection, AdHocCommandManager> instances =
+            new ConcurrentHashMap<Connection, AdHocCommandManager>();
+
+    /**
+     * Register the listener for all the connection creations. When a new
+     * connection is created a new AdHocCommandManager is also created and
+     * related to that connection.
+     */
+    static {
+        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+            public void connectionCreated(Connection connection) {
+                new AdHocCommandManager(connection);
+            }
+        });
+    }
+
+    /**
+     * Returns the <code>AdHocCommandManager</code> related to the
+     * <code>connection</code>.
+     *
+     * @param connection the XMPP connection.
+     * @return the AdHocCommandManager associated with the connection.
+     */
+    public static AdHocCommandManager getAddHocCommandsManager(Connection connection) {
+        return instances.get(connection);
+    }
+
+    /**
+     * Thread that reaps stale sessions.
+     */
+    private Thread sessionsSweeper;
+
+    /**
+     * The Connection that this instances of AdHocCommandManager manages
+     */
+    private Connection connection;
+
+    /**
+     * Map a command node with its AdHocCommandInfo. Note: Key=command node,
+     * Value=command. Command node matches the node attribute sent by command
+     * requesters.
+     */
+    private Map<String, AdHocCommandInfo> commands = new ConcurrentHashMap<String, AdHocCommandInfo>();
+
+    /**
+     * Map a command session ID with the instance LocalCommand. The LocalCommand
+     * is the an objects that has all the information of the current state of
+     * the command execution. Note: Key=session ID, Value=LocalCommand. Session
+     * ID matches the sessionid attribute sent by command responders.
+     */
+    private Map<String, LocalCommand> executingCommands = new ConcurrentHashMap<String, LocalCommand>();
+
+    private AdHocCommandManager(Connection connection) {
+        super();
+        this.connection = connection;
+        init();
+    }
+
+    /**
+     * Registers a new command with this command manager, which is related to a
+     * connection. The <tt>node</tt> is an unique identifier of that command for
+     * the connection related to this command manager. The <tt>name</tt> is the
+     * human readable name of the command. The <tt>class</tt> is the class of
+     * the command, which must extend {@link LocalCommand} and have a default
+     * constructor.
+     *
+     * @param node the unique identifier of the command.
+     * @param name the human readable name of the command.
+     * @param clazz the class of the command, which must extend {@link LocalCommand}.
+     */
+    public void registerCommand(String node, String name, final Class<? extends LocalCommand> clazz) {
+        registerCommand(node, name, new LocalCommandFactory() {
+            public LocalCommand getInstance() throws InstantiationException, IllegalAccessException  {
+                return clazz.newInstance();
+            }
+        });
+    }
+
+    /**
+     * Registers a new command with this command manager, which is related to a
+     * connection. The <tt>node</tt> is an unique identifier of that
+     * command for the connection related to this command manager. The <tt>name</tt>
+     * is the human readeale name of the command. The <tt>factory</tt> generates
+     * new instances of the command.
+     *
+     * @param node the unique identifier of the command.
+     * @param name the human readable name of the command.
+     * @param factory a factory to create new instances of the command.
+     */
+    public void registerCommand(String node, final String name, LocalCommandFactory factory) {
+        AdHocCommandInfo commandInfo = new AdHocCommandInfo(node, name, connection.getUser(), factory);
+
+        commands.put(node, commandInfo);
+        // Set the NodeInformationProvider that will provide information about
+        // the added command
+        ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider(node,
+                new NodeInformationProvider() {
+                    public List<DiscoverItems.Item> getNodeItems() {
+                        return null;
+                    }
+
+                    public List<String> getNodeFeatures() {
+                        List<String> answer = new ArrayList<String>();
+                        answer.add(DISCO_NAMESPACE);
+                        // TODO: check if this service is provided by the
+                        // TODO: current connection.
+                        answer.add("jabber:x:data");
+                        return answer;
+                    }
+
+                    public List<DiscoverInfo.Identity> getNodeIdentities() {
+                        List<DiscoverInfo.Identity> answer = new ArrayList<DiscoverInfo.Identity>();
+                        DiscoverInfo.Identity identity = new DiscoverInfo.Identity(
+                                "automation", name, "command-node");
+                        answer.add(identity);
+                        return answer;
+                    }
+
+                    @Override
+                    public List<PacketExtension> getNodePacketExtensions() {
+                        return null;
+                    }
+
+                });
+    }
+
+    /**
+     * Discover the commands of an specific JID. The <code>jid</code> is a
+     * full JID.
+     *
+     * @param jid the full JID to retrieve the commands for.
+     * @return the discovered items.
+     * @throws XMPPException if the operation failed for some reason.
+     */
+    public DiscoverItems discoverCommands(String jid) throws XMPPException {
+        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager
+                .getInstanceFor(connection);
+        return serviceDiscoveryManager.discoverItems(jid, discoNode);
+    }
+
+    /**
+     * Publish the commands to an specific JID.
+     *
+     * @param jid the full JID to publish the commands to.
+     * @throws XMPPException if the operation failed for some reason.
+     */
+    public void publishCommands(String jid) throws XMPPException {
+        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager
+                .getInstanceFor(connection);
+
+        // Collects the commands to publish as items
+        DiscoverItems discoverItems = new DiscoverItems();
+        Collection<AdHocCommandInfo> xCommandsList = getRegisteredCommands();
+
+        for (AdHocCommandInfo info : xCommandsList) {
+            DiscoverItems.Item item = new DiscoverItems.Item(info.getOwnerJID());
+            item.setName(info.getName());
+            item.setNode(info.getNode());
+            discoverItems.addItem(item);
+        }
+
+        serviceDiscoveryManager.publishItems(jid, discoNode, discoverItems);
+    }
+
+    /**
+     * Returns a command that represents an instance of a command in a remote
+     * host. It is used to execute remote commands. The concept is similar to
+     * RMI. Every invocation on this command is equivalent to an invocation in
+     * the remote command.
+     *
+     * @param jid the full JID of the host of the remote command
+     * @param node the identifier of the command
+     * @return a local instance equivalent to the remote command.
+     */
+    public RemoteCommand getRemoteCommand(String jid, String node) {
+        return new RemoteCommand(connection, node, jid);
+    }
+
+    /**
+     * <ul>
+     * <li>Adds listeners to the connection</li>
+     * <li>Registers the ad-hoc command feature to the ServiceDiscoveryManager</li>
+     * <li>Registers the items of the feature</li>
+     * <li>Adds packet listeners to handle execution requests</li>
+     * <li>Creates and start the session sweeper</li>
+     * </ul>
+     */
+    private void init() {
+        // Register the new instance and associate it with the connection
+        instances.put(connection, this);
+
+        // Add a listener to the connection that removes the registered instance
+        // when the connection is closed
+        connection.addConnectionListener(new ConnectionListener() {
+            public void connectionClosed() {
+                // Unregister this instance since the connection has been closed
+                instances.remove(connection);
+            }
+
+            public void connectionClosedOnError(Exception e) {
+                // Unregister this instance since the connection has been closed
+                instances.remove(connection);
+            }
+
+            public void reconnectionSuccessful() {
+                // Register this instance since the connection has been
+                // reestablished
+                instances.put(connection, AdHocCommandManager.this);
+            }
+
+            public void reconnectingIn(int seconds) {
+                // Nothing to do
+            }
+
+            public void reconnectionFailed(Exception e) {
+                // Nothing to do
+            }
+        });
+
+        // Add the feature to the service discovery manage to show that this
+        // connection supports the AdHoc-Commands protocol.
+        // This information will be used when another client tries to
+        // discover whether this client supports AdHoc-Commands or not.
+        ServiceDiscoveryManager.getInstanceFor(connection).addFeature(
+                DISCO_NAMESPACE);
+
+        // Set the NodeInformationProvider that will provide information about
+        // which AdHoc-Commands are registered, whenever a disco request is
+        // received
+        ServiceDiscoveryManager.getInstanceFor(connection)
+                .setNodeInformationProvider(discoNode,
+                        new NodeInformationProvider() {
+                            public List<DiscoverItems.Item> getNodeItems() {
+
+                                List<DiscoverItems.Item> answer = new ArrayList<DiscoverItems.Item>();
+                                Collection<AdHocCommandInfo> commandsList = getRegisteredCommands();
+
+                                for (AdHocCommandInfo info : commandsList) {
+                                    DiscoverItems.Item item = new DiscoverItems.Item(
+                                            info.getOwnerJID());
+                                    item.setName(info.getName());
+                                    item.setNode(info.getNode());
+                                    answer.add(item);
+                                }
+
+                                return answer;
+                            }
+
+                            public List<String> getNodeFeatures() {
+                                return null;
+                            }
+
+                            public List<Identity> getNodeIdentities() {
+                                return null;
+                            }
+
+                            @Override
+                            public List<PacketExtension> getNodePacketExtensions() {
+                                return null;
+                            }
+                        });
+
+        // The packet listener and the filter for processing some AdHoc Commands
+        // Packets
+        PacketListener listener = new PacketListener() {
+            public void processPacket(Packet packet) {
+                AdHocCommandData requestData = (AdHocCommandData) packet;
+                processAdHocCommand(requestData);
+            }
+        };
+
+        PacketFilter filter = new PacketTypeFilter(AdHocCommandData.class);
+        connection.addPacketListener(listener, filter);
+
+        sessionsSweeper = null;
+    }
+
+    /**
+     * Process the AdHoc-Command packet that request the execution of some
+     * action of a command. If this is the first request, this method checks,
+     * before executing the command, if:
+     * <ul>
+     *  <li>The requested command exists</li>
+     *  <li>The requester has permissions to execute it</li>
+     *  <li>The command has more than one stage, if so, it saves the command and
+     *      session ID for further use</li>
+     * </ul>
+     * 
+     * <br>
+     * <br>
+     * If this is not the first request, this method checks, before executing
+     * the command, if:
+     * <ul>
+     *  <li>The session ID of the request was stored</li>
+     *  <li>The session life do not exceed the time out</li>
+     *  <li>The action to execute is one of the available actions</li>
+     * </ul>
+     *
+     * @param requestData
+     *            the packet to process.
+     */
+    private void processAdHocCommand(AdHocCommandData requestData) {
+        // Only process requests of type SET
+        if (requestData.getType() != IQ.Type.SET) {
+            return;
+        }
+
+        // Creates the response with the corresponding data
+        AdHocCommandData response = new AdHocCommandData();
+        response.setTo(requestData.getFrom());
+        response.setPacketID(requestData.getPacketID());
+        response.setNode(requestData.getNode());
+        response.setId(requestData.getTo());
+
+        String sessionId = requestData.getSessionID();
+        String commandNode = requestData.getNode();
+
+        if (sessionId == null) {
+            // A new execution request has been received. Check that the
+            // command exists
+            if (!commands.containsKey(commandNode)) {
+                // Requested command does not exist so return
+                // item_not_found error.
+                respondError(response, XMPPError.Condition.item_not_found);
+                return;
+            }
+
+            // Create new session ID
+            sessionId = StringUtils.randomString(15);
+
+            try {
+                // Create a new instance of the command with the
+                // corresponding sessioid
+                LocalCommand command = newInstanceOfCmd(commandNode, sessionId);
+
+                response.setType(IQ.Type.RESULT);
+                command.setData(response);
+
+                // Check that the requester has enough permission.
+                // Answer forbidden error if requester permissions are not
+                // enough to execute the requested command
+                if (!command.hasPermission(requestData.getFrom())) {
+                    respondError(response, XMPPError.Condition.forbidden);
+                    return;
+                }
+
+                Action action = requestData.getAction();
+
+                // If the action is unknown then respond an error.
+                if (action != null && action.equals(Action.unknown)) {
+                    respondError(response, XMPPError.Condition.bad_request,
+                            AdHocCommand.SpecificErrorCondition.malformedAction);
+                    return;
+                }
+
+                // If the action is not execute, then it is an invalid action.
+                if (action != null && !action.equals(Action.execute)) {
+                    respondError(response, XMPPError.Condition.bad_request,
+                            AdHocCommand.SpecificErrorCondition.badAction);
+                    return;
+                }
+
+                // Increase the state number, so the command knows in witch
+                // stage it is
+                command.incrementStage();
+                // Executes the command
+                command.execute();
+
+                if (command.isLastStage()) {
+                    // If there is only one stage then the command is completed
+                    response.setStatus(Status.completed);
+                }
+                else {
+                    // Else it is still executing, and is registered to be
+                    // available for the next call
+                    response.setStatus(Status.executing);
+                    executingCommands.put(sessionId, command);
+                    // See if the session reaping thread is started. If not, start it.
+                    if (sessionsSweeper == null) {
+                        sessionsSweeper = new Thread(new Runnable() {
+                            public void run() {
+                                while (true) {
+                                    for (String sessionId : executingCommands.keySet()) {
+                                        LocalCommand command = executingCommands.get(sessionId);
+                                        // Since the command could be removed in the meanwhile
+                                        // of getting the key and getting the value - by a
+                                        // processed packet. We must check if it still in the
+                                        // map.
+                                        if (command != null) {
+                                            long creationStamp = command.getCreationDate();
+                                            // Check if the Session data has expired (default is
+                                            // 10 minutes)
+                                            // To remove it from the session list it waits for
+                                            // the double of the of time out time. This is to
+                                            // let
+                                            // the requester know why his execution request is
+                                            // not accepted. If the session is removed just
+                                            // after the time out, then whe the user request to
+                                            // continue the execution he will recieved an
+                                            // invalid session error and not a time out error.
+                                            if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000 * 2) {
+                                                // Remove the expired session
+                                                executingCommands.remove(sessionId);
+                                            }
+                                        }
+                                    }
+                                    try {
+                                        Thread.sleep(1000);
+                                    }
+                                    catch (InterruptedException ie) {
+                                        // Ignore.
+                                    }
+                                }
+                            }
+
+                        });
+                        sessionsSweeper.setDaemon(true);
+                        sessionsSweeper.start();
+                    }
+                }
+
+                // Sends the response packet
+                connection.sendPacket(response);
+
+            }
+            catch (XMPPException e) {
+                // If there is an exception caused by the next, complete,
+                // prev or cancel method, then that error is returned to the
+                // requester.
+                XMPPError error = e.getXMPPError();
+
+                // If the error type is cancel, then the execution is
+                // canceled therefore the status must show that, and the
+                // command be removed from the executing list.
+                if (XMPPError.Type.CANCEL.equals(error.getType())) {
+                    response.setStatus(Status.canceled);
+                    executingCommands.remove(sessionId);
+                }
+                respondError(response, error);
+                e.printStackTrace();
+            }
+        }
+        else {
+            LocalCommand command = executingCommands.get(sessionId);
+
+            // Check that a command exists for the specified sessionID
+            // This also handles if the command was removed in the meanwhile
+            // of getting the key and the value of the map.
+            if (command == null) {
+                respondError(response, XMPPError.Condition.bad_request,
+                        AdHocCommand.SpecificErrorCondition.badSessionid);
+                return;
+            }
+
+            // Check if the Session data has expired (default is 10 minutes)
+            long creationStamp = command.getCreationDate();
+            if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000) {
+                // Remove the expired session
+                executingCommands.remove(sessionId);
+
+                // Answer a not_allowed error (session-expired)
+                respondError(response, XMPPError.Condition.not_allowed,
+                        AdHocCommand.SpecificErrorCondition.sessionExpired);
+                return;
+            }
+
+            /*
+             * Since the requester could send two requests for the same
+             * executing command i.e. the same session id, all the execution of
+             * the action must be synchronized to avoid inconsistencies.
+             */
+            synchronized (command) {
+                Action action = requestData.getAction();
+
+                // If the action is unknown the respond an error
+                if (action != null && action.equals(Action.unknown)) {
+                    respondError(response, XMPPError.Condition.bad_request,
+                            AdHocCommand.SpecificErrorCondition.malformedAction);
+                    return;
+                }
+
+                // If the user didn't specify an action or specify the execute
+                // action then follow the actual default execute action
+                if (action == null || Action.execute.equals(action)) {
+                    action = command.getExecuteAction();
+                }
+
+                // Check that the specified action was previously
+                // offered
+                if (!command.isValidAction(action)) {
+                    respondError(response, XMPPError.Condition.bad_request,
+                            AdHocCommand.SpecificErrorCondition.badAction);
+                    return;
+                }
+
+                try {
+                    // TODO: Check that all the requierd fields of the form are
+                    // TODO: filled, if not throw an exception. This will simplify the
+                    // TODO: construction of new commands
+
+                    // Since all errors were passed, the response is now a
+                    // result
+                    response.setType(IQ.Type.RESULT);
+
+                    // Set the new data to the command.
+                    command.setData(response);
+
+                    if (Action.next.equals(action)) {
+                        command.incrementStage();
+                        command.next(new Form(requestData.getForm()));
+                        if (command.isLastStage()) {
+                            // If it is the last stage then the command is
+                            // completed
+                            response.setStatus(Status.completed);
+                        }
+                        else {
+                            // Otherwise it is still executing
+                            response.setStatus(Status.executing);
+                        }
+                    }
+                    else if (Action.complete.equals(action)) {
+                        command.incrementStage();
+                        command.complete(new Form(requestData.getForm()));
+                        response.setStatus(Status.completed);
+                        // Remove the completed session
+                        executingCommands.remove(sessionId);
+                    }
+                    else if (Action.prev.equals(action)) {
+                        command.decrementStage();
+                        command.prev();
+                    }
+                    else if (Action.cancel.equals(action)) {
+                        command.cancel();
+                        response.setStatus(Status.canceled);
+                        // Remove the canceled session
+                        executingCommands.remove(sessionId);
+                    }
+
+                    connection.sendPacket(response);
+                }
+                catch (XMPPException e) {
+                    // If there is an exception caused by the next, complete,
+                    // prev or cancel method, then that error is returned to the
+                    // requester.
+                    XMPPError error = e.getXMPPError();
+
+                    // If the error type is cancel, then the execution is
+                    // canceled therefore the status must show that, and the
+                    // command be removed from the executing list.
+                    if (XMPPError.Type.CANCEL.equals(error.getType())) {
+                        response.setStatus(Status.canceled);
+                        executingCommands.remove(sessionId);
+                    }
+                    respondError(response, error);
+
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * Responds an error with an specific condition.
+     * 
+     * @param response the response to send.
+     * @param condition the condition of the error.
+     */
+    private void respondError(AdHocCommandData response,
+            XMPPError.Condition condition) {
+        respondError(response, new XMPPError(condition));
+    }
+
+    /**
+     * Responds an error with an specific condition.
+     * 
+     * @param response the response to send.
+     * @param condition the condition of the error.
+     * @param specificCondition the adhoc command error condition.
+     */
+    private void respondError(AdHocCommandData response, XMPPError.Condition condition,
+            AdHocCommand.SpecificErrorCondition specificCondition)
+    {
+        XMPPError error = new XMPPError(condition);
+        error.addExtension(new AdHocCommandData.SpecificError(specificCondition));
+        respondError(response, error);
+    }
+
+    /**
+     * Responds an error with an specific error.
+     * 
+     * @param response the response to send.
+     * @param error the error to send.
+     */
+    private void respondError(AdHocCommandData response, XMPPError error) {
+        response.setType(IQ.Type.ERROR);
+        response.setError(error);
+        connection.sendPacket(response);
+    }
+
+    /**
+     * Creates a new instance of a command to be used by a new execution request
+     * 
+     * @param commandNode the command node that identifies it.
+     * @param sessionID the session id of this execution.
+     * @return the command instance to execute.
+     * @throws XMPPException if there is problem creating the new instance.
+     */
+    private LocalCommand newInstanceOfCmd(String commandNode, String sessionID)
+            throws XMPPException
+    {
+        AdHocCommandInfo commandInfo = commands.get(commandNode);
+        LocalCommand command;
+        try {
+            command = (LocalCommand) commandInfo.getCommandInstance();
+            command.setSessionID(sessionID);
+            command.setName(commandInfo.getName());
+            command.setNode(commandInfo.getNode());
+        }
+        catch (InstantiationException e) {
+            e.printStackTrace();
+            throw new XMPPException(new XMPPError(
+                    XMPPError.Condition.interna_server_error));
+        }
+        catch (IllegalAccessException e) {
+            e.printStackTrace();
+            throw new XMPPException(new XMPPError(
+                    XMPPError.Condition.interna_server_error));
+        }
+        return command;
+    }
+
+    /**
+     * Returns the registered commands of this command manager, which is related
+     * to a connection.
+     * 
+     * @return the registered commands.
+     */
+    private Collection<AdHocCommandInfo> getRegisteredCommands() {
+        return commands.values();
+    }
+
+    /**
+     * Stores ad-hoc command information.
+     */
+    private static class AdHocCommandInfo {
+
+        private String node;
+        private String name;
+        private String ownerJID;
+        private LocalCommandFactory factory;
+
+        public AdHocCommandInfo(String node, String name, String ownerJID,
+                LocalCommandFactory factory)
+        {
+            this.node = node;
+            this.name = name;
+            this.ownerJID = ownerJID;
+            this.factory = factory;
+        }
+
+        public LocalCommand getCommandInstance() throws InstantiationException,
+                IllegalAccessException
+        {
+            return factory.getInstance();
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getNode() {
+            return node;
+        }
+
+        public String getOwnerJID() {
+            return ownerJID;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandNote.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandNote.java
index 10dedbe..15ef4b2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandNote.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/AdHocCommandNote.java
@@ -1,86 +1,86 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-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.smackx.commands;

-

-/**

- * Notes can be added to a command execution response. A note has a type and value.

- * 

- * @author Gabriel Guardincerri

- */

-public class AdHocCommandNote {

-

-    private Type type;

-    private String value;

-

-    /**

-     * Creates a new adhoc command note with the specified type and value.

-     *

-     * @param type the type of the note.

-     * @param value the value of the note.

-     */

-    public AdHocCommandNote(Type type, String value) {

-        this.type = type;

-        this.value = value;

-    }

-

-    /**

-     * Returns the value or message of the note.

-     * 

-     * @return the value or message of the note.

-     */

-    public String getValue() {

-        return value;

-    }

-

-    /**

-     * Return the type of the note.

-     * 

-     * @return the type of the note.

-     */

-    public Type getType() {

-        return type;

-    }

-

-    /**

-     * Represents a note type.

-     */

-    public enum Type {

-

-        /**

-         * The note is informational only. This is not really an exceptional

-         * condition.

-         */

-        info,

-

-        /**

-         * The note indicates a warning. Possibly due to illogical (yet valid)

-         * data.

-         */

-        warn,

-

-        /**

-         * The note indicates an error. The text should indicate the reason for

-         * the error.

-         */

-        error

-    }

-

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-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.smackx.commands;
+
+/**
+ * Notes can be added to a command execution response. A note has a type and value.
+ * 
+ * @author Gabriel Guardincerri
+ */
+public class AdHocCommandNote {
+
+    private Type type;
+    private String value;
+
+    /**
+     * Creates a new adhoc command note with the specified type and value.
+     *
+     * @param type the type of the note.
+     * @param value the value of the note.
+     */
+    public AdHocCommandNote(Type type, String value) {
+        this.type = type;
+        this.value = value;
+    }
+
+    /**
+     * Returns the value or message of the note.
+     * 
+     * @return the value or message of the note.
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Return the type of the note.
+     * 
+     * @return the type of the note.
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * Represents a note type.
+     */
+    public enum Type {
+
+        /**
+         * The note is informational only. This is not really an exceptional
+         * condition.
+         */
+        info,
+
+        /**
+         * The note indicates a warning. Possibly due to illogical (yet valid)
+         * data.
+         */
+        warn,
+
+        /**
+         * The note indicates an error. The text should indicate the reason for
+         * the error.
+         */
+        error
+    }
+
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/LocalCommand.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/LocalCommand.java
index 627d30e..dae1fb4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/LocalCommand.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/commands/LocalCommand.java
@@ -1,169 +1,169 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-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.smackx.commands;

-

-import org.jivesoftware.smackx.packet.AdHocCommandData;

-

-/**

- * Represents a command that can be executed locally from a remote location. This

- * class must be extended to implement an specific ad-hoc command. This class

- * provides some useful tools:<ul>

- *      <li>Node</li>

- *      <li>Name</li>

- *      <li>Session ID</li>

- *      <li>Current Stage</li>

- *      <li>Available actions</li>

- *      <li>Default action</li>

- * </ul><p/>

- * To implement a new command extend this class and implement all the abstract

- * methods. When implementing the actions remember that they could be invoked

- * several times, and that you must use the current stage number to know what to

- * do.

- * 

- * @author Gabriel Guardincerri

- */

-public abstract class LocalCommand extends AdHocCommand {

-

-    /**

-     * The time stamp of first invokation of the command. Used to implement the session timeout.

-     */

-    private long creationDate;

-

-    /**

-     * The unique ID of the execution of the command.

-     */

-    private String sessionID;

-

-    /**

-     * The full JID of the host of the command.

-     */

-    private String ownerJID;

-

-    /**

-     * The number of the current stage.

-     */

-    private int currenStage;

-

-    public LocalCommand() {

-        super();

-        this.creationDate = System.currentTimeMillis();

-        currenStage = -1;

-    }

-

-    /**

-     * The sessionID is an unique identifier of an execution request. This is

-     * automatically handled and should not be called.

-     * 

-     * @param sessionID the unique session id of this execution

-     */

-    public void setSessionID(String sessionID) {

-        this.sessionID = sessionID;

-        getData().setSessionID(sessionID);

-    }

-

-    /**

-     * Returns the session ID of this execution.

-     * 

-     * @return the unique session id of this execution

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    /**

-     * Sets the JID of the command host. This is automatically handled and should

-     * not be called.

-     * 

-     * @param ownerJID the JID of the owner.

-     */

-    public void setOwnerJID(String ownerJID) {

-        this.ownerJID = ownerJID;

-    }

-

-    @Override

-    public String getOwnerJID() {

-        return ownerJID;

-    }

-

-    /**

-     * Returns the date the command was created.

-     * 

-     * @return the date the command was created.

-     */

-    public long getCreationDate() {

-        return creationDate;

-    }

-

-    /**

-     * Returns true if the current stage is the last one. If it is then the

-     * execution of some action will complete the execution of the command.

-     * Commands that don't have multiple stages can always return <tt>true</tt>.

-     * 

-     * @return true if the command is in the last stage.

-     */

-    public abstract boolean isLastStage();

-

-    /**

-     * Returns true if the specified requester has permission to execute all the

-     * stages of this action. This is checked when the first request is received,

-     * if the permission is grant then the requester will be able to execute

-     * all the stages of the command. It is not checked again during the

-     * execution.

-     *

-     * @param jid the JID to check permissions on.

-     * @return true if the user has permission to execute this action.

-     */

-    public abstract boolean hasPermission(String jid);

-

-    /**

-     * Returns the currently executing stage number. The first stage number is

-     * 0. During the execution of the first action this method will answer 0.

-     *

-     * @return the current stage number.

-     */

-    public int getCurrentStage() {

-        return currenStage;

-    }

-

-    @Override

-    void setData(AdHocCommandData data) {

-        data.setSessionID(sessionID);

-        super.setData(data);

-    }

-

-    /**

-     * Increase the current stage number. This is automatically handled and should

-     * not be called.

-     * 

-     */

-    void incrementStage() {

-        currenStage++;

-    }

-

-    /**

-     * Decrease the current stage number. This is automatically handled and should

-     * not be called.

-     * 

-     */

-    void decrementStage() {

-        currenStage--;

-    }

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-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.smackx.commands;
+
+import org.jivesoftware.smackx.packet.AdHocCommandData;
+
+/**
+ * Represents a command that can be executed locally from a remote location. This
+ * class must be extended to implement an specific ad-hoc command. This class
+ * provides some useful tools:<ul>
+ *      <li>Node</li>
+ *      <li>Name</li>
+ *      <li>Session ID</li>
+ *      <li>Current Stage</li>
+ *      <li>Available actions</li>
+ *      <li>Default action</li>
+ * </ul><p/>
+ * To implement a new command extend this class and implement all the abstract
+ * methods. When implementing the actions remember that they could be invoked
+ * several times, and that you must use the current stage number to know what to
+ * do.
+ * 
+ * @author Gabriel Guardincerri
+ */
+public abstract class LocalCommand extends AdHocCommand {
+
+    /**
+     * The time stamp of first invokation of the command. Used to implement the session timeout.
+     */
+    private long creationDate;
+
+    /**
+     * The unique ID of the execution of the command.
+     */
+    private String sessionID;
+
+    /**
+     * The full JID of the host of the command.
+     */
+    private String ownerJID;
+
+    /**
+     * The number of the current stage.
+     */
+    private int currenStage;
+
+    public LocalCommand() {
+        super();
+        this.creationDate = System.currentTimeMillis();
+        currenStage = -1;
+    }
+
+    /**
+     * The sessionID is an unique identifier of an execution request. This is
+     * automatically handled and should not be called.
+     * 
+     * @param sessionID the unique session id of this execution
+     */
+    public void setSessionID(String sessionID) {
+        this.sessionID = sessionID;
+        getData().setSessionID(sessionID);
+    }
+
+    /**
+     * Returns the session ID of this execution.
+     * 
+     * @return the unique session id of this execution
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    /**
+     * Sets the JID of the command host. This is automatically handled and should
+     * not be called.
+     * 
+     * @param ownerJID the JID of the owner.
+     */
+    public void setOwnerJID(String ownerJID) {
+        this.ownerJID = ownerJID;
+    }
+
+    @Override
+    public String getOwnerJID() {
+        return ownerJID;
+    }
+
+    /**
+     * Returns the date the command was created.
+     * 
+     * @return the date the command was created.
+     */
+    public long getCreationDate() {
+        return creationDate;
+    }
+
+    /**
+     * Returns true if the current stage is the last one. If it is then the
+     * execution of some action will complete the execution of the command.
+     * Commands that don't have multiple stages can always return <tt>true</tt>.
+     * 
+     * @return true if the command is in the last stage.
+     */
+    public abstract boolean isLastStage();
+
+    /**
+     * Returns true if the specified requester has permission to execute all the
+     * stages of this action. This is checked when the first request is received,
+     * if the permission is grant then the requester will be able to execute
+     * all the stages of the command. It is not checked again during the
+     * execution.
+     *
+     * @param jid the JID to check permissions on.
+     * @return true if the user has permission to execute this action.
+     */
+    public abstract boolean hasPermission(String jid);
+
+    /**
+     * Returns the currently executing stage number. The first stage number is
+     * 0. During the execution of the first action this method will answer 0.
+     *
+     * @return the current stage number.
+     */
+    public int getCurrentStage() {
+        return currenStage;
+    }
+
+    @Override
+    void setData(AdHocCommandData data) {
+        data.setSessionID(sessionID);
+        super.setData(data);
+    }
+
+    /**
+     * Increase the current stage number. This is automatically handled and should
+     * not be called.
+     * 
+     */
+    void incrementStage() {
+        currenStage++;
+    }
+
+    /**
+     * Decrease the current stage number. This is automatically handled and should
+     * not be called.
+     * 
+     */
+    void decrementStage() {
+        currenStage--;
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransfer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransfer.java
index b840fd5..13d2c5a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransfer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransfer.java
@@ -1,380 +1,380 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smack.XMPPException;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-/**

- * Contains the generic file information and progress related to a particular

- * file transfer.

- *

- * @author Alexander Wenckus

- *

- */

-public abstract class FileTransfer {

-

-	private String fileName;

-

-	private String filePath;

-

-	private long fileSize;

-

-	private String peer;

-

-	private Status status = Status.initial;

-

-    private final Object statusMonitor = new Object();

-

-	protected FileTransferNegotiator negotiator;

-

-	protected String streamID;

-

-	protected long amountWritten = -1;

-

-	private Error error;

-

-	private Exception exception;

-

-    /**

-     * Buffer size between input and output

-     */

-    private static final int BUFFER_SIZE = 8192;

-

-    protected FileTransfer(String peer, String streamID,

-			FileTransferNegotiator negotiator) {

-		this.peer = peer;

-		this.streamID = streamID;

-		this.negotiator = negotiator;

-	}

-

-	protected void setFileInfo(String fileName, long fileSize) {

-		this.fileName = fileName;

-		this.fileSize = fileSize;

-	}

-

-	protected void setFileInfo(String path, String fileName, long fileSize) {

-		this.filePath = path;

-		this.fileName = fileName;

-		this.fileSize = fileSize;

-	}

-

-	/**

-	 * Returns the size of the file being transfered.

-	 *

-	 * @return Returns the size of the file being transfered.

-	 */

-	public long getFileSize() {

-		return fileSize;

-	}

-

-	/**

-	 * Returns the name of the file being transfered.

-	 *

-	 * @return Returns the name of the file being transfered.

-	 */

-	public String getFileName() {

-		return fileName;

-	}

-

-	/**

-	 * Returns the local path of the file.

-	 *

-	 * @return Returns the local path of the file.

-	 */

-	public String getFilePath() {

-		return filePath;

-	}

-

-	/**

-	 * Returns the JID of the peer for this file transfer.

-	 *

-	 * @return Returns the JID of the peer for this file transfer.

-	 */

-	public String getPeer() {

-		return peer;

-	}

-

-	/**

-	 * Returns the progress of the file transfer as a number between 0 and 1.

-	 *

-	 * @return Returns the progress of the file transfer as a number between 0

-	 *         and 1.

-	 */

-	public double getProgress() {

-        if (amountWritten <= 0 || fileSize <= 0) {

-            return 0;

-        }

-        return (double) amountWritten / (double) fileSize;

-	}

-

-	/**

-	 * Returns true if the transfer has been cancelled, if it has stopped because

-	 * of a an error, or the transfer completed successfully.

-	 *

-	 * @return Returns true if the transfer has been cancelled, if it has stopped

-	 *         because of a an error, or the transfer completed successfully.

-	 */

-	public boolean isDone() {

-		return status == Status.cancelled || status == Status.error

-				|| status == Status.complete || status == Status.refused;

-	}

-

-	/**

-	 * Returns the current status of the file transfer.

-	 *

-	 * @return Returns the current status of the file transfer.

-	 */

-	public Status getStatus() {

-		return status;

-	}

-

-	protected void setError(Error type) {

-		this.error = type;

-	}

-

-	/**

-	 * When {@link #getStatus()} returns that there was an {@link Status#error}

-	 * during the transfer, the type of error can be retrieved through this

-	 * method.

-	 *

-	 * @return Returns the type of error that occurred if one has occurred.

-	 */

-	public Error getError() {

-		return error;

-	}

-

-	/**

-	 * If an exception occurs asynchronously it will be stored for later

-	 * retrieval. If there is an error there maybe an exception set.

-	 *

-	 * @return The exception that occurred or null if there was no exception.

-	 * @see #getError()

-	 */

-	public Exception getException() {

-		return exception;

-	}

-

-    public String getStreamID() {

-        return streamID;

-    }

-

-	/**

-	 * Cancels the file transfer.

-	 */

-	public abstract void cancel();

-

-	protected void setException(Exception exception) {

-		this.exception = exception;

-	}

-

-	protected void setStatus(Status status) {

-        synchronized (statusMonitor) {

-		    this.status = status;

-	    }

-    }

-

-    protected boolean updateStatus(Status oldStatus, Status newStatus) {

-        synchronized (statusMonitor) {

-            if (oldStatus != status) {

-                return false;

-            }

-            status = newStatus;

-            return true;

-        }

-    }

-

-	protected void writeToStream(final InputStream in, final OutputStream out)

-			throws XMPPException

-    {

-		final byte[] b = new byte[BUFFER_SIZE];

-		int count = 0;

-		amountWritten = 0;

-

-        do {

-			// write to the output stream

-			try {

-				out.write(b, 0, count);

-			} catch (IOException e) {

-				throw new XMPPException("error writing to output stream", e);

-			}

-

-			amountWritten += count;

-

-			// read more bytes from the input stream

-			try {

-				count = in.read(b);

-			} catch (IOException e) {

-				throw new XMPPException("error reading from input stream", e);

-			}

-		} while (count != -1 && !getStatus().equals(Status.cancelled));

-

-		// the connection was likely terminated abrubtly if these are not equal

-		if (!getStatus().equals(Status.cancelled) && getError() == Error.none

-				&& amountWritten != fileSize) {

-            setStatus(Status.error);

-			this.error = Error.connection;

-		}

-	}

-

-	/**

-	 * A class to represent the current status of the file transfer.

-	 *

-	 * @author Alexander Wenckus

-	 *

-	 */

-	public enum Status {

-

-		/**

-		 * An error occurred during the transfer.

-		 *

-		 * @see FileTransfer#getError()

-		 */

-		error("Error"),

-

-		/**

-         * The initial status of the file transfer.

-         */

-        initial("Initial"),

-

-        /**

-		 * The file transfer is being negotiated with the peer. The party

-		 * Receiving the file has the option to accept or refuse a file transfer

-		 * request. If they accept, then the process of stream negotiation will

-		 * begin. If they refuse the file will not be transfered.

-		 *

-		 * @see #negotiating_stream

-		 */

-		negotiating_transfer("Negotiating Transfer"),

-

-		/**

-		 * The peer has refused the file transfer request halting the file

-		 * transfer negotiation process.

-		 */

-		refused("Refused"),

-

-		/**

-		 * The stream to transfer the file is being negotiated over the chosen

-		 * stream type. After the stream negotiating process is complete the

-		 * status becomes negotiated.

-		 *

-		 * @see #negotiated

-		 */

-		negotiating_stream("Negotiating Stream"),

-

-		/**

-		 * After the stream negotiation has completed the intermediate state

-		 * between the time when the negotiation is finished and the actual

-		 * transfer begins.

-		 */

-		negotiated("Negotiated"),

-

-		/**

-		 * The transfer is in progress.

-		 *

-		 * @see FileTransfer#getProgress()

-		 */

-		in_progress("In Progress"),

-

-		/**

-		 * The transfer has completed successfully.

-		 */

-		complete("Complete"),

-

-		/**

-		 * The file transfer was cancelled

-		 */

-		cancelled("Cancelled");

-

-        private String status;

-

-        private Status(String status) {

-            this.status = status;

-        }

-

-        public String toString() {

-            return status;

-        }

-    }

-

-    /**

-     * Return the length of bytes written out to the stream.

-     * @return the amount in bytes written out.

-     */

-    public long getAmountWritten(){

-        return amountWritten;

-    }

-

-    public enum Error {

-		/**

-		 * No error

-		 */

-		none("No error"),

-

-		/**

-		 * The peer did not find any of the provided stream mechanisms

-		 * acceptable.

-		 */

-		not_acceptable("The peer did not find any of the provided stream mechanisms acceptable."),

-

-		/**

-		 * The provided file to transfer does not exist or could not be read.

-		 */

-		bad_file("The provided file to transfer does not exist or could not be read."),

-

-		/**

-		 * The remote user did not respond or the connection timed out.

-		 */

-		no_response("The remote user did not respond or the connection timed out."),

-

-		/**

-		 * An error occurred over the socket connected to send the file.

-		 */

-		connection("An error occured over the socket connected to send the file."),

-

-		/**

-		 * An error occurred while sending or receiving the file

-		 */

-		stream("An error occured while sending or recieving the file.");

-

-		private final String msg;

-

-		private Error(String msg) {

-			this.msg = msg;

-		}

-

-		/**

-		 * Returns a String representation of this error.

-		 *

-		 * @return Returns a String representation of this error.

-		 */

-		public String getMessage() {

-			return msg;

-		}

-

-		public String toString() {

-			return msg;

-		}

-	}

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smack.XMPPException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Contains the generic file information and progress related to a particular
+ * file transfer.
+ *
+ * @author Alexander Wenckus
+ *
+ */
+public abstract class FileTransfer {
+
+	private String fileName;
+
+	private String filePath;
+
+	private long fileSize;
+
+	private String peer;
+
+	private Status status = Status.initial;
+
+    private final Object statusMonitor = new Object();
+
+	protected FileTransferNegotiator negotiator;
+
+	protected String streamID;
+
+	protected long amountWritten = -1;
+
+	private Error error;
+
+	private Exception exception;
+
+    /**
+     * Buffer size between input and output
+     */
+    private static final int BUFFER_SIZE = 8192;
+
+    protected FileTransfer(String peer, String streamID,
+			FileTransferNegotiator negotiator) {
+		this.peer = peer;
+		this.streamID = streamID;
+		this.negotiator = negotiator;
+	}
+
+	protected void setFileInfo(String fileName, long fileSize) {
+		this.fileName = fileName;
+		this.fileSize = fileSize;
+	}
+
+	protected void setFileInfo(String path, String fileName, long fileSize) {
+		this.filePath = path;
+		this.fileName = fileName;
+		this.fileSize = fileSize;
+	}
+
+	/**
+	 * Returns the size of the file being transfered.
+	 *
+	 * @return Returns the size of the file being transfered.
+	 */
+	public long getFileSize() {
+		return fileSize;
+	}
+
+	/**
+	 * Returns the name of the file being transfered.
+	 *
+	 * @return Returns the name of the file being transfered.
+	 */
+	public String getFileName() {
+		return fileName;
+	}
+
+	/**
+	 * Returns the local path of the file.
+	 *
+	 * @return Returns the local path of the file.
+	 */
+	public String getFilePath() {
+		return filePath;
+	}
+
+	/**
+	 * Returns the JID of the peer for this file transfer.
+	 *
+	 * @return Returns the JID of the peer for this file transfer.
+	 */
+	public String getPeer() {
+		return peer;
+	}
+
+	/**
+	 * Returns the progress of the file transfer as a number between 0 and 1.
+	 *
+	 * @return Returns the progress of the file transfer as a number between 0
+	 *         and 1.
+	 */
+	public double getProgress() {
+        if (amountWritten <= 0 || fileSize <= 0) {
+            return 0;
+        }
+        return (double) amountWritten / (double) fileSize;
+	}
+
+	/**
+	 * Returns true if the transfer has been cancelled, if it has stopped because
+	 * of a an error, or the transfer completed successfully.
+	 *
+	 * @return Returns true if the transfer has been cancelled, if it has stopped
+	 *         because of a an error, or the transfer completed successfully.
+	 */
+	public boolean isDone() {
+		return status == Status.cancelled || status == Status.error
+				|| status == Status.complete || status == Status.refused;
+	}
+
+	/**
+	 * Returns the current status of the file transfer.
+	 *
+	 * @return Returns the current status of the file transfer.
+	 */
+	public Status getStatus() {
+		return status;
+	}
+
+	protected void setError(Error type) {
+		this.error = type;
+	}
+
+	/**
+	 * When {@link #getStatus()} returns that there was an {@link Status#error}
+	 * during the transfer, the type of error can be retrieved through this
+	 * method.
+	 *
+	 * @return Returns the type of error that occurred if one has occurred.
+	 */
+	public Error getError() {
+		return error;
+	}
+
+	/**
+	 * If an exception occurs asynchronously it will be stored for later
+	 * retrieval. If there is an error there maybe an exception set.
+	 *
+	 * @return The exception that occurred or null if there was no exception.
+	 * @see #getError()
+	 */
+	public Exception getException() {
+		return exception;
+	}
+
+    public String getStreamID() {
+        return streamID;
+    }
+
+	/**
+	 * Cancels the file transfer.
+	 */
+	public abstract void cancel();
+
+	protected void setException(Exception exception) {
+		this.exception = exception;
+	}
+
+	protected void setStatus(Status status) {
+        synchronized (statusMonitor) {
+		    this.status = status;
+	    }
+    }
+
+    protected boolean updateStatus(Status oldStatus, Status newStatus) {
+        synchronized (statusMonitor) {
+            if (oldStatus != status) {
+                return false;
+            }
+            status = newStatus;
+            return true;
+        }
+    }
+
+	protected void writeToStream(final InputStream in, final OutputStream out)
+			throws XMPPException
+    {
+		final byte[] b = new byte[BUFFER_SIZE];
+		int count = 0;
+		amountWritten = 0;
+
+        do {
+			// write to the output stream
+			try {
+				out.write(b, 0, count);
+			} catch (IOException e) {
+				throw new XMPPException("error writing to output stream", e);
+			}
+
+			amountWritten += count;
+
+			// read more bytes from the input stream
+			try {
+				count = in.read(b);
+			} catch (IOException e) {
+				throw new XMPPException("error reading from input stream", e);
+			}
+		} while (count != -1 && !getStatus().equals(Status.cancelled));
+
+		// the connection was likely terminated abrubtly if these are not equal
+		if (!getStatus().equals(Status.cancelled) && getError() == Error.none
+				&& amountWritten != fileSize) {
+            setStatus(Status.error);
+			this.error = Error.connection;
+		}
+	}
+
+	/**
+	 * A class to represent the current status of the file transfer.
+	 *
+	 * @author Alexander Wenckus
+	 *
+	 */
+	public enum Status {
+
+		/**
+		 * An error occurred during the transfer.
+		 *
+		 * @see FileTransfer#getError()
+		 */
+		error("Error"),
+
+		/**
+         * The initial status of the file transfer.
+         */
+        initial("Initial"),
+
+        /**
+		 * The file transfer is being negotiated with the peer. The party
+		 * Receiving the file has the option to accept or refuse a file transfer
+		 * request. If they accept, then the process of stream negotiation will
+		 * begin. If they refuse the file will not be transfered.
+		 *
+		 * @see #negotiating_stream
+		 */
+		negotiating_transfer("Negotiating Transfer"),
+
+		/**
+		 * The peer has refused the file transfer request halting the file
+		 * transfer negotiation process.
+		 */
+		refused("Refused"),
+
+		/**
+		 * The stream to transfer the file is being negotiated over the chosen
+		 * stream type. After the stream negotiating process is complete the
+		 * status becomes negotiated.
+		 *
+		 * @see #negotiated
+		 */
+		negotiating_stream("Negotiating Stream"),
+
+		/**
+		 * After the stream negotiation has completed the intermediate state
+		 * between the time when the negotiation is finished and the actual
+		 * transfer begins.
+		 */
+		negotiated("Negotiated"),
+
+		/**
+		 * The transfer is in progress.
+		 *
+		 * @see FileTransfer#getProgress()
+		 */
+		in_progress("In Progress"),
+
+		/**
+		 * The transfer has completed successfully.
+		 */
+		complete("Complete"),
+
+		/**
+		 * The file transfer was cancelled
+		 */
+		cancelled("Cancelled");
+
+        private String status;
+
+        private Status(String status) {
+            this.status = status;
+        }
+
+        public String toString() {
+            return status;
+        }
+    }
+
+    /**
+     * Return the length of bytes written out to the stream.
+     * @return the amount in bytes written out.
+     */
+    public long getAmountWritten(){
+        return amountWritten;
+    }
+
+    public enum Error {
+		/**
+		 * No error
+		 */
+		none("No error"),
+
+		/**
+		 * The peer did not find any of the provided stream mechanisms
+		 * acceptable.
+		 */
+		not_acceptable("The peer did not find any of the provided stream mechanisms acceptable."),
+
+		/**
+		 * The provided file to transfer does not exist or could not be read.
+		 */
+		bad_file("The provided file to transfer does not exist or could not be read."),
+
+		/**
+		 * The remote user did not respond or the connection timed out.
+		 */
+		no_response("The remote user did not respond or the connection timed out."),
+
+		/**
+		 * An error occurred over the socket connected to send the file.
+		 */
+		connection("An error occured over the socket connected to send the file."),
+
+		/**
+		 * An error occurred while sending or receiving the file
+		 */
+		stream("An error occured while sending or recieving the file.");
+
+		private final String msg;
+
+		private Error(String msg) {
+			this.msg = msg;
+		}
+
+		/**
+		 * Returns a String representation of this error.
+		 *
+		 * @return Returns a String representation of this error.
+		 */
+		public String getMessage() {
+			return msg;
+		}
+
+		public String toString() {
+			return msg;
+		}
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferListener.java
index 8e07543..904623c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferListener.java
@@ -1,36 +1,36 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-/**

- * File transfers can cause several events to be raised. These events can be

- * monitored through this interface.

- * 

- * @author Alexander Wenckus

- */

-public interface FileTransferListener {

-	/**

-	 * A request to send a file has been recieved from another user.

-	 * 

-	 * @param request

-	 *            The request from the other user.

-	 */

-	public void fileTransferRequest(final FileTransferRequest request);

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+/**
+ * File transfers can cause several events to be raised. These events can be
+ * monitored through this interface.
+ * 
+ * @author Alexander Wenckus
+ */
+public interface FileTransferListener {
+	/**
+	 * A request to send a file has been recieved from another user.
+	 * 
+	 * @param request
+	 *            The request from the other user.
+	 */
+	public void fileTransferRequest(final FileTransferRequest request);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferManager.java
index 6e413fa..493fda8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferManager.java
@@ -1,182 +1,182 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.IQTypeFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-import java.util.ArrayList;

-import java.util.List;

-

-/**

- * The file transfer manager class handles the sending and recieving of files.

- * To send a file invoke the {@link #createOutgoingFileTransfer(String)} method.

- * <p>

- * And to recieve a file add a file transfer listener to the manager. The

- * listener will notify you when there is a new file transfer request. To create

- * the {@link IncomingFileTransfer} object accept the transfer, or, if the

- * transfer is not desirable reject it.

- * 

- * @author Alexander Wenckus

- * 

- */

-public class FileTransferManager {

-

-	private final FileTransferNegotiator fileTransferNegotiator;

-

-	private List<FileTransferListener> listeners;

-

-	private Connection connection;

-

-	/**

-	 * Creates a file transfer manager to initiate and receive file transfers.

-	 * 

-	 * @param connection

-	 *            The Connection that the file transfers will use.

-	 */

-	public FileTransferManager(Connection connection) {

-		this.connection = connection;

-		this.fileTransferNegotiator = FileTransferNegotiator

-				.getInstanceFor(connection);

-	}

-

-	/**

-	 * Add a file transfer listener to listen to incoming file transfer

-	 * requests.

-	 * 

-	 * @param li

-	 *            The listener

-	 * @see #removeFileTransferListener(FileTransferListener)

-	 * @see FileTransferListener

-	 */

-	public void addFileTransferListener(final FileTransferListener li) {

-		if (listeners == null) {

-			initListeners();

-		}

-		synchronized (this.listeners) {

-			listeners.add(li);

-		}

-	}

-

-	private void initListeners() {

-		listeners = new ArrayList<FileTransferListener>();

-

-		connection.addPacketListener(new PacketListener() {

-			public void processPacket(Packet packet) {

-				fireNewRequest((StreamInitiation) packet);

-			}

-		}, new AndFilter(new PacketTypeFilter(StreamInitiation.class),

-				new IQTypeFilter(IQ.Type.SET)));

-	}

-

-	protected void fireNewRequest(StreamInitiation initiation) {

-		FileTransferListener[] listeners = null;

-		synchronized (this.listeners) {

-			listeners = new FileTransferListener[this.listeners.size()];

-			this.listeners.toArray(listeners);

-		}

-		FileTransferRequest request = new FileTransferRequest(this, initiation);

-		for (int i = 0; i < listeners.length; i++) {

-			listeners[i].fileTransferRequest(request);

-		}

-	}

-

-	/**

-	 * Removes a file transfer listener.

-	 * 

-	 * @param li

-	 *            The file transfer listener to be removed

-	 * @see FileTransferListener

-	 */

-	public void removeFileTransferListener(final FileTransferListener li) {

-		if (listeners == null) {

-			return;

-		}

-		synchronized (this.listeners) {

-			listeners.remove(li);

-		}

-	}

-

-	/**

-	 * Creates an OutgoingFileTransfer to send a file to another user.

-	 * 

-	 * @param userID

-	 *            The fully qualified jabber ID (i.e. full JID) with resource of the user to

-	 *            send the file to.

-	 * @return The send file object on which the negotiated transfer can be run.

-	 * @exception IllegalArgumentException if userID is null or not a full JID

-	 */

-	public OutgoingFileTransfer createOutgoingFileTransfer(String userID) {

-        if (userID == null) {

-            throw new IllegalArgumentException("userID was null");

-        }

-        // We need to create outgoing file transfers with a full JID since this method will later

-        // use XEP-0095 to negotiate the stream. This is done with IQ stanzas that need to be addressed to a full JID

-        // in order to reach an client entity.

-        else if (!StringUtils.isFullJID(userID)) {

-            throw new IllegalArgumentException("The provided user id was not a full JID (i.e. with resource part)");

-        }

-

-		return new OutgoingFileTransfer(connection.getUser(), userID,

-				fileTransferNegotiator.getNextStreamID(),

-				fileTransferNegotiator);

-	}

-

-	/**

-	 * When the file transfer request is acceptable, this method should be

-	 * invoked. It will create an IncomingFileTransfer which allows the

-	 * transmission of the file to procede.

-	 * 

-	 * @param request

-	 *            The remote request that is being accepted.

-	 * @return The IncomingFileTransfer which manages the download of the file

-	 *         from the transfer initiator.

-	 */

-	protected IncomingFileTransfer createIncomingFileTransfer(

-			FileTransferRequest request) {

-		if (request == null) {

-			throw new NullPointerException("RecieveRequest cannot be null");

-		}

-

-		IncomingFileTransfer transfer = new IncomingFileTransfer(request,

-                fileTransferNegotiator);

-		transfer.setFileInfo(request.getFileName(), request.getFileSize());

-

-		return transfer;

-	}

-

-	protected void rejectIncomingFileTransfer(FileTransferRequest request) {

-		StreamInitiation initiation = request.getStreamInitiation();

-

-		IQ rejection = FileTransferNegotiator.createIQ(

-				initiation.getPacketID(), initiation.getFrom(), initiation

-						.getTo(), IQ.Type.ERROR);

-		rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable));

-		connection.sendPacket(rejection);

-	}

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.IQTypeFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The file transfer manager class handles the sending and recieving of files.
+ * To send a file invoke the {@link #createOutgoingFileTransfer(String)} method.
+ * <p>
+ * And to recieve a file add a file transfer listener to the manager. The
+ * listener will notify you when there is a new file transfer request. To create
+ * the {@link IncomingFileTransfer} object accept the transfer, or, if the
+ * transfer is not desirable reject it.
+ * 
+ * @author Alexander Wenckus
+ * 
+ */
+public class FileTransferManager {
+
+	private final FileTransferNegotiator fileTransferNegotiator;
+
+	private List<FileTransferListener> listeners;
+
+	private Connection connection;
+
+	/**
+	 * Creates a file transfer manager to initiate and receive file transfers.
+	 * 
+	 * @param connection
+	 *            The Connection that the file transfers will use.
+	 */
+	public FileTransferManager(Connection connection) {
+		this.connection = connection;
+		this.fileTransferNegotiator = FileTransferNegotiator
+				.getInstanceFor(connection);
+	}
+
+	/**
+	 * Add a file transfer listener to listen to incoming file transfer
+	 * requests.
+	 * 
+	 * @param li
+	 *            The listener
+	 * @see #removeFileTransferListener(FileTransferListener)
+	 * @see FileTransferListener
+	 */
+	public void addFileTransferListener(final FileTransferListener li) {
+		if (listeners == null) {
+			initListeners();
+		}
+		synchronized (this.listeners) {
+			listeners.add(li);
+		}
+	}
+
+	private void initListeners() {
+		listeners = new ArrayList<FileTransferListener>();
+
+		connection.addPacketListener(new PacketListener() {
+			public void processPacket(Packet packet) {
+				fireNewRequest((StreamInitiation) packet);
+			}
+		}, new AndFilter(new PacketTypeFilter(StreamInitiation.class),
+				new IQTypeFilter(IQ.Type.SET)));
+	}
+
+	protected void fireNewRequest(StreamInitiation initiation) {
+		FileTransferListener[] listeners = null;
+		synchronized (this.listeners) {
+			listeners = new FileTransferListener[this.listeners.size()];
+			this.listeners.toArray(listeners);
+		}
+		FileTransferRequest request = new FileTransferRequest(this, initiation);
+		for (int i = 0; i < listeners.length; i++) {
+			listeners[i].fileTransferRequest(request);
+		}
+	}
+
+	/**
+	 * Removes a file transfer listener.
+	 * 
+	 * @param li
+	 *            The file transfer listener to be removed
+	 * @see FileTransferListener
+	 */
+	public void removeFileTransferListener(final FileTransferListener li) {
+		if (listeners == null) {
+			return;
+		}
+		synchronized (this.listeners) {
+			listeners.remove(li);
+		}
+	}
+
+	/**
+	 * Creates an OutgoingFileTransfer to send a file to another user.
+	 * 
+	 * @param userID
+	 *            The fully qualified jabber ID (i.e. full JID) with resource of the user to
+	 *            send the file to.
+	 * @return The send file object on which the negotiated transfer can be run.
+	 * @exception IllegalArgumentException if userID is null or not a full JID
+	 */
+	public OutgoingFileTransfer createOutgoingFileTransfer(String userID) {
+        if (userID == null) {
+            throw new IllegalArgumentException("userID was null");
+        }
+        // We need to create outgoing file transfers with a full JID since this method will later
+        // use XEP-0095 to negotiate the stream. This is done with IQ stanzas that need to be addressed to a full JID
+        // in order to reach an client entity.
+        else if (!StringUtils.isFullJID(userID)) {
+            throw new IllegalArgumentException("The provided user id was not a full JID (i.e. with resource part)");
+        }
+
+		return new OutgoingFileTransfer(connection.getUser(), userID,
+				fileTransferNegotiator.getNextStreamID(),
+				fileTransferNegotiator);
+	}
+
+	/**
+	 * When the file transfer request is acceptable, this method should be
+	 * invoked. It will create an IncomingFileTransfer which allows the
+	 * transmission of the file to procede.
+	 * 
+	 * @param request
+	 *            The remote request that is being accepted.
+	 * @return The IncomingFileTransfer which manages the download of the file
+	 *         from the transfer initiator.
+	 */
+	protected IncomingFileTransfer createIncomingFileTransfer(
+			FileTransferRequest request) {
+		if (request == null) {
+			throw new NullPointerException("RecieveRequest cannot be null");
+		}
+
+		IncomingFileTransfer transfer = new IncomingFileTransfer(request,
+                fileTransferNegotiator);
+		transfer.setFileInfo(request.getFileName(), request.getFileSize());
+
+		return transfer;
+	}
+
+	protected void rejectIncomingFileTransfer(FileTransferRequest request) {
+		StreamInitiation initiation = request.getStreamInitiation();
+
+		IQ rejection = FileTransferNegotiator.createIQ(
+				initiation.getPacketID(), initiation.getFrom(), initiation
+						.getTo(), IQ.Type.ERROR);
+		rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable));
+		connection.sendPacket(rejection);
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java
index d1fb7bf..3b2b30d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java
@@ -1,485 +1,485 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import java.net.URLConnection;

-import java.util.ArrayList;

-import java.util.Arrays;

-import java.util.Collection;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

-import java.util.Random;

-import java.util.concurrent.ConcurrentHashMap;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.ConnectionListener;

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.ServiceDiscoveryManager;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-/**

- * Manages the negotiation of file transfers according to JEP-0096. If a file is

- * being sent the remote user chooses the type of stream under which the file

- * will be sent.

- *

- * @author Alexander Wenckus

- * @see <a href="http://xmpp.org/extensions/xep-0096.html">XEP-0096: SI File Transfer</a>

- */

-public class FileTransferNegotiator {

-

-    // Static

-

-    private static final String[] NAMESPACE = {

-            "http://jabber.org/protocol/si/profile/file-transfer",

-            "http://jabber.org/protocol/si"};

-

-    private static final Map<Connection, FileTransferNegotiator> transferObject =

-            new ConcurrentHashMap<Connection, FileTransferNegotiator>();

-

-    private static final String STREAM_INIT_PREFIX = "jsi_";

-

-    protected static final String STREAM_DATA_FIELD_NAME = "stream-method";

-

-    private static final Random randomGenerator = new Random();

-

-    /**

-     * A static variable to use only offer IBB for file transfer. It is generally recommend to only

-     * set this variable to true for testing purposes as IBB is the backup file transfer method

-     * and shouldn't be used as the only transfer method in production systems.

-     */

-    public static boolean IBB_ONLY = (System.getProperty("ibb") != null);//true;

-

-    /**

-     * Returns the file transfer negotiator related to a particular connection.

-     * When this class is requested on a particular connection the file transfer

-     * service is automatically enabled.

-     *

-     * @param connection The connection for which the transfer manager is desired

-     * @return The IMFileTransferManager

-     */

-    public static FileTransferNegotiator getInstanceFor(

-            final Connection connection) {

-        if (connection == null) {

-            throw new IllegalArgumentException("Connection cannot be null");

-        }

-        if (!connection.isConnected()) {

-            return null;

-        }

-

-        if (transferObject.containsKey(connection)) {

-            return transferObject.get(connection);

-        }

-        else {

-            FileTransferNegotiator transfer = new FileTransferNegotiator(

-                    connection);

-            setServiceEnabled(connection, true);

-            transferObject.put(connection, transfer);

-            return transfer;

-        }

-    }

-

-    /**

-     * Enable the Jabber services related to file transfer on the particular

-     * connection.

-     *

-     * @param connection The connection on which to enable or disable the services.

-     * @param isEnabled  True to enable, false to disable.

-     */

-    public static void setServiceEnabled(final Connection connection,

-            final boolean isEnabled) {

-        ServiceDiscoveryManager manager = ServiceDiscoveryManager

-                .getInstanceFor(connection);

-

-        List<String> namespaces = new ArrayList<String>();

-        namespaces.addAll(Arrays.asList(NAMESPACE));

-        namespaces.add(InBandBytestreamManager.NAMESPACE);

-        if (!IBB_ONLY) {

-            namespaces.add(Socks5BytestreamManager.NAMESPACE);

-        }

-

-        for (String namespace : namespaces) {

-            if (isEnabled) {

-                if (!manager.includesFeature(namespace)) {

-                    manager.addFeature(namespace);

-                }

-            } else {

-                manager.removeFeature(namespace);

-            }

-        }

-        

-    }

-

-    /**

-     * Checks to see if all file transfer related services are enabled on the

-     * connection.

-     *

-     * @param connection The connection to check

-     * @return True if all related services are enabled, false if they are not.

-     */

-    public static boolean isServiceEnabled(final Connection connection) {

-        ServiceDiscoveryManager manager = ServiceDiscoveryManager

-                .getInstanceFor(connection);

-

-        List<String> namespaces = new ArrayList<String>();

-        namespaces.addAll(Arrays.asList(NAMESPACE));

-        namespaces.add(InBandBytestreamManager.NAMESPACE);

-        if (!IBB_ONLY) {

-            namespaces.add(Socks5BytestreamManager.NAMESPACE);

-        }

-

-        for (String namespace : namespaces) {

-            if (!manager.includesFeature(namespace)) {

-                return false;

-            }

-        }

-        return true;

-    }

-

-    /**

-     * A convenience method to create an IQ packet.

-     *

-     * @param ID   The packet ID of the

-     * @param to   To whom the packet is addressed.

-     * @param from From whom the packet is sent.

-     * @param type The IQ type of the packet.

-     * @return The created IQ packet.

-     */

-    public static IQ createIQ(final String ID, final String to,

-            final String from, final IQ.Type type) {

-        IQ iqPacket = new IQ() {

-            public String getChildElementXML() {

-                return null;

-            }

-        };

-        iqPacket.setPacketID(ID);

-        iqPacket.setTo(to);

-        iqPacket.setFrom(from);

-        iqPacket.setType(type);

-

-        return iqPacket;

-    }

-

-    /**

-     * Returns a collection of the supported transfer protocols.

-     *

-     * @return Returns a collection of the supported transfer protocols.

-     */

-    public static Collection<String> getSupportedProtocols() {

-        List<String> protocols = new ArrayList<String>();

-        protocols.add(InBandBytestreamManager.NAMESPACE);

-        if (!IBB_ONLY) {

-            protocols.add(Socks5BytestreamManager.NAMESPACE);

-        }

-        return Collections.unmodifiableList(protocols);

-    }

-

-    // non-static

-

-    private final Connection connection;

-

-    private final StreamNegotiator byteStreamTransferManager;

-

-    private final StreamNegotiator inbandTransferManager;

-

-    private FileTransferNegotiator(final Connection connection) {

-        configureConnection(connection);

-

-        this.connection = connection;

-        byteStreamTransferManager = new Socks5TransferNegotiator(connection);

-        inbandTransferManager = new IBBTransferNegotiator(connection);

-    }

-

-    private void configureConnection(final Connection connection) {

-        connection.addConnectionListener(new ConnectionListener() {

-            public void connectionClosed() {

-                cleanup(connection);

-            }

-

-            public void connectionClosedOnError(Exception e) {

-                cleanup(connection);

-            }

-

-            public void reconnectionFailed(Exception e) {

-                // ignore

-            }

-

-            public void reconnectionSuccessful() {

-                // ignore

-            }

-

-            public void reconnectingIn(int seconds) {

-                // ignore

-            }

-        });

-    }

-

-    private void cleanup(final Connection connection) {

-        if (transferObject.remove(connection) != null) {

-            inbandTransferManager.cleanup();

-        }

-    }

-

-    /**

-     * Selects an appropriate stream negotiator after examining the incoming file transfer request.

-     *

-     * @param request The related file transfer request.

-     * @return The file transfer object that handles the transfer

-     * @throws XMPPException If there are either no stream methods contained in the packet, or

-     *                       there is not an appropriate stream method.

-     */

-    public StreamNegotiator selectStreamNegotiator(

-            FileTransferRequest request) throws XMPPException {

-        StreamInitiation si = request.getStreamInitiation();

-        FormField streamMethodField = getStreamMethodField(si

-                .getFeatureNegotiationForm());

-

-        if (streamMethodField == null) {

-            String errorMessage = "No stream methods contained in packet.";

-            XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage);

-            IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),

-                    IQ.Type.ERROR);

-            iqPacket.setError(error);

-            connection.sendPacket(iqPacket);

-            throw new XMPPException(errorMessage, error);

-        }

-

-        // select the appropriate protocol

-

-        StreamNegotiator selectedStreamNegotiator;

-        try {

-            selectedStreamNegotiator = getNegotiator(streamMethodField);

-        }

-        catch (XMPPException e) {

-            IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),

-                    IQ.Type.ERROR);

-            iqPacket.setError(e.getXMPPError());

-            connection.sendPacket(iqPacket);

-            throw e;

-        }

-

-        // return the appropriate negotiator

-

-        return selectedStreamNegotiator;

-    }

-

-    private FormField getStreamMethodField(DataForm form) {

-        FormField field = null;

-        for (Iterator<FormField> it = form.getFields(); it.hasNext();) {

-            field = it.next();

-            if (field.getVariable().equals(STREAM_DATA_FIELD_NAME)) {

-                break;

-            }

-            field = null;

-        }

-        return field;

-    }

-

-    private StreamNegotiator getNegotiator(final FormField field)

-            throws XMPPException {

-        String variable;

-        boolean isByteStream = false;

-        boolean isIBB = false;

-        for (Iterator<FormField.Option> it = field.getOptions(); it.hasNext();) {

-            variable = it.next().getValue();

-            if (variable.equals(Socks5BytestreamManager.NAMESPACE) && !IBB_ONLY) {

-                isByteStream = true;

-            }

-            else if (variable.equals(InBandBytestreamManager.NAMESPACE)) {

-                isIBB = true;

-            }

-        }

-

-        if (!isByteStream && !isIBB) {

-            XMPPError error = new XMPPError(XMPPError.Condition.bad_request,

-                    "No acceptable transfer mechanism");

-            throw new XMPPException(error.getMessage(), error);

-        }

-

-       //if (isByteStream && isIBB && field.getType().equals(FormField.TYPE_LIST_MULTI)) {

-        if (isByteStream && isIBB) { 

-            return new FaultTolerantNegotiator(connection,

-                    byteStreamTransferManager,

-                    inbandTransferManager);

-        }

-        else if (isByteStream) {

-            return byteStreamTransferManager;

-        }

-        else {

-            return inbandTransferManager;

-        }

-    }

-

-    /**

-     * Reject a stream initiation request from a remote user.

-     *

-     * @param si The Stream Initiation request to reject.

-     */

-    public void rejectStream(final StreamInitiation si) {

-        XMPPError error = new XMPPError(XMPPError.Condition.forbidden, "Offer Declined");

-        IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),

-                IQ.Type.ERROR);

-        iqPacket.setError(error);

-        connection.sendPacket(iqPacket);

-    }

-

-    /**

-     * Returns a new, unique, stream ID to identify a file transfer.

-     *

-     * @return Returns a new, unique, stream ID to identify a file transfer.

-     */

-    public String getNextStreamID() {

-        StringBuilder buffer = new StringBuilder();

-        buffer.append(STREAM_INIT_PREFIX);

-        buffer.append(Math.abs(randomGenerator.nextLong()));

-

-        return buffer.toString();

-    }

-

-    /**

-     * Send a request to another user to send them a file. The other user has

-     * the option of, accepting, rejecting, or not responding to a received file

-     * transfer request.

-     * <p/>

-     * If they accept, the packet will contain the other user's chosen stream

-     * type to send the file across. The two choices this implementation

-     * provides to the other user for file transfer are <a

-     * href="http://www.jabber.org/jeps/jep-0065.html">SOCKS5 Bytestreams</a>,

-     * which is the preferred method of transfer, and <a

-     * href="http://www.jabber.org/jeps/jep-0047.html">In-Band Bytestreams</a>,

-     * which is the fallback mechanism.

-     * <p/>

-     * The other user may choose to decline the file request if they do not

-     * desire the file, their client does not support JEP-0096, or if there are

-     * no acceptable means to transfer the file.

-     * <p/>

-     * Finally, if the other user does not respond this method will return null

-     * after the specified timeout.

-     *

-     * @param userID          The userID of the user to whom the file will be sent.

-     * @param streamID        The unique identifier for this file transfer.

-     * @param fileName        The name of this file. Preferably it should include an

-     *                        extension as it is used to determine what type of file it is.

-     * @param size            The size, in bytes, of the file.

-     * @param desc            A description of the file.

-     * @param responseTimeout The amount of time, in milliseconds, to wait for the remote

-     *                        user to respond. If they do not respond in time, this

-     * @return Returns the stream negotiator selected by the peer.

-     * @throws XMPPException Thrown if there is an error negotiating the file transfer.

-     */

-    public StreamNegotiator negotiateOutgoingTransfer(final String userID,

-            final String streamID, final String fileName, final long size,

-            final String desc, int responseTimeout) throws XMPPException {

-        StreamInitiation si = new StreamInitiation();

-        si.setSesssionID(streamID);

-        si.setMimeType(URLConnection.guessContentTypeFromName(fileName));

-

-        StreamInitiation.File siFile = new StreamInitiation.File(fileName, size);

-        siFile.setDesc(desc);

-        si.setFile(siFile);

-

-        si.setFeatureNegotiationForm(createDefaultInitiationForm());

-

-        si.setFrom(connection.getUser());

-        si.setTo(userID);

-        si.setType(IQ.Type.SET);

-

-        PacketCollector collector = connection

-                .createPacketCollector(new PacketIDFilter(si.getPacketID()));

-        connection.sendPacket(si);

-        Packet siResponse = collector.nextResult(responseTimeout);

-        collector.cancel();

-

-        if (siResponse instanceof IQ) {

-            IQ iqResponse = (IQ) siResponse;

-            if (iqResponse.getType().equals(IQ.Type.RESULT)) {

-                StreamInitiation response = (StreamInitiation) siResponse;

-                return getOutgoingNegotiator(getStreamMethodField(response

-                        .getFeatureNegotiationForm()));

-

-            }

-            else if (iqResponse.getType().equals(IQ.Type.ERROR)) {

-                throw new XMPPException(iqResponse.getError());

-            }

-            else {

-                throw new XMPPException("File transfer response unreadable");

-            }

-        }

-        else {

-            return null;

-        }

-    }

-

-    private StreamNegotiator getOutgoingNegotiator(final FormField field)

-            throws XMPPException {

-        String variable;

-        boolean isByteStream = false;

-        boolean isIBB = false;

-        for (Iterator<String> it = field.getValues(); it.hasNext();) {

-            variable = it.next();

-            if (variable.equals(Socks5BytestreamManager.NAMESPACE) && !IBB_ONLY) {

-                isByteStream = true;

-            }

-            else if (variable.equals(InBandBytestreamManager.NAMESPACE)) {

-                isIBB = true;

-            }

-        }

-

-        if (!isByteStream && !isIBB) {

-            XMPPError error = new XMPPError(XMPPError.Condition.bad_request,

-                    "No acceptable transfer mechanism");

-            throw new XMPPException(error.getMessage(), error);

-        }

-

-        if (isByteStream && isIBB) {

-            return new FaultTolerantNegotiator(connection,

-                    byteStreamTransferManager, inbandTransferManager);

-        }

-        else if (isByteStream) {

-            return byteStreamTransferManager;

-        }

-        else {

-            return inbandTransferManager;

-        }

-    }

-

-    private DataForm createDefaultInitiationForm() {

-        DataForm form = new DataForm(Form.TYPE_FORM);

-        FormField field = new FormField(STREAM_DATA_FIELD_NAME);

-        field.setType(FormField.TYPE_LIST_SINGLE);

-        if (!IBB_ONLY) {

-            field.addOption(new FormField.Option(Socks5BytestreamManager.NAMESPACE));

-        }

-        field.addOption(new FormField.Option(InBandBytestreamManager.NAMESPACE));

-        form.addField(field);

-        return form;

-    }

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.ConnectionListener;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+/**
+ * Manages the negotiation of file transfers according to JEP-0096. If a file is
+ * being sent the remote user chooses the type of stream under which the file
+ * will be sent.
+ *
+ * @author Alexander Wenckus
+ * @see <a href="http://xmpp.org/extensions/xep-0096.html">XEP-0096: SI File Transfer</a>
+ */
+public class FileTransferNegotiator {
+
+    // Static
+
+    private static final String[] NAMESPACE = {
+            "http://jabber.org/protocol/si/profile/file-transfer",
+            "http://jabber.org/protocol/si"};
+
+    private static final Map<Connection, FileTransferNegotiator> transferObject =
+            new ConcurrentHashMap<Connection, FileTransferNegotiator>();
+
+    private static final String STREAM_INIT_PREFIX = "jsi_";
+
+    protected static final String STREAM_DATA_FIELD_NAME = "stream-method";
+
+    private static final Random randomGenerator = new Random();
+
+    /**
+     * A static variable to use only offer IBB for file transfer. It is generally recommend to only
+     * set this variable to true for testing purposes as IBB is the backup file transfer method
+     * and shouldn't be used as the only transfer method in production systems.
+     */
+    public static boolean IBB_ONLY = (System.getProperty("ibb") != null);//true;
+
+    /**
+     * Returns the file transfer negotiator related to a particular connection.
+     * When this class is requested on a particular connection the file transfer
+     * service is automatically enabled.
+     *
+     * @param connection The connection for which the transfer manager is desired
+     * @return The IMFileTransferManager
+     */
+    public static FileTransferNegotiator getInstanceFor(
+            final Connection connection) {
+        if (connection == null) {
+            throw new IllegalArgumentException("Connection cannot be null");
+        }
+        if (!connection.isConnected()) {
+            return null;
+        }
+
+        if (transferObject.containsKey(connection)) {
+            return transferObject.get(connection);
+        }
+        else {
+            FileTransferNegotiator transfer = new FileTransferNegotiator(
+                    connection);
+            setServiceEnabled(connection, true);
+            transferObject.put(connection, transfer);
+            return transfer;
+        }
+    }
+
+    /**
+     * Enable the Jabber services related to file transfer on the particular
+     * connection.
+     *
+     * @param connection The connection on which to enable or disable the services.
+     * @param isEnabled  True to enable, false to disable.
+     */
+    public static void setServiceEnabled(final Connection connection,
+            final boolean isEnabled) {
+        ServiceDiscoveryManager manager = ServiceDiscoveryManager
+                .getInstanceFor(connection);
+
+        List<String> namespaces = new ArrayList<String>();
+        namespaces.addAll(Arrays.asList(NAMESPACE));
+        namespaces.add(InBandBytestreamManager.NAMESPACE);
+        if (!IBB_ONLY) {
+            namespaces.add(Socks5BytestreamManager.NAMESPACE);
+        }
+
+        for (String namespace : namespaces) {
+            if (isEnabled) {
+                if (!manager.includesFeature(namespace)) {
+                    manager.addFeature(namespace);
+                }
+            } else {
+                manager.removeFeature(namespace);
+            }
+        }
+        
+    }
+
+    /**
+     * Checks to see if all file transfer related services are enabled on the
+     * connection.
+     *
+     * @param connection The connection to check
+     * @return True if all related services are enabled, false if they are not.
+     */
+    public static boolean isServiceEnabled(final Connection connection) {
+        ServiceDiscoveryManager manager = ServiceDiscoveryManager
+                .getInstanceFor(connection);
+
+        List<String> namespaces = new ArrayList<String>();
+        namespaces.addAll(Arrays.asList(NAMESPACE));
+        namespaces.add(InBandBytestreamManager.NAMESPACE);
+        if (!IBB_ONLY) {
+            namespaces.add(Socks5BytestreamManager.NAMESPACE);
+        }
+
+        for (String namespace : namespaces) {
+            if (!manager.includesFeature(namespace)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * A convenience method to create an IQ packet.
+     *
+     * @param ID   The packet ID of the
+     * @param to   To whom the packet is addressed.
+     * @param from From whom the packet is sent.
+     * @param type The IQ type of the packet.
+     * @return The created IQ packet.
+     */
+    public static IQ createIQ(final String ID, final String to,
+            final String from, final IQ.Type type) {
+        IQ iqPacket = new IQ() {
+            public String getChildElementXML() {
+                return null;
+            }
+        };
+        iqPacket.setPacketID(ID);
+        iqPacket.setTo(to);
+        iqPacket.setFrom(from);
+        iqPacket.setType(type);
+
+        return iqPacket;
+    }
+
+    /**
+     * Returns a collection of the supported transfer protocols.
+     *
+     * @return Returns a collection of the supported transfer protocols.
+     */
+    public static Collection<String> getSupportedProtocols() {
+        List<String> protocols = new ArrayList<String>();
+        protocols.add(InBandBytestreamManager.NAMESPACE);
+        if (!IBB_ONLY) {
+            protocols.add(Socks5BytestreamManager.NAMESPACE);
+        }
+        return Collections.unmodifiableList(protocols);
+    }
+
+    // non-static
+
+    private final Connection connection;
+
+    private final StreamNegotiator byteStreamTransferManager;
+
+    private final StreamNegotiator inbandTransferManager;
+
+    private FileTransferNegotiator(final Connection connection) {
+        configureConnection(connection);
+
+        this.connection = connection;
+        byteStreamTransferManager = new Socks5TransferNegotiator(connection);
+        inbandTransferManager = new IBBTransferNegotiator(connection);
+    }
+
+    private void configureConnection(final Connection connection) {
+        connection.addConnectionListener(new ConnectionListener() {
+            public void connectionClosed() {
+                cleanup(connection);
+            }
+
+            public void connectionClosedOnError(Exception e) {
+                cleanup(connection);
+            }
+
+            public void reconnectionFailed(Exception e) {
+                // ignore
+            }
+
+            public void reconnectionSuccessful() {
+                // ignore
+            }
+
+            public void reconnectingIn(int seconds) {
+                // ignore
+            }
+        });
+    }
+
+    private void cleanup(final Connection connection) {
+        if (transferObject.remove(connection) != null) {
+            inbandTransferManager.cleanup();
+        }
+    }
+
+    /**
+     * Selects an appropriate stream negotiator after examining the incoming file transfer request.
+     *
+     * @param request The related file transfer request.
+     * @return The file transfer object that handles the transfer
+     * @throws XMPPException If there are either no stream methods contained in the packet, or
+     *                       there is not an appropriate stream method.
+     */
+    public StreamNegotiator selectStreamNegotiator(
+            FileTransferRequest request) throws XMPPException {
+        StreamInitiation si = request.getStreamInitiation();
+        FormField streamMethodField = getStreamMethodField(si
+                .getFeatureNegotiationForm());
+
+        if (streamMethodField == null) {
+            String errorMessage = "No stream methods contained in packet.";
+            XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage);
+            IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
+                    IQ.Type.ERROR);
+            iqPacket.setError(error);
+            connection.sendPacket(iqPacket);
+            throw new XMPPException(errorMessage, error);
+        }
+
+        // select the appropriate protocol
+
+        StreamNegotiator selectedStreamNegotiator;
+        try {
+            selectedStreamNegotiator = getNegotiator(streamMethodField);
+        }
+        catch (XMPPException e) {
+            IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
+                    IQ.Type.ERROR);
+            iqPacket.setError(e.getXMPPError());
+            connection.sendPacket(iqPacket);
+            throw e;
+        }
+
+        // return the appropriate negotiator
+
+        return selectedStreamNegotiator;
+    }
+
+    private FormField getStreamMethodField(DataForm form) {
+        FormField field = null;
+        for (Iterator<FormField> it = form.getFields(); it.hasNext();) {
+            field = it.next();
+            if (field.getVariable().equals(STREAM_DATA_FIELD_NAME)) {
+                break;
+            }
+            field = null;
+        }
+        return field;
+    }
+
+    private StreamNegotiator getNegotiator(final FormField field)
+            throws XMPPException {
+        String variable;
+        boolean isByteStream = false;
+        boolean isIBB = false;
+        for (Iterator<FormField.Option> it = field.getOptions(); it.hasNext();) {
+            variable = it.next().getValue();
+            if (variable.equals(Socks5BytestreamManager.NAMESPACE) && !IBB_ONLY) {
+                isByteStream = true;
+            }
+            else if (variable.equals(InBandBytestreamManager.NAMESPACE)) {
+                isIBB = true;
+            }
+        }
+
+        if (!isByteStream && !isIBB) {
+            XMPPError error = new XMPPError(XMPPError.Condition.bad_request,
+                    "No acceptable transfer mechanism");
+            throw new XMPPException(error.getMessage(), error);
+        }
+
+       //if (isByteStream && isIBB && field.getType().equals(FormField.TYPE_LIST_MULTI)) {
+        if (isByteStream && isIBB) { 
+            return new FaultTolerantNegotiator(connection,
+                    byteStreamTransferManager,
+                    inbandTransferManager);
+        }
+        else if (isByteStream) {
+            return byteStreamTransferManager;
+        }
+        else {
+            return inbandTransferManager;
+        }
+    }
+
+    /**
+     * Reject a stream initiation request from a remote user.
+     *
+     * @param si The Stream Initiation request to reject.
+     */
+    public void rejectStream(final StreamInitiation si) {
+        XMPPError error = new XMPPError(XMPPError.Condition.forbidden, "Offer Declined");
+        IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
+                IQ.Type.ERROR);
+        iqPacket.setError(error);
+        connection.sendPacket(iqPacket);
+    }
+
+    /**
+     * Returns a new, unique, stream ID to identify a file transfer.
+     *
+     * @return Returns a new, unique, stream ID to identify a file transfer.
+     */
+    public String getNextStreamID() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(STREAM_INIT_PREFIX);
+        buffer.append(Math.abs(randomGenerator.nextLong()));
+
+        return buffer.toString();
+    }
+
+    /**
+     * Send a request to another user to send them a file. The other user has
+     * the option of, accepting, rejecting, or not responding to a received file
+     * transfer request.
+     * <p/>
+     * If they accept, the packet will contain the other user's chosen stream
+     * type to send the file across. The two choices this implementation
+     * provides to the other user for file transfer are <a
+     * href="http://www.jabber.org/jeps/jep-0065.html">SOCKS5 Bytestreams</a>,
+     * which is the preferred method of transfer, and <a
+     * href="http://www.jabber.org/jeps/jep-0047.html">In-Band Bytestreams</a>,
+     * which is the fallback mechanism.
+     * <p/>
+     * The other user may choose to decline the file request if they do not
+     * desire the file, their client does not support JEP-0096, or if there are
+     * no acceptable means to transfer the file.
+     * <p/>
+     * Finally, if the other user does not respond this method will return null
+     * after the specified timeout.
+     *
+     * @param userID          The userID of the user to whom the file will be sent.
+     * @param streamID        The unique identifier for this file transfer.
+     * @param fileName        The name of this file. Preferably it should include an
+     *                        extension as it is used to determine what type of file it is.
+     * @param size            The size, in bytes, of the file.
+     * @param desc            A description of the file.
+     * @param responseTimeout The amount of time, in milliseconds, to wait for the remote
+     *                        user to respond. If they do not respond in time, this
+     * @return Returns the stream negotiator selected by the peer.
+     * @throws XMPPException Thrown if there is an error negotiating the file transfer.
+     */
+    public StreamNegotiator negotiateOutgoingTransfer(final String userID,
+            final String streamID, final String fileName, final long size,
+            final String desc, int responseTimeout) throws XMPPException {
+        StreamInitiation si = new StreamInitiation();
+        si.setSesssionID(streamID);
+        si.setMimeType(URLConnection.guessContentTypeFromName(fileName));
+
+        StreamInitiation.File siFile = new StreamInitiation.File(fileName, size);
+        siFile.setDesc(desc);
+        si.setFile(siFile);
+
+        si.setFeatureNegotiationForm(createDefaultInitiationForm());
+
+        si.setFrom(connection.getUser());
+        si.setTo(userID);
+        si.setType(IQ.Type.SET);
+
+        PacketCollector collector = connection
+                .createPacketCollector(new PacketIDFilter(si.getPacketID()));
+        connection.sendPacket(si);
+        Packet siResponse = collector.nextResult(responseTimeout);
+        collector.cancel();
+
+        if (siResponse instanceof IQ) {
+            IQ iqResponse = (IQ) siResponse;
+            if (iqResponse.getType().equals(IQ.Type.RESULT)) {
+                StreamInitiation response = (StreamInitiation) siResponse;
+                return getOutgoingNegotiator(getStreamMethodField(response
+                        .getFeatureNegotiationForm()));
+
+            }
+            else if (iqResponse.getType().equals(IQ.Type.ERROR)) {
+                throw new XMPPException(iqResponse.getError());
+            }
+            else {
+                throw new XMPPException("File transfer response unreadable");
+            }
+        }
+        else {
+            return null;
+        }
+    }
+
+    private StreamNegotiator getOutgoingNegotiator(final FormField field)
+            throws XMPPException {
+        String variable;
+        boolean isByteStream = false;
+        boolean isIBB = false;
+        for (Iterator<String> it = field.getValues(); it.hasNext();) {
+            variable = it.next();
+            if (variable.equals(Socks5BytestreamManager.NAMESPACE) && !IBB_ONLY) {
+                isByteStream = true;
+            }
+            else if (variable.equals(InBandBytestreamManager.NAMESPACE)) {
+                isIBB = true;
+            }
+        }
+
+        if (!isByteStream && !isIBB) {
+            XMPPError error = new XMPPError(XMPPError.Condition.bad_request,
+                    "No acceptable transfer mechanism");
+            throw new XMPPException(error.getMessage(), error);
+        }
+
+        if (isByteStream && isIBB) {
+            return new FaultTolerantNegotiator(connection,
+                    byteStreamTransferManager, inbandTransferManager);
+        }
+        else if (isByteStream) {
+            return byteStreamTransferManager;
+        }
+        else {
+            return inbandTransferManager;
+        }
+    }
+
+    private DataForm createDefaultInitiationForm() {
+        DataForm form = new DataForm(Form.TYPE_FORM);
+        FormField field = new FormField(STREAM_DATA_FIELD_NAME);
+        field.setType(FormField.TYPE_LIST_SINGLE);
+        if (!IBB_ONLY) {
+            field.addOption(new FormField.Option(Socks5BytestreamManager.NAMESPACE));
+        }
+        field.addOption(new FormField.Option(InBandBytestreamManager.NAMESPACE));
+        form.addField(field);
+        return form;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java
index 6b5ccd8..69a073f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java
@@ -1,138 +1,138 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-/**

- * A request to send a file recieved from another user.

- * 

- * @author Alexander Wenckus

- * 

- */

-public class FileTransferRequest {

-	private final StreamInitiation streamInitiation;

-

-	private final FileTransferManager manager;

-

-	/**

-	 * A recieve request is constructed from the Stream Initiation request

-	 * received from the initator.

-	 * 

-	 * @param manager

-	 *            The manager handling this file transfer

-	 * 

-	 * @param si

-	 *            The Stream initiaton recieved from the initiator.

-	 */

-	public FileTransferRequest(FileTransferManager manager, StreamInitiation si) {

-		this.streamInitiation = si;

-		this.manager = manager;

-	}

-

-	/**

-	 * Returns the name of the file.

-	 * 

-	 * @return Returns the name of the file.

-	 */

-	public String getFileName() {

-		return streamInitiation.getFile().getName();

-	}

-

-	/**

-	 * Returns the size in bytes of the file.

-	 * 

-	 * @return Returns the size in bytes of the file.

-	 */

-	public long getFileSize() {

-		return streamInitiation.getFile().getSize();

-	}

-

-	/**

-	 * Returns the description of the file provided by the requestor.

-	 * 

-	 * @return Returns the description of the file provided by the requestor.

-	 */

-	public String getDescription() {

-		return streamInitiation.getFile().getDesc();

-	}

-

-	/**

-	 * Returns the mime-type of the file.

-	 * 

-	 * @return Returns the mime-type of the file.

-	 */

-	public String getMimeType() {

-		return streamInitiation.getMimeType();

-	}

-

-	/**

-	 * Returns the fully-qualified jabber ID of the user that requested this

-	 * file transfer.

-	 * 

-	 * @return Returns the fully-qualified jabber ID of the user that requested

-	 *         this file transfer.

-	 */

-	public String getRequestor() {

-		return streamInitiation.getFrom();

-	}

-

-	/**

-	 * Returns the stream ID that uniquely identifies this file transfer.

-	 * 

-	 * @return Returns the stream ID that uniquely identifies this file

-	 *         transfer.

-	 */

-	public String getStreamID() {

-		return streamInitiation.getSessionID();

-	}

-

-	/**

-	 * Returns the stream initiation packet that was sent by the requestor which

-	 * contains the parameters of the file transfer being transfer and also the

-	 * methods available to transfer the file.

-	 * 

-	 * @return Returns the stream initiation packet that was sent by the

-	 *         requestor which contains the parameters of the file transfer

-	 *         being transfer and also the methods available to transfer the

-	 *         file.

-	 */

-	protected StreamInitiation getStreamInitiation() {

-		return streamInitiation;

-	}

-

-	/**

-	 * Accepts this file transfer and creates the incoming file transfer.

-	 * 

-	 * @return Returns the <b><i>IncomingFileTransfer</b></i> on which the

-	 *         file transfer can be carried out.

-	 */

-	public IncomingFileTransfer accept() {

-		return manager.createIncomingFileTransfer(this);

-	}

-

-	/**

-	 * Rejects the file transfer request.

-	 */

-	public void reject() {

-		manager.rejectIncomingFileTransfer(this);

-	}

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+/**
+ * A request to send a file recieved from another user.
+ * 
+ * @author Alexander Wenckus
+ * 
+ */
+public class FileTransferRequest {
+	private final StreamInitiation streamInitiation;
+
+	private final FileTransferManager manager;
+
+	/**
+	 * A recieve request is constructed from the Stream Initiation request
+	 * received from the initator.
+	 * 
+	 * @param manager
+	 *            The manager handling this file transfer
+	 * 
+	 * @param si
+	 *            The Stream initiaton recieved from the initiator.
+	 */
+	public FileTransferRequest(FileTransferManager manager, StreamInitiation si) {
+		this.streamInitiation = si;
+		this.manager = manager;
+	}
+
+	/**
+	 * Returns the name of the file.
+	 * 
+	 * @return Returns the name of the file.
+	 */
+	public String getFileName() {
+		return streamInitiation.getFile().getName();
+	}
+
+	/**
+	 * Returns the size in bytes of the file.
+	 * 
+	 * @return Returns the size in bytes of the file.
+	 */
+	public long getFileSize() {
+		return streamInitiation.getFile().getSize();
+	}
+
+	/**
+	 * Returns the description of the file provided by the requestor.
+	 * 
+	 * @return Returns the description of the file provided by the requestor.
+	 */
+	public String getDescription() {
+		return streamInitiation.getFile().getDesc();
+	}
+
+	/**
+	 * Returns the mime-type of the file.
+	 * 
+	 * @return Returns the mime-type of the file.
+	 */
+	public String getMimeType() {
+		return streamInitiation.getMimeType();
+	}
+
+	/**
+	 * Returns the fully-qualified jabber ID of the user that requested this
+	 * file transfer.
+	 * 
+	 * @return Returns the fully-qualified jabber ID of the user that requested
+	 *         this file transfer.
+	 */
+	public String getRequestor() {
+		return streamInitiation.getFrom();
+	}
+
+	/**
+	 * Returns the stream ID that uniquely identifies this file transfer.
+	 * 
+	 * @return Returns the stream ID that uniquely identifies this file
+	 *         transfer.
+	 */
+	public String getStreamID() {
+		return streamInitiation.getSessionID();
+	}
+
+	/**
+	 * Returns the stream initiation packet that was sent by the requestor which
+	 * contains the parameters of the file transfer being transfer and also the
+	 * methods available to transfer the file.
+	 * 
+	 * @return Returns the stream initiation packet that was sent by the
+	 *         requestor which contains the parameters of the file transfer
+	 *         being transfer and also the methods available to transfer the
+	 *         file.
+	 */
+	protected StreamInitiation getStreamInitiation() {
+		return streamInitiation;
+	}
+
+	/**
+	 * Accepts this file transfer and creates the incoming file transfer.
+	 * 
+	 * @return Returns the <b><i>IncomingFileTransfer</b></i> on which the
+	 *         file transfer can be carried out.
+	 */
+	public IncomingFileTransfer accept() {
+		return manager.createIncomingFileTransfer(this);
+	}
+
+	/**
+	 * Rejects the file transfer request.
+	 */
+	public void reject() {
+		manager.rejectIncomingFileTransfer(this);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java
index b32f49a..7d5969c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java
@@ -1,152 +1,152 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import java.io.InputStream;

-import java.io.OutputStream;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.FromContainsFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;

-import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;

-import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-/**

- * The In-Band Bytestream file transfer method, or IBB for short, transfers the

- * file over the same XML Stream used by XMPP. It is the fall-back mechanism in

- * case the SOCKS5 bytestream method of transferring files is not available.

- * 

- * @author Alexander Wenckus

- * @author Henning Staib

- * @see <a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047: In-Band

- *      Bytestreams (IBB)</a>

- */

-public class IBBTransferNegotiator extends StreamNegotiator {

-

-    private Connection connection;

-

-    private InBandBytestreamManager manager;

-

-    /**

-     * The default constructor for the In-Band Bytestream Negotiator.

-     * 

-     * @param connection The connection which this negotiator works on.

-     */

-    protected IBBTransferNegotiator(Connection connection) {

-        this.connection = connection;

-        this.manager = InBandBytestreamManager.getByteStreamManager(connection);

-    }

-

-    public OutputStream createOutgoingStream(String streamID, String initiator,

-                    String target) throws XMPPException {

-        InBandBytestreamSession session = this.manager.establishSession(target, streamID);

-        session.setCloseBothStreamsEnabled(true);

-        return session.getOutputStream();

-    }

-

-    public InputStream createIncomingStream(StreamInitiation initiation)

-                    throws XMPPException {

-        /*

-         * In-Band Bytestream initiation listener must ignore next in-band

-         * bytestream request with given session ID

-         */

-        this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());

-

-        Packet streamInitiation = initiateIncomingStream(this.connection, initiation);

-        return negotiateIncomingStream(streamInitiation);

-    }

-

-    public PacketFilter getInitiationPacketFilter(String from, String streamID) {

-        /*

-         * this method is always called prior to #negotiateIncomingStream() so

-         * the In-Band Bytestream initiation listener must ignore the next

-         * In-Band Bytestream request with the given session ID

-         */

-        this.manager.ignoreBytestreamRequestOnce(streamID);

-

-        return new AndFilter(new FromContainsFilter(from), new IBBOpenSidFilter(streamID));

-    }

-

-    public String[] getNamespaces() {

-        return new String[] { InBandBytestreamManager.NAMESPACE };

-    }

-

-    InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException {

-        // build In-Band Bytestream request

-        InBandBytestreamRequest request = new ByteStreamRequest(this.manager,

-                        (Open) streamInitiation);

-

-        // always accept the request

-        InBandBytestreamSession session = request.accept();

-        session.setCloseBothStreamsEnabled(true);

-        return session.getInputStream();

-    }

-

-    public void cleanup() {

-    }

-

-    /**

-     * This PacketFilter accepts an incoming In-Band Bytestream open request

-     * with a specified session ID.

-     */

-    private static class IBBOpenSidFilter extends PacketTypeFilter {

-

-        private String sessionID;

-

-        public IBBOpenSidFilter(String sessionID) {

-            super(Open.class);

-            if (sessionID == null) {

-                throw new IllegalArgumentException("StreamID cannot be null");

-            }

-            this.sessionID = sessionID;

-        }

-

-        public boolean accept(Packet packet) {

-            if (super.accept(packet)) {

-                Open bytestream = (Open) packet;

-

-                // packet must by of type SET and contains the given session ID

-                return this.sessionID.equals(bytestream.getSessionID())

-                                && IQ.Type.SET.equals(bytestream.getType());

-            }

-            return false;

-        }

-    }

-

-    /**

-     * Derive from InBandBytestreamRequest to access protected constructor.

-     */

-    private static class ByteStreamRequest extends InBandBytestreamRequest {

-

-        private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) {

-            super(manager, byteStreamRequest);

-        }

-

-    }

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.FromContainsFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
+import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
+import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+/**
+ * The In-Band Bytestream file transfer method, or IBB for short, transfers the
+ * file over the same XML Stream used by XMPP. It is the fall-back mechanism in
+ * case the SOCKS5 bytestream method of transferring files is not available.
+ * 
+ * @author Alexander Wenckus
+ * @author Henning Staib
+ * @see <a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047: In-Band
+ *      Bytestreams (IBB)</a>
+ */
+public class IBBTransferNegotiator extends StreamNegotiator {
+
+    private Connection connection;
+
+    private InBandBytestreamManager manager;
+
+    /**
+     * The default constructor for the In-Band Bytestream Negotiator.
+     * 
+     * @param connection The connection which this negotiator works on.
+     */
+    protected IBBTransferNegotiator(Connection connection) {
+        this.connection = connection;
+        this.manager = InBandBytestreamManager.getByteStreamManager(connection);
+    }
+
+    public OutputStream createOutgoingStream(String streamID, String initiator,
+                    String target) throws XMPPException {
+        InBandBytestreamSession session = this.manager.establishSession(target, streamID);
+        session.setCloseBothStreamsEnabled(true);
+        return session.getOutputStream();
+    }
+
+    public InputStream createIncomingStream(StreamInitiation initiation)
+                    throws XMPPException {
+        /*
+         * In-Band Bytestream initiation listener must ignore next in-band
+         * bytestream request with given session ID
+         */
+        this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());
+
+        Packet streamInitiation = initiateIncomingStream(this.connection, initiation);
+        return negotiateIncomingStream(streamInitiation);
+    }
+
+    public PacketFilter getInitiationPacketFilter(String from, String streamID) {
+        /*
+         * this method is always called prior to #negotiateIncomingStream() so
+         * the In-Band Bytestream initiation listener must ignore the next
+         * In-Band Bytestream request with the given session ID
+         */
+        this.manager.ignoreBytestreamRequestOnce(streamID);
+
+        return new AndFilter(new FromContainsFilter(from), new IBBOpenSidFilter(streamID));
+    }
+
+    public String[] getNamespaces() {
+        return new String[] { InBandBytestreamManager.NAMESPACE };
+    }
+
+    InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException {
+        // build In-Band Bytestream request
+        InBandBytestreamRequest request = new ByteStreamRequest(this.manager,
+                        (Open) streamInitiation);
+
+        // always accept the request
+        InBandBytestreamSession session = request.accept();
+        session.setCloseBothStreamsEnabled(true);
+        return session.getInputStream();
+    }
+
+    public void cleanup() {
+    }
+
+    /**
+     * This PacketFilter accepts an incoming In-Band Bytestream open request
+     * with a specified session ID.
+     */
+    private static class IBBOpenSidFilter extends PacketTypeFilter {
+
+        private String sessionID;
+
+        public IBBOpenSidFilter(String sessionID) {
+            super(Open.class);
+            if (sessionID == null) {
+                throw new IllegalArgumentException("StreamID cannot be null");
+            }
+            this.sessionID = sessionID;
+        }
+
+        public boolean accept(Packet packet) {
+            if (super.accept(packet)) {
+                Open bytestream = (Open) packet;
+
+                // packet must by of type SET and contains the given session ID
+                return this.sessionID.equals(bytestream.getSessionID())
+                                && IQ.Type.SET.equals(bytestream.getType());
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Derive from InBandBytestreamRequest to access protected constructor.
+     */
+    private static class ByteStreamRequest extends InBandBytestreamRequest {
+
+        private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) {
+            super(manager, byteStreamRequest);
+        }
+
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java
index 91a5a0d..fc15b63 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java
@@ -1,215 +1,215 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smack.XMPPException;

-

-import java.io.*;

-import java.util.concurrent.*;

-

-/**

- * An incoming file transfer is created when the

- * {@link FileTransferManager#createIncomingFileTransfer(FileTransferRequest)}

- * method is invoked. It is a file being sent to the local user from another

- * user on the jabber network. There are two stages of the file transfer to be

- * concerned with and they can be handled in different ways depending upon the

- * method that is invoked on this class.

- * <p/>

- * The first way that a file is recieved is by calling the

- * {@link #recieveFile()} method. This method, negotiates the appropriate stream

- * method and then returns the <b><i>InputStream</b></i> to read the file

- * data from.

- * <p/>

- * The second way that a file can be recieved through this class is by invoking

- * the {@link #recieveFile(File)} method. This method returns immediatly and

- * takes as its parameter a file on the local file system where the file

- * recieved from the transfer will be put.

- *

- * @author Alexander Wenckus

- */

-public class IncomingFileTransfer extends FileTransfer {

-

-    private FileTransferRequest recieveRequest;

-

-    private InputStream inputStream;

-

-    protected IncomingFileTransfer(FileTransferRequest request,

-            FileTransferNegotiator transferNegotiator) {

-        super(request.getRequestor(), request.getStreamID(), transferNegotiator);

-        this.recieveRequest = request;

-    }

-

-    /**

-     * Negotiates the stream method to transfer the file over and then returns

-     * the negotiated stream.

-     *

-     * @return The negotiated InputStream from which to read the data.

-     * @throws XMPPException If there is an error in the negotiation process an exception

-     *                       is thrown.

-     */

-    public InputStream recieveFile() throws XMPPException {

-        if (inputStream != null) {

-            throw new IllegalStateException("Transfer already negotiated!");

-        }

-

-        try {

-            inputStream = negotiateStream();

-        }

-        catch (XMPPException e) {

-            setException(e);

-            throw e;

-        }

-

-        return inputStream;

-    }

-

-    /**

-     * This method negotitates the stream and then transfer's the file over the

-     * negotiated stream. The transfered file will be saved at the provided

-     * location.

-     * <p/>

-     * This method will return immedialtly, file transfer progress can be

-     * monitored through several methods:

-     * <p/>

-     * <UL>

-     * <LI>{@link FileTransfer#getStatus()}

-     * <LI>{@link FileTransfer#getProgress()}

-     * <LI>{@link FileTransfer#isDone()}

-     * </UL>

-     *

-     * @param file The location to save the file.

-     * @throws XMPPException            when the file transfer fails

-     * @throws IllegalArgumentException This exception is thrown when the the provided file is

-     *                                  either null, or cannot be written to.

-     */

-    public void recieveFile(final File file) throws XMPPException {

-        if (file != null) {

-            if (!file.exists()) {

-                try {

-                    file.createNewFile();

-                }

-                catch (IOException e) {

-                    throw new XMPPException(

-                            "Could not create file to write too", e);

-                }

-            }

-            if (!file.canWrite()) {

-                throw new IllegalArgumentException("Cannot write to provided file");

-            }

-        }

-        else {

-            throw new IllegalArgumentException("File cannot be null");

-        }

-

-        Thread transferThread = new Thread(new Runnable() {

-            public void run() {

-                try {

-                    inputStream = negotiateStream();

-                }

-                catch (XMPPException e) {

-                    handleXMPPException(e);

-                    return;

-                }

-

-                OutputStream outputStream = null;

-                try {

-                    outputStream = new FileOutputStream(file);

-                    setStatus(Status.in_progress);

-                    writeToStream(inputStream, outputStream);

-                }

-                catch (XMPPException e) {

-                    setStatus(Status.error);

-                    setError(Error.stream);

-                    setException(e);

-                }

-                catch (FileNotFoundException e) {

-                    setStatus(Status.error);

-                    setError(Error.bad_file);

-                    setException(e);

-                }

-

-                if (getStatus().equals(Status.in_progress)) {

-                    setStatus(Status.complete);

-                }

-                if (inputStream != null) {

-                    try {

-                        inputStream.close();

-                    }

-                    catch (Throwable io) {

-                        /* Ignore */

-                    }

-                }

-                if (outputStream != null) {

-                    try {

-                        outputStream.close();

-                    }

-                    catch (Throwable io) {

-                        /* Ignore */

-                    }

-                }

-            }

-        }, "File Transfer " + streamID);

-        transferThread.start();

-    }

-

-    private void handleXMPPException(XMPPException e) {

-        setStatus(FileTransfer.Status.error);

-        setException(e);

-    }

-

-    private InputStream negotiateStream() throws XMPPException {

-        setStatus(Status.negotiating_transfer);

-        final StreamNegotiator streamNegotiator = negotiator

-                .selectStreamNegotiator(recieveRequest);

-        setStatus(Status.negotiating_stream);

-        FutureTask<InputStream> streamNegotiatorTask = new FutureTask<InputStream>(

-                new Callable<InputStream>() {

-

-                    public InputStream call() throws Exception {

-                        return streamNegotiator

-                                .createIncomingStream(recieveRequest.getStreamInitiation());

-                    }

-                });

-        streamNegotiatorTask.run();

-        InputStream inputStream;

-        try {

-            inputStream = streamNegotiatorTask.get(15, TimeUnit.SECONDS);

-        }

-        catch (InterruptedException e) {

-            throw new XMPPException("Interruption while executing", e);

-        }

-        catch (ExecutionException e) {

-            throw new XMPPException("Error in execution", e);

-        }

-        catch (TimeoutException e) {

-            throw new XMPPException("Request timed out", e);

-        }

-        finally {

-            streamNegotiatorTask.cancel(true);

-        }

-        setStatus(Status.negotiated);

-        return inputStream;

-    }

-

-    public void cancel() {

-        setStatus(Status.cancelled);

-    }

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smack.XMPPException;
+
+import java.io.*;
+import java.util.concurrent.*;
+
+/**
+ * An incoming file transfer is created when the
+ * {@link FileTransferManager#createIncomingFileTransfer(FileTransferRequest)}
+ * method is invoked. It is a file being sent to the local user from another
+ * user on the jabber network. There are two stages of the file transfer to be
+ * concerned with and they can be handled in different ways depending upon the
+ * method that is invoked on this class.
+ * <p/>
+ * The first way that a file is recieved is by calling the
+ * {@link #recieveFile()} method. This method, negotiates the appropriate stream
+ * method and then returns the <b><i>InputStream</b></i> to read the file
+ * data from.
+ * <p/>
+ * The second way that a file can be recieved through this class is by invoking
+ * the {@link #recieveFile(File)} method. This method returns immediatly and
+ * takes as its parameter a file on the local file system where the file
+ * recieved from the transfer will be put.
+ *
+ * @author Alexander Wenckus
+ */
+public class IncomingFileTransfer extends FileTransfer {
+
+    private FileTransferRequest recieveRequest;
+
+    private InputStream inputStream;
+
+    protected IncomingFileTransfer(FileTransferRequest request,
+            FileTransferNegotiator transferNegotiator) {
+        super(request.getRequestor(), request.getStreamID(), transferNegotiator);
+        this.recieveRequest = request;
+    }
+
+    /**
+     * Negotiates the stream method to transfer the file over and then returns
+     * the negotiated stream.
+     *
+     * @return The negotiated InputStream from which to read the data.
+     * @throws XMPPException If there is an error in the negotiation process an exception
+     *                       is thrown.
+     */
+    public InputStream recieveFile() throws XMPPException {
+        if (inputStream != null) {
+            throw new IllegalStateException("Transfer already negotiated!");
+        }
+
+        try {
+            inputStream = negotiateStream();
+        }
+        catch (XMPPException e) {
+            setException(e);
+            throw e;
+        }
+
+        return inputStream;
+    }
+
+    /**
+     * This method negotitates the stream and then transfer's the file over the
+     * negotiated stream. The transfered file will be saved at the provided
+     * location.
+     * <p/>
+     * This method will return immedialtly, file transfer progress can be
+     * monitored through several methods:
+     * <p/>
+     * <UL>
+     * <LI>{@link FileTransfer#getStatus()}
+     * <LI>{@link FileTransfer#getProgress()}
+     * <LI>{@link FileTransfer#isDone()}
+     * </UL>
+     *
+     * @param file The location to save the file.
+     * @throws XMPPException            when the file transfer fails
+     * @throws IllegalArgumentException This exception is thrown when the the provided file is
+     *                                  either null, or cannot be written to.
+     */
+    public void recieveFile(final File file) throws XMPPException {
+        if (file != null) {
+            if (!file.exists()) {
+                try {
+                    file.createNewFile();
+                }
+                catch (IOException e) {
+                    throw new XMPPException(
+                            "Could not create file to write too", e);
+                }
+            }
+            if (!file.canWrite()) {
+                throw new IllegalArgumentException("Cannot write to provided file");
+            }
+        }
+        else {
+            throw new IllegalArgumentException("File cannot be null");
+        }
+
+        Thread transferThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    inputStream = negotiateStream();
+                }
+                catch (XMPPException e) {
+                    handleXMPPException(e);
+                    return;
+                }
+
+                OutputStream outputStream = null;
+                try {
+                    outputStream = new FileOutputStream(file);
+                    setStatus(Status.in_progress);
+                    writeToStream(inputStream, outputStream);
+                }
+                catch (XMPPException e) {
+                    setStatus(Status.error);
+                    setError(Error.stream);
+                    setException(e);
+                }
+                catch (FileNotFoundException e) {
+                    setStatus(Status.error);
+                    setError(Error.bad_file);
+                    setException(e);
+                }
+
+                if (getStatus().equals(Status.in_progress)) {
+                    setStatus(Status.complete);
+                }
+                if (inputStream != null) {
+                    try {
+                        inputStream.close();
+                    }
+                    catch (Throwable io) {
+                        /* Ignore */
+                    }
+                }
+                if (outputStream != null) {
+                    try {
+                        outputStream.close();
+                    }
+                    catch (Throwable io) {
+                        /* Ignore */
+                    }
+                }
+            }
+        }, "File Transfer " + streamID);
+        transferThread.start();
+    }
+
+    private void handleXMPPException(XMPPException e) {
+        setStatus(FileTransfer.Status.error);
+        setException(e);
+    }
+
+    private InputStream negotiateStream() throws XMPPException {
+        setStatus(Status.negotiating_transfer);
+        final StreamNegotiator streamNegotiator = negotiator
+                .selectStreamNegotiator(recieveRequest);
+        setStatus(Status.negotiating_stream);
+        FutureTask<InputStream> streamNegotiatorTask = new FutureTask<InputStream>(
+                new Callable<InputStream>() {
+
+                    public InputStream call() throws Exception {
+                        return streamNegotiator
+                                .createIncomingStream(recieveRequest.getStreamInitiation());
+                    }
+                });
+        streamNegotiatorTask.run();
+        InputStream inputStream;
+        try {
+            inputStream = streamNegotiatorTask.get(15, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+            throw new XMPPException("Interruption while executing", e);
+        }
+        catch (ExecutionException e) {
+            throw new XMPPException("Error in execution", e);
+        }
+        catch (TimeoutException e) {
+            throw new XMPPException("Request timed out", e);
+        }
+        finally {
+            streamNegotiatorTask.cancel(true);
+        }
+        setStatus(Status.negotiated);
+        return inputStream;
+    }
+
+    public void cancel() {
+        setStatus(Status.cancelled);
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java
index 13b8a57..ded096e 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java
@@ -1,449 +1,449 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.XMPPError;

-

-import java.io.*;

-

-/**

- * Handles the sending of a file to another user. File transfer's in jabber have

- * several steps and there are several methods in this class that handle these

- * steps differently.

- *

- * @author Alexander Wenckus

- *

- */

-public class OutgoingFileTransfer extends FileTransfer {

-

-	private static int RESPONSE_TIMEOUT = 60 * 1000;

-    private NegotiationProgress callback;

-

-    /**

-     * Returns the time in milliseconds after which the file transfer

-     * negotiation process will timeout if the other user has not responded.

-     *

-     * @return Returns the time in milliseconds after which the file transfer

-     *         negotiation process will timeout if the remote user has not

-     *         responded.

-     */

-    public static int getResponseTimeout() {

-        return RESPONSE_TIMEOUT;

-    }

-

-	/**

-	 * Sets the time in milliseconds after which the file transfer negotiation

-	 * process will timeout if the other user has not responded.

-	 *

-	 * @param responseTimeout

-	 *            The timeout time in milliseconds.

-	 */

-	public static void setResponseTimeout(int responseTimeout) {

-		RESPONSE_TIMEOUT = responseTimeout;

-	}

-

-	private OutputStream outputStream;

-

-	private String initiator;

-

-	private Thread transferThread;

-

-	protected OutgoingFileTransfer(String initiator, String target,

-			String streamID, FileTransferNegotiator transferNegotiator) {

-		super(target, streamID, transferNegotiator);

-		this.initiator = initiator;

-	}

-

-	protected void setOutputStream(OutputStream stream) {

-		if (outputStream == null) {

-			this.outputStream = stream;

-		}

-	}

-

-	/**

-	 * Returns the output stream connected to the peer to transfer the file. It

-	 * is only available after it has been successfully negotiated by the

-	 * {@link StreamNegotiator}.

-	 *

-	 * @return Returns the output stream connected to the peer to transfer the

-	 *         file.

-	 */

-	protected OutputStream getOutputStream() {

-		if (getStatus().equals(FileTransfer.Status.negotiated)) {

-			return outputStream;

-		} else {

-			return null;

-		}

-	}

-

-	/**

-	 * This method handles the negotiation of the file transfer and the stream,

-	 * it only returns the created stream after the negotiation has been completed.

-	 *

-	 * @param fileName

-	 *            The name of the file that will be transmitted. It is

-	 *            preferable for this name to have an extension as it will be

-	 *            used to determine the type of file it is.

-	 * @param fileSize

-	 *            The size in bytes of the file that will be transmitted.

-	 * @param description

-	 *            A description of the file that will be transmitted.

-	 * @return The OutputStream that is connected to the peer to transmit the

-	 *         file.

-	 * @throws XMPPException

-	 *             Thrown if an error occurs during the file transfer

-	 *             negotiation process.

-	 */

-	public synchronized OutputStream sendFile(String fileName, long fileSize,

-			String description) throws XMPPException {

-		if (isDone() || outputStream != null) {

-			throw new IllegalStateException(

-					"The negotation process has already"

-							+ " been attempted on this file transfer");

-		}

-		try {

-			setFileInfo(fileName, fileSize);

-			this.outputStream = negotiateStream(fileName, fileSize, description);

-		} catch (XMPPException e) {

-			handleXMPPException(e);

-			throw e;

-		}

-		return outputStream;

-	}

-

-	/**

-	 * This methods handles the transfer and stream negotiation process. It

-	 * returns immediately and its progress will be updated through the

-	 * {@link NegotiationProgress} callback.

-	 *

-	 * @param fileName

-	 *            The name of the file that will be transmitted. It is

-	 *            preferable for this name to have an extension as it will be

-	 *            used to determine the type of file it is.

-	 * @param fileSize

-	 *            The size in bytes of the file that will be transmitted.

-	 * @param description

-	 *            A description of the file that will be transmitted.

-	 * @param progress

-	 *            A callback to monitor the progress of the file transfer

-	 *            negotiation process and to retrieve the OutputStream when it

-	 *            is complete.

-	 */

-	public synchronized void sendFile(final String fileName,

-			final long fileSize, final String description,

-			final NegotiationProgress progress)

-    {

-        if(progress == null) {

-            throw new IllegalArgumentException("Callback progress cannot be null.");

-        }

-        checkTransferThread();

-		if (isDone() || outputStream != null) {

-			throw new IllegalStateException(

-					"The negotation process has already"

-							+ " been attempted for this file transfer");

-		}

-        setFileInfo(fileName, fileSize);

-        this.callback = progress;

-        transferThread = new Thread(new Runnable() {

-			public void run() {

-				try {

-					OutgoingFileTransfer.this.outputStream = negotiateStream(

-							fileName, fileSize, description);

-                    progress.outputStreamEstablished(OutgoingFileTransfer.this.outputStream);

-                }

-                catch (XMPPException e) {

-					handleXMPPException(e);

-				}

-			}

-		}, "File Transfer Negotiation " + streamID);

-		transferThread.start();

-	}

-

-	private void checkTransferThread() {

-		if (transferThread != null && transferThread.isAlive() || isDone()) {

-			throw new IllegalStateException(

-					"File transfer in progress or has already completed.");

-		}

-	}

-

-    /**

-	 * This method handles the stream negotiation process and transmits the file

-	 * to the remote user. It returns immediately and the progress of the file

-	 * transfer can be monitored through several methods:

-	 *

-	 * <UL>

-	 * <LI>{@link FileTransfer#getStatus()}

-	 * <LI>{@link FileTransfer#getProgress()}

-	 * <LI>{@link FileTransfer#isDone()}

-	 * </UL>

-	 *

-     * @param file the file to transfer to the remote entity.

-     * @param description a description for the file to transfer.

-	 * @throws XMPPException

-	 *             If there is an error during the negotiation process or the

-	 *             sending of the file.

-	 */

-	public synchronized void sendFile(final File file, final String description)

-			throws XMPPException {

-		checkTransferThread();

-		if (file == null || !file.exists() || !file.canRead()) {

-			throw new IllegalArgumentException("Could not read file");

-		} else {

-			setFileInfo(file.getAbsolutePath(), file.getName(), file.length());

-		}

-

-		transferThread = new Thread(new Runnable() {

-			public void run() {

-				try {

-					outputStream = negotiateStream(file.getName(), file

-							.length(), description);

-				} catch (XMPPException e) {

-					handleXMPPException(e);

-					return;

-				}

-				if (outputStream == null) {

-					return;

-				}

-

-                if (!updateStatus(Status.negotiated, Status.in_progress)) {

-					return;

-				}

-

-				InputStream inputStream = null;

-				try {

-					inputStream = new FileInputStream(file);

-					writeToStream(inputStream, outputStream);

-				} catch (FileNotFoundException e) {

-					setStatus(FileTransfer.Status.error);

-					setError(Error.bad_file);

-					setException(e);

-				} catch (XMPPException e) {

-					setStatus(FileTransfer.Status.error);

-					setException(e);

-				} finally {

-					try {

-						if (inputStream != null) {

-							inputStream.close();

-						}

-

-						outputStream.flush();

-						outputStream.close();

-					} catch (IOException e) {

-                        /* Do Nothing */

-					}

-				}

-                updateStatus(Status.in_progress, FileTransfer.Status.complete);

-				}

-

-		}, "File Transfer " + streamID);

-		transferThread.start();

-	}

-

-    /**

-	 * This method handles the stream negotiation process and transmits the file

-	 * to the remote user. It returns immediately and the progress of the file

-	 * transfer can be monitored through several methods:

-	 *

-	 * <UL>

-	 * <LI>{@link FileTransfer#getStatus()}

-	 * <LI>{@link FileTransfer#getProgress()}

-	 * <LI>{@link FileTransfer#isDone()}

-	 * </UL>

-	 *

-     * @param in the stream to transfer to the remote entity.

-     * @param fileName the name of the file that is transferred

-     * @param fileSize the size of the file that is transferred

-     * @param description a description for the file to transfer.

-	 */

-	public synchronized void sendStream(final InputStream in, final String fileName, final long fileSize, final String description){

-		checkTransferThread();

-

-		setFileInfo(fileName, fileSize);

-		transferThread = new Thread(new Runnable() {

-			public void run() {

-                //Create packet filter

-                try {

-					outputStream = negotiateStream(fileName, fileSize, description);

-				} catch (XMPPException e) {

-					handleXMPPException(e);

-					return;

-				}

-				if (outputStream == null) {

-					return;

-				}

-

-                if (!updateStatus(Status.negotiated, Status.in_progress)) {

-					return;

-				}

-				try {

-					writeToStream(in, outputStream);

-				} catch (XMPPException e) {

-					setStatus(FileTransfer.Status.error);

-					setException(e);

-				} finally {

-					try {

-						if (in != null) {

-							in.close();

-						}

-

-						outputStream.flush();

-						outputStream.close();

-					} catch (IOException e) {

-                        /* Do Nothing */

-					}

-				}

-                updateStatus(Status.in_progress, FileTransfer.Status.complete);

-				}

-

-		}, "File Transfer " + streamID);

-		transferThread.start();

-	}

-

-	private void handleXMPPException(XMPPException e) {

-		XMPPError error = e.getXMPPError();

-		if (error != null) {

-			int code = error.getCode();

-			if (code == 403) {

-				setStatus(Status.refused);

-				return;

-			}

-            else if (code == 400) {

-				setStatus(Status.error);

-				setError(Error.not_acceptable);

-            }

-            else {

-                setStatus(FileTransfer.Status.error);

-            }

-        }

-

-        setException(e);

-	}

-

-	/**

-	 * Returns the amount of bytes that have been sent for the file transfer. Or

-	 * -1 if the file transfer has not started.

-	 * <p>

-	 * Note: This method is only useful when the {@link #sendFile(File, String)}

-	 * method is called, as it is the only method that actually transmits the

-	 * file.

-	 *

-	 * @return Returns the amount of bytes that have been sent for the file

-	 *         transfer. Or -1 if the file transfer has not started.

-	 */

-	public long getBytesSent() {

-		return amountWritten;

-	}

-

-	private OutputStream negotiateStream(String fileName, long fileSize,

-			String description) throws XMPPException {

-		// Negotiate the file transfer profile

-

-        if (!updateStatus(Status.initial, Status.negotiating_transfer)) {

-            throw new XMPPException("Illegal state change");

-        }

-		StreamNegotiator streamNegotiator = negotiator.negotiateOutgoingTransfer(

-				getPeer(), streamID, fileName, fileSize, description,

-				RESPONSE_TIMEOUT);

-

-		if (streamNegotiator == null) {

-			setStatus(Status.error);

-			setError(Error.no_response);

-			return null;

-		}

-

-        // Negotiate the stream

-        if (!updateStatus(Status.negotiating_transfer, Status.negotiating_stream)) {

-            throw new XMPPException("Illegal state change");

-        }

-		outputStream = streamNegotiator.createOutgoingStream(streamID,

-                initiator, getPeer());

-

-        if (!updateStatus(Status.negotiating_stream, Status.negotiated)) {

-            throw new XMPPException("Illegal state change");

-		}

-		return outputStream;

-	}

-

-	public void cancel() {

-		setStatus(Status.cancelled);

-	}

-

-    @Override

-    protected boolean updateStatus(Status oldStatus, Status newStatus) {

-        boolean isUpdated = super.updateStatus(oldStatus, newStatus);

-        if(callback != null && isUpdated) {

-            callback.statusUpdated(oldStatus, newStatus);

-        }

-        return isUpdated;

-    }

-

-    @Override

-    protected void setStatus(Status status) {

-        Status oldStatus = getStatus();

-        super.setStatus(status);

-        if(callback != null) {

-            callback.statusUpdated(oldStatus, status);

-        }

-    }

-

-    @Override

-    protected void setException(Exception exception) {

-        super.setException(exception);

-        if(callback != null) {

-            callback.errorEstablishingStream(exception);

-        }

-    }

-

-    /**

-	 * A callback class to retrieve the status of an outgoing transfer

-	 * negotiation process.

-	 *

-	 * @author Alexander Wenckus

-	 *

-	 */

-	public interface NegotiationProgress {

-

-		/**

-		 * Called when the status changes

-         *

-         * @param oldStatus the previous status of the file transfer.

-         * @param newStatus the new status of the file transfer.

-         */

-		void statusUpdated(Status oldStatus, Status newStatus);

-

-		/**

-		 * Once the negotiation process is completed the output stream can be

-		 * retrieved.

-         *

-         * @param stream the established stream which can be used to transfer the file to the remote

-         * entity

-		 */

-		void outputStreamEstablished(OutputStream stream);

-

-        /**

-         * Called when an exception occurs during the negotiation progress.

-         *

-         * @param e the exception that occurred.

-         */

-        void errorEstablishingStream(Exception e);

-    }

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.XMPPError;
+
+import java.io.*;
+
+/**
+ * Handles the sending of a file to another user. File transfer's in jabber have
+ * several steps and there are several methods in this class that handle these
+ * steps differently.
+ *
+ * @author Alexander Wenckus
+ *
+ */
+public class OutgoingFileTransfer extends FileTransfer {
+
+	private static int RESPONSE_TIMEOUT = 60 * 1000;
+    private NegotiationProgress callback;
+
+    /**
+     * Returns the time in milliseconds after which the file transfer
+     * negotiation process will timeout if the other user has not responded.
+     *
+     * @return Returns the time in milliseconds after which the file transfer
+     *         negotiation process will timeout if the remote user has not
+     *         responded.
+     */
+    public static int getResponseTimeout() {
+        return RESPONSE_TIMEOUT;
+    }
+
+	/**
+	 * Sets the time in milliseconds after which the file transfer negotiation
+	 * process will timeout if the other user has not responded.
+	 *
+	 * @param responseTimeout
+	 *            The timeout time in milliseconds.
+	 */
+	public static void setResponseTimeout(int responseTimeout) {
+		RESPONSE_TIMEOUT = responseTimeout;
+	}
+
+	private OutputStream outputStream;
+
+	private String initiator;
+
+	private Thread transferThread;
+
+	protected OutgoingFileTransfer(String initiator, String target,
+			String streamID, FileTransferNegotiator transferNegotiator) {
+		super(target, streamID, transferNegotiator);
+		this.initiator = initiator;
+	}
+
+	protected void setOutputStream(OutputStream stream) {
+		if (outputStream == null) {
+			this.outputStream = stream;
+		}
+	}
+
+	/**
+	 * Returns the output stream connected to the peer to transfer the file. It
+	 * is only available after it has been successfully negotiated by the
+	 * {@link StreamNegotiator}.
+	 *
+	 * @return Returns the output stream connected to the peer to transfer the
+	 *         file.
+	 */
+	protected OutputStream getOutputStream() {
+		if (getStatus().equals(FileTransfer.Status.negotiated)) {
+			return outputStream;
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * This method handles the negotiation of the file transfer and the stream,
+	 * it only returns the created stream after the negotiation has been completed.
+	 *
+	 * @param fileName
+	 *            The name of the file that will be transmitted. It is
+	 *            preferable for this name to have an extension as it will be
+	 *            used to determine the type of file it is.
+	 * @param fileSize
+	 *            The size in bytes of the file that will be transmitted.
+	 * @param description
+	 *            A description of the file that will be transmitted.
+	 * @return The OutputStream that is connected to the peer to transmit the
+	 *         file.
+	 * @throws XMPPException
+	 *             Thrown if an error occurs during the file transfer
+	 *             negotiation process.
+	 */
+	public synchronized OutputStream sendFile(String fileName, long fileSize,
+			String description) throws XMPPException {
+		if (isDone() || outputStream != null) {
+			throw new IllegalStateException(
+					"The negotation process has already"
+							+ " been attempted on this file transfer");
+		}
+		try {
+			setFileInfo(fileName, fileSize);
+			this.outputStream = negotiateStream(fileName, fileSize, description);
+		} catch (XMPPException e) {
+			handleXMPPException(e);
+			throw e;
+		}
+		return outputStream;
+	}
+
+	/**
+	 * This methods handles the transfer and stream negotiation process. It
+	 * returns immediately and its progress will be updated through the
+	 * {@link NegotiationProgress} callback.
+	 *
+	 * @param fileName
+	 *            The name of the file that will be transmitted. It is
+	 *            preferable for this name to have an extension as it will be
+	 *            used to determine the type of file it is.
+	 * @param fileSize
+	 *            The size in bytes of the file that will be transmitted.
+	 * @param description
+	 *            A description of the file that will be transmitted.
+	 * @param progress
+	 *            A callback to monitor the progress of the file transfer
+	 *            negotiation process and to retrieve the OutputStream when it
+	 *            is complete.
+	 */
+	public synchronized void sendFile(final String fileName,
+			final long fileSize, final String description,
+			final NegotiationProgress progress)
+    {
+        if(progress == null) {
+            throw new IllegalArgumentException("Callback progress cannot be null.");
+        }
+        checkTransferThread();
+		if (isDone() || outputStream != null) {
+			throw new IllegalStateException(
+					"The negotation process has already"
+							+ " been attempted for this file transfer");
+		}
+        setFileInfo(fileName, fileSize);
+        this.callback = progress;
+        transferThread = new Thread(new Runnable() {
+			public void run() {
+				try {
+					OutgoingFileTransfer.this.outputStream = negotiateStream(
+							fileName, fileSize, description);
+                    progress.outputStreamEstablished(OutgoingFileTransfer.this.outputStream);
+                }
+                catch (XMPPException e) {
+					handleXMPPException(e);
+				}
+			}
+		}, "File Transfer Negotiation " + streamID);
+		transferThread.start();
+	}
+
+	private void checkTransferThread() {
+		if (transferThread != null && transferThread.isAlive() || isDone()) {
+			throw new IllegalStateException(
+					"File transfer in progress or has already completed.");
+		}
+	}
+
+    /**
+	 * This method handles the stream negotiation process and transmits the file
+	 * to the remote user. It returns immediately and the progress of the file
+	 * transfer can be monitored through several methods:
+	 *
+	 * <UL>
+	 * <LI>{@link FileTransfer#getStatus()}
+	 * <LI>{@link FileTransfer#getProgress()}
+	 * <LI>{@link FileTransfer#isDone()}
+	 * </UL>
+	 *
+     * @param file the file to transfer to the remote entity.
+     * @param description a description for the file to transfer.
+	 * @throws XMPPException
+	 *             If there is an error during the negotiation process or the
+	 *             sending of the file.
+	 */
+	public synchronized void sendFile(final File file, final String description)
+			throws XMPPException {
+		checkTransferThread();
+		if (file == null || !file.exists() || !file.canRead()) {
+			throw new IllegalArgumentException("Could not read file");
+		} else {
+			setFileInfo(file.getAbsolutePath(), file.getName(), file.length());
+		}
+
+		transferThread = new Thread(new Runnable() {
+			public void run() {
+				try {
+					outputStream = negotiateStream(file.getName(), file
+							.length(), description);
+				} catch (XMPPException e) {
+					handleXMPPException(e);
+					return;
+				}
+				if (outputStream == null) {
+					return;
+				}
+
+                if (!updateStatus(Status.negotiated, Status.in_progress)) {
+					return;
+				}
+
+				InputStream inputStream = null;
+				try {
+					inputStream = new FileInputStream(file);
+					writeToStream(inputStream, outputStream);
+				} catch (FileNotFoundException e) {
+					setStatus(FileTransfer.Status.error);
+					setError(Error.bad_file);
+					setException(e);
+				} catch (XMPPException e) {
+					setStatus(FileTransfer.Status.error);
+					setException(e);
+				} finally {
+					try {
+						if (inputStream != null) {
+							inputStream.close();
+						}
+
+						outputStream.flush();
+						outputStream.close();
+					} catch (IOException e) {
+                        /* Do Nothing */
+					}
+				}
+                updateStatus(Status.in_progress, FileTransfer.Status.complete);
+				}
+
+		}, "File Transfer " + streamID);
+		transferThread.start();
+	}
+
+    /**
+	 * This method handles the stream negotiation process and transmits the file
+	 * to the remote user. It returns immediately and the progress of the file
+	 * transfer can be monitored through several methods:
+	 *
+	 * <UL>
+	 * <LI>{@link FileTransfer#getStatus()}
+	 * <LI>{@link FileTransfer#getProgress()}
+	 * <LI>{@link FileTransfer#isDone()}
+	 * </UL>
+	 *
+     * @param in the stream to transfer to the remote entity.
+     * @param fileName the name of the file that is transferred
+     * @param fileSize the size of the file that is transferred
+     * @param description a description for the file to transfer.
+	 */
+	public synchronized void sendStream(final InputStream in, final String fileName, final long fileSize, final String description){
+		checkTransferThread();
+
+		setFileInfo(fileName, fileSize);
+		transferThread = new Thread(new Runnable() {
+			public void run() {
+                //Create packet filter
+                try {
+					outputStream = negotiateStream(fileName, fileSize, description);
+				} catch (XMPPException e) {
+					handleXMPPException(e);
+					return;
+				}
+				if (outputStream == null) {
+					return;
+				}
+
+                if (!updateStatus(Status.negotiated, Status.in_progress)) {
+					return;
+				}
+				try {
+					writeToStream(in, outputStream);
+				} catch (XMPPException e) {
+					setStatus(FileTransfer.Status.error);
+					setException(e);
+				} finally {
+					try {
+						if (in != null) {
+							in.close();
+						}
+
+						outputStream.flush();
+						outputStream.close();
+					} catch (IOException e) {
+                        /* Do Nothing */
+					}
+				}
+                updateStatus(Status.in_progress, FileTransfer.Status.complete);
+				}
+
+		}, "File Transfer " + streamID);
+		transferThread.start();
+	}
+
+	private void handleXMPPException(XMPPException e) {
+		XMPPError error = e.getXMPPError();
+		if (error != null) {
+			int code = error.getCode();
+			if (code == 403) {
+				setStatus(Status.refused);
+				return;
+			}
+            else if (code == 400) {
+				setStatus(Status.error);
+				setError(Error.not_acceptable);
+            }
+            else {
+                setStatus(FileTransfer.Status.error);
+            }
+        }
+
+        setException(e);
+	}
+
+	/**
+	 * Returns the amount of bytes that have been sent for the file transfer. Or
+	 * -1 if the file transfer has not started.
+	 * <p>
+	 * Note: This method is only useful when the {@link #sendFile(File, String)}
+	 * method is called, as it is the only method that actually transmits the
+	 * file.
+	 *
+	 * @return Returns the amount of bytes that have been sent for the file
+	 *         transfer. Or -1 if the file transfer has not started.
+	 */
+	public long getBytesSent() {
+		return amountWritten;
+	}
+
+	private OutputStream negotiateStream(String fileName, long fileSize,
+			String description) throws XMPPException {
+		// Negotiate the file transfer profile
+
+        if (!updateStatus(Status.initial, Status.negotiating_transfer)) {
+            throw new XMPPException("Illegal state change");
+        }
+		StreamNegotiator streamNegotiator = negotiator.negotiateOutgoingTransfer(
+				getPeer(), streamID, fileName, fileSize, description,
+				RESPONSE_TIMEOUT);
+
+		if (streamNegotiator == null) {
+			setStatus(Status.error);
+			setError(Error.no_response);
+			return null;
+		}
+
+        // Negotiate the stream
+        if (!updateStatus(Status.negotiating_transfer, Status.negotiating_stream)) {
+            throw new XMPPException("Illegal state change");
+        }
+		outputStream = streamNegotiator.createOutgoingStream(streamID,
+                initiator, getPeer());
+
+        if (!updateStatus(Status.negotiating_stream, Status.negotiated)) {
+            throw new XMPPException("Illegal state change");
+		}
+		return outputStream;
+	}
+
+	public void cancel() {
+		setStatus(Status.cancelled);
+	}
+
+    @Override
+    protected boolean updateStatus(Status oldStatus, Status newStatus) {
+        boolean isUpdated = super.updateStatus(oldStatus, newStatus);
+        if(callback != null && isUpdated) {
+            callback.statusUpdated(oldStatus, newStatus);
+        }
+        return isUpdated;
+    }
+
+    @Override
+    protected void setStatus(Status status) {
+        Status oldStatus = getStatus();
+        super.setStatus(status);
+        if(callback != null) {
+            callback.statusUpdated(oldStatus, status);
+        }
+    }
+
+    @Override
+    protected void setException(Exception exception) {
+        super.setException(exception);
+        if(callback != null) {
+            callback.errorEstablishingStream(exception);
+        }
+    }
+
+    /**
+	 * A callback class to retrieve the status of an outgoing transfer
+	 * negotiation process.
+	 *
+	 * @author Alexander Wenckus
+	 *
+	 */
+	public interface NegotiationProgress {
+
+		/**
+		 * Called when the status changes
+         *
+         * @param oldStatus the previous status of the file transfer.
+         * @param newStatus the new status of the file transfer.
+         */
+		void statusUpdated(Status oldStatus, Status newStatus);
+
+		/**
+		 * Once the negotiation process is completed the output stream can be
+		 * retrieved.
+         *
+         * @param stream the established stream which can be used to transfer the file to the remote
+         * entity
+		 */
+		void outputStreamEstablished(OutputStream stream);
+
+        /**
+         * Called when an exception occurs during the negotiation progress.
+         *
+         * @param e the exception that occurred.
+         */
+        void errorEstablishingStream(Exception e);
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
index 3c07fdc..8db0929 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
@@ -1,164 +1,164 @@
-/**

- * 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.smackx.filetransfer;

-

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.io.PushbackInputStream;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.AndFilter;

-import org.jivesoftware.smack.filter.FromMatchesFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;

-import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;

-import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-/**

- * Negotiates a SOCKS5 Bytestream to be used for file transfers. The implementation is based on the

- * {@link Socks5BytestreamManager} and the {@link Socks5BytestreamRequest}.

- * 

- * @author Henning Staib

- * @see <a href="http://xmpp.org/extensions/xep-0065.html">XEP-0065: SOCKS5 Bytestreams</a>

- */

-public class Socks5TransferNegotiator extends StreamNegotiator {

-

-    private Connection connection;

-

-    private Socks5BytestreamManager manager;

-

-    Socks5TransferNegotiator(Connection connection) {

-        this.connection = connection;

-        this.manager = Socks5BytestreamManager.getBytestreamManager(this.connection);

-    }

-

-    @Override

-    public OutputStream createOutgoingStream(String streamID, String initiator, String target)

-                    throws XMPPException {

-        try {

-            return this.manager.establishSession(target, streamID).getOutputStream();

-        }

-        catch (IOException e) {

-            throw new XMPPException("error establishing SOCKS5 Bytestream", e);

-        }

-        catch (InterruptedException e) {

-            throw new XMPPException("error establishing SOCKS5 Bytestream", e);

-        }

-    }

-

-    @Override

-    public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException,

-                    InterruptedException {

-        /*

-         * SOCKS5 initiation listener must ignore next SOCKS5 Bytestream request with given session

-         * ID

-         */

-        this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());

-

-        Packet streamInitiation = initiateIncomingStream(this.connection, initiation);

-        return negotiateIncomingStream(streamInitiation);

-    }

-

-    @Override

-    public PacketFilter getInitiationPacketFilter(final String from, String streamID) {

-        /*

-         * this method is always called prior to #negotiateIncomingStream() so the SOCKS5

-         * InitiationListener must ignore the next SOCKS5 Bytestream request with the given session

-         * ID

-         */

-        this.manager.ignoreBytestreamRequestOnce(streamID);

-

-        return new AndFilter(new FromMatchesFilter(from), new BytestreamSIDFilter(streamID));

-    }

-

-    @Override

-    public String[] getNamespaces() {

-        return new String[] { Socks5BytestreamManager.NAMESPACE };

-    }

-

-    @Override

-    InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException,

-                    InterruptedException {

-        // build SOCKS5 Bytestream request

-        Socks5BytestreamRequest request = new ByteStreamRequest(this.manager,

-                        (Bytestream) streamInitiation);

-

-        // always accept the request

-        Socks5BytestreamSession session = request.accept();

-

-        // test input stream

-        try {

-            PushbackInputStream stream = new PushbackInputStream(session.getInputStream());

-            int firstByte = stream.read();

-            stream.unread(firstByte);

-            return stream;

-        }

-        catch (IOException e) {

-            throw new XMPPException("Error establishing input stream", e);

-        }

-    }

-

-    @Override

-    public void cleanup() {

-        /* do nothing */

-    }

-

-    /**

-     * This PacketFilter accepts an incoming SOCKS5 Bytestream request with a specified session ID.

-     */

-    private static class BytestreamSIDFilter extends PacketTypeFilter {

-

-        private String sessionID;

-

-        public BytestreamSIDFilter(String sessionID) {

-            super(Bytestream.class);

-            if (sessionID == null) {

-                throw new IllegalArgumentException("StreamID cannot be null");

-            }

-            this.sessionID = sessionID;

-        }

-

-        @Override

-        public boolean accept(Packet packet) {

-            if (super.accept(packet)) {

-                Bytestream bytestream = (Bytestream) packet;

-

-                // packet must by of type SET and contains the given session ID

-                return this.sessionID.equals(bytestream.getSessionID())

-                                && IQ.Type.SET.equals(bytestream.getType());

-            }

-            return false;

-        }

-

-    }

-

-    /**

-     * Derive from Socks5BytestreamRequest to access protected constructor.

-     */

-    private static class ByteStreamRequest extends Socks5BytestreamRequest {

-

-        private ByteStreamRequest(Socks5BytestreamManager manager, Bytestream byteStreamRequest) {

-            super(manager, byteStreamRequest);

-        }

-

-    }

-

-}

+/**
+ * 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.smackx.filetransfer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.AndFilter;
+import org.jivesoftware.smack.filter.FromMatchesFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
+import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
+import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+/**
+ * Negotiates a SOCKS5 Bytestream to be used for file transfers. The implementation is based on the
+ * {@link Socks5BytestreamManager} and the {@link Socks5BytestreamRequest}.
+ * 
+ * @author Henning Staib
+ * @see <a href="http://xmpp.org/extensions/xep-0065.html">XEP-0065: SOCKS5 Bytestreams</a>
+ */
+public class Socks5TransferNegotiator extends StreamNegotiator {
+
+    private Connection connection;
+
+    private Socks5BytestreamManager manager;
+
+    Socks5TransferNegotiator(Connection connection) {
+        this.connection = connection;
+        this.manager = Socks5BytestreamManager.getBytestreamManager(this.connection);
+    }
+
+    @Override
+    public OutputStream createOutgoingStream(String streamID, String initiator, String target)
+                    throws XMPPException {
+        try {
+            return this.manager.establishSession(target, streamID).getOutputStream();
+        }
+        catch (IOException e) {
+            throw new XMPPException("error establishing SOCKS5 Bytestream", e);
+        }
+        catch (InterruptedException e) {
+            throw new XMPPException("error establishing SOCKS5 Bytestream", e);
+        }
+    }
+
+    @Override
+    public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException,
+                    InterruptedException {
+        /*
+         * SOCKS5 initiation listener must ignore next SOCKS5 Bytestream request with given session
+         * ID
+         */
+        this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());
+
+        Packet streamInitiation = initiateIncomingStream(this.connection, initiation);
+        return negotiateIncomingStream(streamInitiation);
+    }
+
+    @Override
+    public PacketFilter getInitiationPacketFilter(final String from, String streamID) {
+        /*
+         * this method is always called prior to #negotiateIncomingStream() so the SOCKS5
+         * InitiationListener must ignore the next SOCKS5 Bytestream request with the given session
+         * ID
+         */
+        this.manager.ignoreBytestreamRequestOnce(streamID);
+
+        return new AndFilter(new FromMatchesFilter(from), new BytestreamSIDFilter(streamID));
+    }
+
+    @Override
+    public String[] getNamespaces() {
+        return new String[] { Socks5BytestreamManager.NAMESPACE };
+    }
+
+    @Override
+    InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException,
+                    InterruptedException {
+        // build SOCKS5 Bytestream request
+        Socks5BytestreamRequest request = new ByteStreamRequest(this.manager,
+                        (Bytestream) streamInitiation);
+
+        // always accept the request
+        Socks5BytestreamSession session = request.accept();
+
+        // test input stream
+        try {
+            PushbackInputStream stream = new PushbackInputStream(session.getInputStream());
+            int firstByte = stream.read();
+            stream.unread(firstByte);
+            return stream;
+        }
+        catch (IOException e) {
+            throw new XMPPException("Error establishing input stream", e);
+        }
+    }
+
+    @Override
+    public void cleanup() {
+        /* do nothing */
+    }
+
+    /**
+     * This PacketFilter accepts an incoming SOCKS5 Bytestream request with a specified session ID.
+     */
+    private static class BytestreamSIDFilter extends PacketTypeFilter {
+
+        private String sessionID;
+
+        public BytestreamSIDFilter(String sessionID) {
+            super(Bytestream.class);
+            if (sessionID == null) {
+                throw new IllegalArgumentException("StreamID cannot be null");
+            }
+            this.sessionID = sessionID;
+        }
+
+        @Override
+        public boolean accept(Packet packet) {
+            if (super.accept(packet)) {
+                Bytestream bytestream = (Bytestream) packet;
+
+                // packet must by of type SET and contains the given session ID
+                return this.sessionID.equals(bytestream.getSessionID())
+                                && IQ.Type.SET.equals(bytestream.getType());
+            }
+            return false;
+        }
+
+    }
+
+    /**
+     * Derive from Socks5BytestreamRequest to access protected constructor.
+     */
+    private static class ByteStreamRequest extends Socks5BytestreamRequest {
+
+        private ByteStreamRequest(Socks5BytestreamManager manager, Bytestream byteStreamRequest) {
+            super(manager, byteStreamRequest);
+        }
+
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java
index 5eefe43..fb60ee1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java
@@ -1,167 +1,167 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.filetransfer;

-

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-

-import java.io.InputStream;

-import java.io.OutputStream;

-

-/**

- * After the file transfer negotiation process is completed according to

- * JEP-0096, the negotiation process is passed off to a particular stream

- * negotiator. The stream negotiator will then negotiate the chosen stream and

- * return the stream to transfer the file.

- *

- * @author Alexander Wenckus

- */

-public abstract class StreamNegotiator {

-

-    /**

-     * Creates the initiation acceptance packet to forward to the stream

-     * initiator.

-     *

-     * @param streamInitiationOffer The offer from the stream initiator to connect for a stream.

-     * @param namespaces            The namespace that relates to the accepted means of transfer.

-     * @return The response to be forwarded to the initiator.

-     */

-    public StreamInitiation createInitiationAccept(

-            StreamInitiation streamInitiationOffer, String[] namespaces)

-    {

-        StreamInitiation response = new StreamInitiation();

-        response.setTo(streamInitiationOffer.getFrom());

-        response.setFrom(streamInitiationOffer.getTo());

-        response.setType(IQ.Type.RESULT);

-        response.setPacketID(streamInitiationOffer.getPacketID());

-

-        DataForm form = new DataForm(Form.TYPE_SUBMIT);

-        FormField field = new FormField(

-                FileTransferNegotiator.STREAM_DATA_FIELD_NAME);

-        for (String namespace : namespaces) {

-            field.addValue(namespace);

-        }

-        form.addField(field);

-

-        response.setFeatureNegotiationForm(form);

-        return response;

-    }

-

-

-    public IQ createError(String from, String to, String packetID, XMPPError xmppError) {

-        IQ iq = FileTransferNegotiator.createIQ(packetID, to, from, IQ.Type.ERROR);

-        iq.setError(xmppError);

-        return iq;

-    }

-

-    Packet initiateIncomingStream(Connection connection, StreamInitiation initiation) throws XMPPException {

-        StreamInitiation response = createInitiationAccept(initiation,

-                getNamespaces());

-

-        // establish collector to await response

-        PacketCollector collector = connection

-                .createPacketCollector(getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID()));

-        connection.sendPacket(response);

-

-        Packet streamMethodInitiation = collector

-                .nextResult(SmackConfiguration.getPacketReplyTimeout());

-        collector.cancel();

-        if (streamMethodInitiation == null) {

-            throw new XMPPException("No response from file transfer initiator");

-        }

-

-        return streamMethodInitiation;

-    }

-

-    /**

-     * Returns the packet filter that will return the initiation packet for the appropriate stream

-     * initiation.

-     *

-     * @param from     The initiator of the file transfer.

-     * @param streamID The stream ID related to the transfer.

-     * @return The <b><i>PacketFilter</b></i> that will return the packet relatable to the stream

-     *         initiation.

-     */

-    public abstract PacketFilter getInitiationPacketFilter(String from, String streamID);

-

-

-    abstract InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException,

-            InterruptedException;

-

-    /**

-     * This method handles the file stream download negotiation process. The

-     * appropriate stream negotiator's initiate incoming stream is called after

-     * an appropriate file transfer method is selected. The manager will respond

-     * to the initiator with the selected means of transfer, then it will handle

-     * any negotiation specific to the particular transfer method. This method

-     * returns the InputStream, ready to transfer the file.

-     *

-     * @param initiation The initiation that triggered this download.

-     * @return After the negotiation process is complete, the InputStream to

-     *         write a file to is returned.

-     * @throws XMPPException If an error occurs during this process an XMPPException is

-     *                       thrown.

-     * @throws InterruptedException If thread is interrupted.

-     */

-    public abstract InputStream createIncomingStream(StreamInitiation initiation)

-            throws XMPPException, InterruptedException;

-

-    /**

-     * This method handles the file upload stream negotiation process. The

-     * particular stream negotiator is determined during the file transfer

-     * negotiation process. This method returns the OutputStream to transmit the

-     * file to the remote user.

-     *

-     * @param streamID  The streamID that uniquely identifies the file transfer.

-     * @param initiator The fully-qualified JID of the initiator of the file transfer.

-     * @param target    The fully-qualified JID of the target or receiver of the file

-     *                  transfer.

-     * @return The negotiated stream ready for data.

-     * @throws XMPPException If an error occurs during the negotiation process an

-     *                       exception will be thrown.

-     */

-    public abstract OutputStream createOutgoingStream(String streamID,

-            String initiator, String target) throws XMPPException;

-

-    /**

-     * Returns the XMPP namespace reserved for this particular type of file

-     * transfer.

-     *

-     * @return Returns the XMPP namespace reserved for this particular type of

-     *         file transfer.

-     */

-    public abstract String[] getNamespaces();

-

-    /**

-     * Cleanup any and all resources associated with this negotiator.

-     */

-    public abstract void cleanup();

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.filetransfer;
+
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * After the file transfer negotiation process is completed according to
+ * JEP-0096, the negotiation process is passed off to a particular stream
+ * negotiator. The stream negotiator will then negotiate the chosen stream and
+ * return the stream to transfer the file.
+ *
+ * @author Alexander Wenckus
+ */
+public abstract class StreamNegotiator {
+
+    /**
+     * Creates the initiation acceptance packet to forward to the stream
+     * initiator.
+     *
+     * @param streamInitiationOffer The offer from the stream initiator to connect for a stream.
+     * @param namespaces            The namespace that relates to the accepted means of transfer.
+     * @return The response to be forwarded to the initiator.
+     */
+    public StreamInitiation createInitiationAccept(
+            StreamInitiation streamInitiationOffer, String[] namespaces)
+    {
+        StreamInitiation response = new StreamInitiation();
+        response.setTo(streamInitiationOffer.getFrom());
+        response.setFrom(streamInitiationOffer.getTo());
+        response.setType(IQ.Type.RESULT);
+        response.setPacketID(streamInitiationOffer.getPacketID());
+
+        DataForm form = new DataForm(Form.TYPE_SUBMIT);
+        FormField field = new FormField(
+                FileTransferNegotiator.STREAM_DATA_FIELD_NAME);
+        for (String namespace : namespaces) {
+            field.addValue(namespace);
+        }
+        form.addField(field);
+
+        response.setFeatureNegotiationForm(form);
+        return response;
+    }
+
+
+    public IQ createError(String from, String to, String packetID, XMPPError xmppError) {
+        IQ iq = FileTransferNegotiator.createIQ(packetID, to, from, IQ.Type.ERROR);
+        iq.setError(xmppError);
+        return iq;
+    }
+
+    Packet initiateIncomingStream(Connection connection, StreamInitiation initiation) throws XMPPException {
+        StreamInitiation response = createInitiationAccept(initiation,
+                getNamespaces());
+
+        // establish collector to await response
+        PacketCollector collector = connection
+                .createPacketCollector(getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID()));
+        connection.sendPacket(response);
+
+        Packet streamMethodInitiation = collector
+                .nextResult(SmackConfiguration.getPacketReplyTimeout());
+        collector.cancel();
+        if (streamMethodInitiation == null) {
+            throw new XMPPException("No response from file transfer initiator");
+        }
+
+        return streamMethodInitiation;
+    }
+
+    /**
+     * Returns the packet filter that will return the initiation packet for the appropriate stream
+     * initiation.
+     *
+     * @param from     The initiator of the file transfer.
+     * @param streamID The stream ID related to the transfer.
+     * @return The <b><i>PacketFilter</b></i> that will return the packet relatable to the stream
+     *         initiation.
+     */
+    public abstract PacketFilter getInitiationPacketFilter(String from, String streamID);
+
+
+    abstract InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException,
+            InterruptedException;
+
+    /**
+     * This method handles the file stream download negotiation process. The
+     * appropriate stream negotiator's initiate incoming stream is called after
+     * an appropriate file transfer method is selected. The manager will respond
+     * to the initiator with the selected means of transfer, then it will handle
+     * any negotiation specific to the particular transfer method. This method
+     * returns the InputStream, ready to transfer the file.
+     *
+     * @param initiation The initiation that triggered this download.
+     * @return After the negotiation process is complete, the InputStream to
+     *         write a file to is returned.
+     * @throws XMPPException If an error occurs during this process an XMPPException is
+     *                       thrown.
+     * @throws InterruptedException If thread is interrupted.
+     */
+    public abstract InputStream createIncomingStream(StreamInitiation initiation)
+            throws XMPPException, InterruptedException;
+
+    /**
+     * This method handles the file upload stream negotiation process. The
+     * particular stream negotiator is determined during the file transfer
+     * negotiation process. This method returns the OutputStream to transmit the
+     * file to the remote user.
+     *
+     * @param streamID  The streamID that uniquely identifies the file transfer.
+     * @param initiator The fully-qualified JID of the initiator of the file transfer.
+     * @param target    The fully-qualified JID of the target or receiver of the file
+     *                  transfer.
+     * @return The negotiated stream ready for data.
+     * @throws XMPPException If an error occurs during the negotiation process an
+     *                       exception will be thrown.
+     */
+    public abstract OutputStream createOutgoingStream(String streamID,
+            String initiator, String target) throws XMPPException;
+
+    /**
+     * Returns the XMPP namespace reserved for this particular type of file
+     * transfer.
+     *
+     * @return Returns the XMPP namespace reserved for this particular type of
+     *         file transfer.
+     */
+    public abstract String[] getNamespaces();
+
+    /**
+     * Cleanup any and all resources associated with this negotiator.
+     */
+    public abstract void cleanup();
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AdHocCommandData.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AdHocCommandData.java
index bceffcd..37ba14d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AdHocCommandData.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AdHocCommandData.java
@@ -1,279 +1,279 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-2008 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.smackx.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.commands.AdHocCommand;

-import org.jivesoftware.smackx.commands.AdHocCommand.Action;

-import org.jivesoftware.smackx.commands.AdHocCommand.SpecificErrorCondition;

-import org.jivesoftware.smackx.commands.AdHocCommandNote;

-

-import java.util.ArrayList;

-import java.util.List;

-

-/**

- * Represents the state and the request of the execution of an adhoc command.

- * 

- * @author Gabriel Guardincerri

- */

-public class AdHocCommandData extends IQ {

-

-    /* JID of the command host */

-    private String id;

-

-    /* Command name */

-    private String name;

-

-    /* Command identifier */

-    private String node;

-

-    /* Unique ID of the execution */

-    private String sessionID;

-

-    private List<AdHocCommandNote> notes = new ArrayList<AdHocCommandNote>();

-

-    private DataForm form;

-

-    /* Action request to be executed */

-    private AdHocCommand.Action action;

-

-    /* Current execution status */

-    private AdHocCommand.Status status;

-

-    private ArrayList<AdHocCommand.Action> actions = new ArrayList<AdHocCommand.Action>();

-

-    private AdHocCommand.Action executeAction;

-

-    private String lang;

-

-    public AdHocCommandData() {

-    }

-

-    @Override

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<command xmlns=\"http://jabber.org/protocol/commands\"");

-        buf.append(" node=\"").append(node).append("\"");

-        if (sessionID != null) {

-            if (!sessionID.equals("")) {

-                buf.append(" sessionid=\"").append(sessionID).append("\"");

-            }

-        }

-        if (status != null) {

-            buf.append(" status=\"").append(status).append("\"");

-        }

-        if (action != null) {

-            buf.append(" action=\"").append(action).append("\"");

-        }

-

-        if (lang != null) {

-            if (!lang.equals("")) {

-                buf.append(" lang=\"").append(lang).append("\"");

-            }

-        }

-        buf.append(">");

-

-        if (getType() == Type.RESULT) {

-            buf.append("<actions");

-

-            if (executeAction != null) {

-                buf.append(" execute=\"").append(executeAction).append("\"");

-            }

-            if (actions.size() == 0) {

-                buf.append("/>");

-            } else {

-                buf.append(">");

-

-                for (AdHocCommand.Action action : actions) {

-                    buf.append("<").append(action).append("/>");

-                }

-                buf.append("</actions>");

-            }

-        }

-

-        if (form != null) {

-            buf.append(form.toXML());

-        }

-

-        for (AdHocCommandNote note : notes) {

-            buf.append("<note type=\"").append(note.getType().toString()).append("\">");

-            buf.append(note.getValue());

-            buf.append("</note>");

-        }

-

-        // TODO ERRORS

-//        if (getError() != null) {

-//            buf.append(getError().toXML());

-//        }

-

-        buf.append("</command>");

-        return buf.toString();

-    }

-

-    /**

-     * Returns the JID of the command host.

-     *

-     * @return the JID of the command host.

-     */

-    public String getId() {

-        return id;

-    }

-

-    public void setId(String id) {

-        this.id = id;

-    }

-

-    /**

-     * Returns the human name of the command

-     *

-     * @return the name of the command.

-     */

-    public String getName() {

-        return name;

-    }

-

-    public void setName(String name) {

-        this.name = name;

-    }

-

-    /**

-     * Returns the identifier of the command

-     *

-     * @return the node.

-     */

-    public String getNode() {

-        return node;

-    }

-

-    public void setNode(String node) {

-        this.node = node;

-    }

-

-    /**

-     * Returns the list of notes that the command has.

-     *

-     * @return the notes.

-     */

-    public List<AdHocCommandNote> getNotes() {

-        return notes;

-    }

-

-    public void addNote(AdHocCommandNote note) {

-        this.notes.add(note);

-    }

-

-    public void remveNote(AdHocCommandNote note) {

-        this.notes.remove(note);

-    }

-

-    /**

-     * Returns the form of the command.

-     *

-     * @return the data form associated with the command.

-     */

-    public DataForm getForm() {

-        return form;

-    }

-

-    public void setForm(DataForm form) {

-        this.form = form;

-    }

-

-    /**

-     * Returns the action to execute. The action is set only on a request.

-     *

-     * @return the action to execute.

-     */

-    public AdHocCommand.Action getAction() {

-        return action;

-    }

-

-    public void setAction(AdHocCommand.Action action) {

-        this.action = action;

-    }

-

-    /**

-     * Returns the status of the execution.

-     *

-     * @return the status.

-     */

-    public AdHocCommand.Status getStatus() {

-        return status;

-    }

-

-    public void setStatus(AdHocCommand.Status status) {

-        this.status = status;

-    }

-

-    public List<Action> getActions() {

-        return actions;

-    }

-

-    public void addAction(Action action) {

-        actions.add(action);

-    }

-

-    public void setExecuteAction(Action executeAction) {

-        this.executeAction = executeAction;

-    }

-

-    public Action getExecuteAction() {

-        return executeAction;

-    }

-

-    public void setSessionID(String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public static class SpecificError implements PacketExtension {

-

-        public static final String namespace = "http://jabber.org/protocol/commands";

-        

-        public SpecificErrorCondition condition;

-        

-        public SpecificError(SpecificErrorCondition condition) {

-            this.condition = condition;

-        }

-        

-        public String getElementName() {

-            return condition.toString();

-        }

-        public String getNamespace() {

-            return namespace;

-        }

-

-        public SpecificErrorCondition getCondition() {

-            return condition;

-        }

-        

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-            buf.append("<").append(getElementName());

-            buf.append(" xmlns=\"").append(getNamespace()).append("\"/>");

-            return buf.toString();

-        }

-    }

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-2008 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.smackx.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.commands.AdHocCommand;
+import org.jivesoftware.smackx.commands.AdHocCommand.Action;
+import org.jivesoftware.smackx.commands.AdHocCommand.SpecificErrorCondition;
+import org.jivesoftware.smackx.commands.AdHocCommandNote;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the state and the request of the execution of an adhoc command.
+ * 
+ * @author Gabriel Guardincerri
+ */
+public class AdHocCommandData extends IQ {
+
+    /* JID of the command host */
+    private String id;
+
+    /* Command name */
+    private String name;
+
+    /* Command identifier */
+    private String node;
+
+    /* Unique ID of the execution */
+    private String sessionID;
+
+    private List<AdHocCommandNote> notes = new ArrayList<AdHocCommandNote>();
+
+    private DataForm form;
+
+    /* Action request to be executed */
+    private AdHocCommand.Action action;
+
+    /* Current execution status */
+    private AdHocCommand.Status status;
+
+    private ArrayList<AdHocCommand.Action> actions = new ArrayList<AdHocCommand.Action>();
+
+    private AdHocCommand.Action executeAction;
+
+    private String lang;
+
+    public AdHocCommandData() {
+    }
+
+    @Override
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<command xmlns=\"http://jabber.org/protocol/commands\"");
+        buf.append(" node=\"").append(node).append("\"");
+        if (sessionID != null) {
+            if (!sessionID.equals("")) {
+                buf.append(" sessionid=\"").append(sessionID).append("\"");
+            }
+        }
+        if (status != null) {
+            buf.append(" status=\"").append(status).append("\"");
+        }
+        if (action != null) {
+            buf.append(" action=\"").append(action).append("\"");
+        }
+
+        if (lang != null) {
+            if (!lang.equals("")) {
+                buf.append(" lang=\"").append(lang).append("\"");
+            }
+        }
+        buf.append(">");
+
+        if (getType() == Type.RESULT) {
+            buf.append("<actions");
+
+            if (executeAction != null) {
+                buf.append(" execute=\"").append(executeAction).append("\"");
+            }
+            if (actions.size() == 0) {
+                buf.append("/>");
+            } else {
+                buf.append(">");
+
+                for (AdHocCommand.Action action : actions) {
+                    buf.append("<").append(action).append("/>");
+                }
+                buf.append("</actions>");
+            }
+        }
+
+        if (form != null) {
+            buf.append(form.toXML());
+        }
+
+        for (AdHocCommandNote note : notes) {
+            buf.append("<note type=\"").append(note.getType().toString()).append("\">");
+            buf.append(note.getValue());
+            buf.append("</note>");
+        }
+
+        // TODO ERRORS
+//        if (getError() != null) {
+//            buf.append(getError().toXML());
+//        }
+
+        buf.append("</command>");
+        return buf.toString();
+    }
+
+    /**
+     * Returns the JID of the command host.
+     *
+     * @return the JID of the command host.
+     */
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Returns the human name of the command
+     *
+     * @return the name of the command.
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the identifier of the command
+     *
+     * @return the node.
+     */
+    public String getNode() {
+        return node;
+    }
+
+    public void setNode(String node) {
+        this.node = node;
+    }
+
+    /**
+     * Returns the list of notes that the command has.
+     *
+     * @return the notes.
+     */
+    public List<AdHocCommandNote> getNotes() {
+        return notes;
+    }
+
+    public void addNote(AdHocCommandNote note) {
+        this.notes.add(note);
+    }
+
+    public void remveNote(AdHocCommandNote note) {
+        this.notes.remove(note);
+    }
+
+    /**
+     * Returns the form of the command.
+     *
+     * @return the data form associated with the command.
+     */
+    public DataForm getForm() {
+        return form;
+    }
+
+    public void setForm(DataForm form) {
+        this.form = form;
+    }
+
+    /**
+     * Returns the action to execute. The action is set only on a request.
+     *
+     * @return the action to execute.
+     */
+    public AdHocCommand.Action getAction() {
+        return action;
+    }
+
+    public void setAction(AdHocCommand.Action action) {
+        this.action = action;
+    }
+
+    /**
+     * Returns the status of the execution.
+     *
+     * @return the status.
+     */
+    public AdHocCommand.Status getStatus() {
+        return status;
+    }
+
+    public void setStatus(AdHocCommand.Status status) {
+        this.status = status;
+    }
+
+    public List<Action> getActions() {
+        return actions;
+    }
+
+    public void addAction(Action action) {
+        actions.add(action);
+    }
+
+    public void setExecuteAction(Action executeAction) {
+        this.executeAction = executeAction;
+    }
+
+    public Action getExecuteAction() {
+        return executeAction;
+    }
+
+    public void setSessionID(String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public static class SpecificError implements PacketExtension {
+
+        public static final String namespace = "http://jabber.org/protocol/commands";
+        
+        public SpecificErrorCondition condition;
+        
+        public SpecificError(SpecificErrorCondition condition) {
+            this.condition = condition;
+        }
+        
+        public String getElementName() {
+            return condition.toString();
+        }
+        public String getNamespace() {
+            return namespace;
+        }
+
+        public SpecificErrorCondition getCondition() {
+            return condition;
+        }
+        
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+            buf.append("<").append(getElementName());
+            buf.append(" xmlns=\"").append(getNamespace()).append("\"/>");
+            return buf.toString();
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AttentionExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AttentionExtension.java
index d86fa41..ad40851 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AttentionExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/AttentionExtension.java
@@ -1,100 +1,100 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2010 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.smackx.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * A PacketExtension that implements XEP-0224: Attention

- * 

- * This extension is expected to be added to message stanzas of type 'headline.'

- * Please refer to the XEP for more implementation guidelines.

- * 

- * @author Guus der Kinderen, guus.der.kinderen@gmail.com

- * @see <a

- *      href="http://xmpp.org/extensions/xep-0224.html">XEP-0224:&nbsp;Attention</a>

- */

-public class AttentionExtension implements PacketExtension {

-

-    /**

-     * The XML element name of an 'attention' extension.

-     */

-    public static final String ELEMENT_NAME = "attention";

-

-    /**

-     * The namespace that qualifies the XML element of an 'attention' extension.

-     */

-    public static final String NAMESPACE = "urn:xmpp:attention:0";

-

-    /*

-     * (non-Javadoc)

-     * 

-     * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()

-     */

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    /*

-     * (non-Javadoc)

-     * 

-     * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()

-     */

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    /*

-     * (non-Javadoc)

-     * 

-     * @see org.jivesoftware.smack.packet.PacketExtension#toXML()

-     */

-    public String toXML() {

-        final StringBuilder sb = new StringBuilder();

-        sb.append("<").append(getElementName()).append(" xmlns=\"").append(

-                getNamespace()).append("\"/>");

-        return sb.toString();

-    }

-

-    /**

-     * A {@link PacketExtensionProvider} for the {@link AttentionExtension}. As

-     * Attention elements have no state/information other than the element name

-     * and namespace, this implementation simply returns new instances of

-     * {@link AttentionExtension}.

-     * 

-     * @author Guus der Kinderen, guus.der.kinderen@gmail.com

-s     */

-    public static class Provider implements PacketExtensionProvider {

-

-        /*

-         * (non-Javadoc)

-         * 

-         * @see

-         * org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension

-         * (org.xmlpull.v1.XmlPullParser)

-         */

-        public PacketExtension parseExtension(XmlPullParser arg0)

-                throws Exception {

-            return new AttentionExtension();

-        }

-    }

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2010 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.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A PacketExtension that implements XEP-0224: Attention
+ * 
+ * This extension is expected to be added to message stanzas of type 'headline.'
+ * Please refer to the XEP for more implementation guidelines.
+ * 
+ * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+ * @see <a
+ *      href="http://xmpp.org/extensions/xep-0224.html">XEP-0224:&nbsp;Attention</a>
+ */
+public class AttentionExtension implements PacketExtension {
+
+    /**
+     * The XML element name of an 'attention' extension.
+     */
+    public static final String ELEMENT_NAME = "attention";
+
+    /**
+     * The namespace that qualifies the XML element of an 'attention' extension.
+     */
+    public static final String NAMESPACE = "urn:xmpp:attention:0";
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()
+     */
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()
+     */
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.jivesoftware.smack.packet.PacketExtension#toXML()
+     */
+    public String toXML() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("<").append(getElementName()).append(" xmlns=\"").append(
+                getNamespace()).append("\"/>");
+        return sb.toString();
+    }
+
+    /**
+     * A {@link PacketExtensionProvider} for the {@link AttentionExtension}. As
+     * Attention elements have no state/information other than the element name
+     * and namespace, this implementation simply returns new instances of
+     * {@link AttentionExtension}.
+     * 
+     * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+s     */
+    public static class Provider implements PacketExtensionProvider {
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension
+         * (org.xmlpull.v1.XmlPullParser)
+         */
+        public PacketExtension parseExtension(XmlPullParser arg0)
+                throws Exception {
+            return new AttentionExtension();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/ChatStateExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/ChatStateExtension.java
index ecc6acc..a9fbbb4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/ChatStateExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/ChatStateExtension.java
@@ -1,73 +1,73 @@
-/**

- * $RCSfile$

- * $Revision: 2407 $

- * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $

- *

- * 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.smackx.packet;

-

-import org.jivesoftware.smackx.ChatState;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Represents a chat state which is an extension to message packets which is used to indicate

- * the current status of a chat participant.

- *

- * @author Alexander Wenckus

- * @see org.jivesoftware.smackx.ChatState

- */

-public class ChatStateExtension implements PacketExtension {

-

-    private ChatState state;

-

-    /**

-     * Default constructor. The argument provided is the state that the extension will represent.

-     *

-     * @param state the state that the extension represents.

-     */

-    public ChatStateExtension(ChatState state) {

-        this.state = state;

-    }

-

-    public String getElementName() {

-        return state.name();

-    }

-

-    public String getNamespace() {

-        return "http://jabber.org/protocol/chatstates";

-    }

-

-    public String toXML() {

-        return "<" + getElementName() + " xmlns=\"" + getNamespace() + "\" />";

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            ChatState state;

-            try {

-                state = ChatState.valueOf(parser.getName());

-            }

-            catch (Exception ex) {

-                state = ChatState.active;

-            }

-            return new ChatStateExtension(state);

-        }

-    }

-}

+/**
+ * $RCSfile$
+ * $Revision: 2407 $
+ * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $
+ *
+ * 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.smackx.packet;
+
+import org.jivesoftware.smackx.ChatState;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Represents a chat state which is an extension to message packets which is used to indicate
+ * the current status of a chat participant.
+ *
+ * @author Alexander Wenckus
+ * @see org.jivesoftware.smackx.ChatState
+ */
+public class ChatStateExtension implements PacketExtension {
+
+    private ChatState state;
+
+    /**
+     * Default constructor. The argument provided is the state that the extension will represent.
+     *
+     * @param state the state that the extension represents.
+     */
+    public ChatStateExtension(ChatState state) {
+        this.state = state;
+    }
+
+    public String getElementName() {
+        return state.name();
+    }
+
+    public String getNamespace() {
+        return "http://jabber.org/protocol/chatstates";
+    }
+
+    public String toXML() {
+        return "<" + getElementName() + " xmlns=\"" + getNamespace() + "\" />";
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            ChatState state;
+            try {
+                state = ChatState.valueOf(parser.getName());
+            }
+            catch (Exception ex) {
+                state = ChatState.active;
+            }
+            return new ChatStateExtension(state);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/DelayInfo.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/DelayInfo.java
index f5ba78f..5e8f11a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/DelayInfo.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/DelayInfo.java
@@ -1,105 +1,105 @@
-/**

- * 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.smackx.packet;

-

-import java.util.Date;

-

-import org.jivesoftware.smack.util.StringUtils;

-

-/**

- * A decorator for the {@link DelayInformation} class to transparently support

- * both the new <b>Delay Delivery</b> specification <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a> and 

- * the old one <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.

- * 

- * Existing code can be backward compatible. 

- * 

- * @author Robin Collier

- */

-public class DelayInfo extends DelayInformation

-{

-    

-	DelayInformation wrappedInfo;

-

-        /**

-         * Creates a new instance with given delay information. 

-         * @param delay the delay information

-         */

-	public DelayInfo(DelayInformation delay)

-	{

-		super(delay.getStamp());

-		wrappedInfo = delay;

-	}

-	

-	@Override

-	public String getFrom()

-	{

-		return wrappedInfo.getFrom();

-	}

-

-	@Override

-	public String getReason()

-	{

-		return wrappedInfo.getReason();

-	}

-

-	@Override

-	public Date getStamp()

-	{

-		return wrappedInfo.getStamp();

-	}

-

-	@Override

-	public void setFrom(String from)

-	{

-		wrappedInfo.setFrom(from);

-	}

-

-	@Override

-	public void setReason(String reason)

-	{

-		wrappedInfo.setReason(reason);

-	}

-

-	@Override

-	public String getElementName()

-	{

-		return "delay";

-	}

-

-	@Override

-	public String getNamespace()

-	{

-		return "urn:xmpp:delay";

-	}

-

-	@Override

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(

-                "\"");

-        buf.append(" stamp=\"");

-        buf.append(StringUtils.formatXEP0082Date(getStamp()));

-        buf.append("\"");

-        if (getFrom() != null && getFrom().length() > 0) {

-            buf.append(" from=\"").append(getFrom()).append("\"");

-        }

-        buf.append(">");

-        if (getReason() != null && getReason().length() > 0) {

-            buf.append(getReason());

-        }

-        buf.append("</").append(getElementName()).append(">");

-        return buf.toString();

-    }

-	

-}

+/**
+ * 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.smackx.packet;
+
+import java.util.Date;
+
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * A decorator for the {@link DelayInformation} class to transparently support
+ * both the new <b>Delay Delivery</b> specification <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a> and 
+ * the old one <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.
+ * 
+ * Existing code can be backward compatible. 
+ * 
+ * @author Robin Collier
+ */
+public class DelayInfo extends DelayInformation
+{
+    
+	DelayInformation wrappedInfo;
+
+        /**
+         * Creates a new instance with given delay information. 
+         * @param delay the delay information
+         */
+	public DelayInfo(DelayInformation delay)
+	{
+		super(delay.getStamp());
+		wrappedInfo = delay;
+	}
+	
+	@Override
+	public String getFrom()
+	{
+		return wrappedInfo.getFrom();
+	}
+
+	@Override
+	public String getReason()
+	{
+		return wrappedInfo.getReason();
+	}
+
+	@Override
+	public Date getStamp()
+	{
+		return wrappedInfo.getStamp();
+	}
+
+	@Override
+	public void setFrom(String from)
+	{
+		wrappedInfo.setFrom(from);
+	}
+
+	@Override
+	public void setReason(String reason)
+	{
+		wrappedInfo.setReason(reason);
+	}
+
+	@Override
+	public String getElementName()
+	{
+		return "delay";
+	}
+
+	@Override
+	public String getNamespace()
+	{
+		return "urn:xmpp:delay";
+	}
+
+	@Override
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
+                "\"");
+        buf.append(" stamp=\"");
+        buf.append(StringUtils.formatXEP0082Date(getStamp()));
+        buf.append("\"");
+        if (getFrom() != null && getFrom().length() > 0) {
+            buf.append(" from=\"").append(getFrom()).append("\"");
+        }
+        buf.append(">");
+        if (getReason() != null && getReason().length() > 0) {
+            buf.append(getReason());
+        }
+        buf.append("</").append(getElementName()).append(">");
+        return buf.toString();
+    }
+	
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Header.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Header.java
index 3fa8386..44533f8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Header.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Header.java
@@ -1,59 +1,59 @@
-/*

- * 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.smackx.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * Represents a <b>Header</b> entry as specified by the <a href="http://xmpp.org/extensions/xep-031.html">Stanza Headers and Internet Metadata (SHIM)</a>

-

- * @author Robin Collier

- */

-public class Header implements PacketExtension

-{

-	private String name;

-	private String value;

-	

-	public Header(String name, String value)

-	{

-		this.name = name;

-		this.value = value;

-	}

-	

-	public String getName()

-	{

-		return name;

-	}

-

-	public String getValue()

-	{

-		return value;

-	}

-

-	public String getElementName()

-	{

-		return "header";

-	}

-

-	public String getNamespace()

-	{

-		return HeadersExtension.NAMESPACE;

-	}

-

-	public String toXML()

-	{

-		return "<header name='" + name + "'>" + value + "</header>";

-	}

-

-}

+/*
+ * 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.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Represents a <b>Header</b> entry as specified by the <a href="http://xmpp.org/extensions/xep-031.html">Stanza Headers and Internet Metadata (SHIM)</a>
+
+ * @author Robin Collier
+ */
+public class Header implements PacketExtension
+{
+	private String name;
+	private String value;
+	
+	public Header(String name, String value)
+	{
+		this.name = name;
+		this.value = value;
+	}
+	
+	public String getName()
+	{
+		return name;
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+
+	public String getElementName()
+	{
+		return "header";
+	}
+
+	public String getNamespace()
+	{
+		return HeadersExtension.NAMESPACE;
+	}
+
+	public String toXML()
+	{
+		return "<header name='" + name + "'>" + value + "</header>";
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/HeadersExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/HeadersExtension.java
index 78564db..03fde17 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/HeadersExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/HeadersExtension.java
@@ -1,69 +1,69 @@
-/**

- * 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.smackx.packet;

-

-import java.util.Collection;

-import java.util.Collections;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * Extension representing a list of headers as specified in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>

- * 

- * @see Header

- * 

- * @author Robin Collier

- */

-public class HeadersExtension implements PacketExtension

-{

-	public static final String NAMESPACE = "http://jabber.org/protocol/shim";

-	

-	private Collection<Header> headers = Collections.EMPTY_LIST;

-	

-	public HeadersExtension(Collection<Header> headerList)

-	{

-		if (headerList != null)

-			headers = headerList;

-	}

-	

-	public Collection<Header> getHeaders()

-	{

-		return headers;

-	}

-

-	public String getElementName()

-	{

-		return "headers";

-	}

-

-	public String getNamespace()

-	{

-		return NAMESPACE;

-	}

-

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<" + getElementName() + " xmlns='" + getNamespace() + "'>");

-		

-		for (Header header : headers)

-		{

-			builder.append(header.toXML());

-		}

-		builder.append("</" + getElementName() + '>');

-

-		return builder.toString();

-	}

-

-}

+/**
+ * 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.smackx.packet;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Extension representing a list of headers as specified in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>
+ * 
+ * @see Header
+ * 
+ * @author Robin Collier
+ */
+public class HeadersExtension implements PacketExtension
+{
+	public static final String NAMESPACE = "http://jabber.org/protocol/shim";
+	
+	private Collection<Header> headers = Collections.EMPTY_LIST;
+	
+	public HeadersExtension(Collection<Header> headerList)
+	{
+		if (headerList != null)
+			headers = headerList;
+	}
+	
+	public Collection<Header> getHeaders()
+	{
+		return headers;
+	}
+
+	public String getElementName()
+	{
+		return "headers";
+	}
+
+	public String getNamespace()
+	{
+		return NAMESPACE;
+	}
+
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<" + getElementName() + " xmlns='" + getNamespace() + "'>");
+		
+		for (Header header : headers)
+		{
+			builder.append(header.toXML());
+		}
+		builder.append("</" + getElementName() + '>');
+
+		return builder.toString();
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Nick.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Nick.java
index 9a64eaa..61d7912 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Nick.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/Nick.java
@@ -1,112 +1,112 @@
-/**

- * $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.smackx.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * A minimalistic implementation of a {@link PacketExtension} for nicknames.

- * 

- * @author Guus der Kinderen, guus.der.kinderen@gmail.com

- * @see <a href="http://xmpp.org/extensions/xep-0172.html">XEP-0172: User Nickname</a>

- */

-public class Nick implements PacketExtension {

-

-	public static final String NAMESPACE = "http://jabber.org/protocol/nick";

-

-	public static final String ELEMENT_NAME = "nick";

-

-	private String name = null;

-

-	public Nick(String name) {

-		this.name = name;

-	}

-

-	/**

-	 * The value of this nickname

-	 * 

-	 * @return the nickname

-	 */

-	public String getName() {

-		return name;

-	}

-

-	/**

-	 * Sets the value of this nickname

-	 * 

-	 * @param name

-	 *            the name to set

-	 */

-	public void setName(String name) {

-		this.name = name;

-	}

-

-	/*

-	 * (non-Javadoc)

-	 * 

-	 * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()

-	 */

-	public String getElementName() {

-		return ELEMENT_NAME;

-	}

-

-	/*

-	 * (non-Javadoc)

-	 * 

-	 * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()

-	 */

-	public String getNamespace() {

-		return NAMESPACE;

-	}

-

-	/*

-	 * (non-Javadoc)

-	 * 

-	 * @see org.jivesoftware.smack.packet.PacketExtension#toXML()

-	 */

-	public String toXML() {

-		final StringBuilder buf = new StringBuilder();

-

-		buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(

-				NAMESPACE).append("\">");

-		buf.append(getName());

-		buf.append("</").append(ELEMENT_NAME).append('>');

-

-		return buf.toString();

-	}

-

-	public static class Provider implements PacketExtensionProvider {

-

-		public PacketExtension parseExtension(XmlPullParser parser)

-				throws Exception {

-			

-            parser.next();

-			final String name = parser.getText();

-

-			// Advance to end of extension.

-			while(parser.getEventType() != XmlPullParser.END_TAG) {

-				parser.next();

-			}

-

-			return new Nick(name);

-		}

-	}

-}

+/**
+ * $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.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A minimalistic implementation of a {@link PacketExtension} for nicknames.
+ * 
+ * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+ * @see <a href="http://xmpp.org/extensions/xep-0172.html">XEP-0172: User Nickname</a>
+ */
+public class Nick implements PacketExtension {
+
+	public static final String NAMESPACE = "http://jabber.org/protocol/nick";
+
+	public static final String ELEMENT_NAME = "nick";
+
+	private String name = null;
+
+	public Nick(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * The value of this nickname
+	 * 
+	 * @return the nickname
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Sets the value of this nickname
+	 * 
+	 * @param name
+	 *            the name to set
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()
+	 */
+	public String getElementName() {
+		return ELEMENT_NAME;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()
+	 */
+	public String getNamespace() {
+		return NAMESPACE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jivesoftware.smack.packet.PacketExtension#toXML()
+	 */
+	public String toXML() {
+		final StringBuilder buf = new StringBuilder();
+
+		buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(
+				NAMESPACE).append("\">");
+		buf.append(getName());
+		buf.append("</").append(ELEMENT_NAME).append('>');
+
+		return buf.toString();
+	}
+
+	public static class Provider implements PacketExtensionProvider {
+
+		public PacketExtension parseExtension(XmlPullParser parser)
+				throws Exception {
+			
+            parser.next();
+			final String name = parser.getText();
+
+			// Advance to end of extension.
+			while(parser.getEventType() != XmlPullParser.END_TAG) {
+				parser.next();
+			}
+
+			return new Nick(name);
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java
index 59bd98e..f4f1c82 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java
@@ -17,76 +17,76 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * IQ packet used for discovering the user's shared groups and for getting the answer back

- * from the server.<p>

- *

- * Important note: This functionality is not part of the XMPP spec and it will only work

- * with Wildfire.

- *

- * @author Gaston Dombiak

- */

-public class SharedGroupsInfo extends IQ {

-

-    private List<String> groups = new ArrayList<String>();

-

-    /**

-     * Returns a collection with the shared group names returned from the server.

-     *

-     * @return collection with the shared group names returned from the server.

-     */

-    public List<String> getGroups() {

-        return groups;

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<sharedgroup xmlns=\"http://www.jivesoftware.org/protocol/sharedgroup\">");

-        for (Iterator<String> it=groups.iterator(); it.hasNext();) {

-            buf.append("<group>").append(it.next()).append("</group>");

-        }

-        buf.append("</sharedgroup>");

-        return buf.toString();

-    }

-

-    /**

-     * Internal Search service Provider.

-     */

-    public static class Provider implements IQProvider {

-

-        /**

-         * Provider Constructor.

-         */

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            SharedGroupsInfo groupsInfo = new SharedGroupsInfo();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG && parser.getName().equals("group")) {

-                    groupsInfo.getGroups().add(parser.nextText());

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals("sharedgroup")) {

-                        done = true;

-                    }

-                }

-            }

-            return groupsInfo;

-        }

-    }

-}

+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * IQ packet used for discovering the user's shared groups and for getting the answer back
+ * from the server.<p>
+ *
+ * Important note: This functionality is not part of the XMPP spec and it will only work
+ * with Wildfire.
+ *
+ * @author Gaston Dombiak
+ */
+public class SharedGroupsInfo extends IQ {
+
+    private List<String> groups = new ArrayList<String>();
+
+    /**
+     * Returns a collection with the shared group names returned from the server.
+     *
+     * @return collection with the shared group names returned from the server.
+     */
+    public List<String> getGroups() {
+        return groups;
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<sharedgroup xmlns=\"http://www.jivesoftware.org/protocol/sharedgroup\">");
+        for (Iterator<String> it=groups.iterator(); it.hasNext();) {
+            buf.append("<group>").append(it.next()).append("</group>");
+        }
+        buf.append("</sharedgroup>");
+        return buf.toString();
+    }
+
+    /**
+     * Internal Search service Provider.
+     */
+    public static class Provider implements IQProvider {
+
+        /**
+         * Provider Constructor.
+         */
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            SharedGroupsInfo groupsInfo = new SharedGroupsInfo();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG && parser.getName().equals("group")) {
+                    groupsInfo.getGroups().add(parser.nextText());
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals("sharedgroup")) {
+                        done = true;
+                    }
+                }
+            }
+            return groupsInfo;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/StreamInitiation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/StreamInitiation.java
index 7856c1b..e9b6681 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/StreamInitiation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/packet/StreamInitiation.java
@@ -1,419 +1,419 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.packet;

-

-import java.util.Date;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.util.StringUtils;

-

-/**

- * The process by which two entities initiate a stream.

- *

- * @author Alexander Wenckus

- */

-public class StreamInitiation extends IQ {

-

-    private String id;

-

-    private String mimeType;

-

-    private File file;

-

-    private Feature featureNegotiation;

-

-    /**

-     * The "id" attribute is an opaque identifier. This attribute MUST be

-     * present on type='set', and MUST be a valid string. This SHOULD NOT be

-     * sent back on type='result', since the <iq/> "id" attribute provides the

-     * only context needed. This value is generated by the Sender, and the same

-     * value MUST be used throughout a session when talking to the Receiver.

-     *

-     * @param id The "id" attribute.

-     */

-    public void setSesssionID(final String id) {

-        this.id = id;

-    }

-

-    /**

-     * Uniquely identifies a stream initiation to the recipient.

-     *

-     * @return The "id" attribute.

-     * @see #setSesssionID(String)

-     */

-    public String getSessionID() {

-        return id;

-    }

-

-    /**

-     * The "mime-type" attribute identifies the MIME-type for the data across

-     * the stream. This attribute MUST be a valid MIME-type as registered with

-     * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as

-     * listed at <http://www.iana.org/assignments/media-types>). During

-     * negotiation, this attribute SHOULD be present, and is otherwise not

-     * required. If not included during negotiation, its value is assumed to be

-     * "binary/octect-stream".

-     *

-     * @param mimeType The valid mime-type.

-     */

-    public void setMimeType(final String mimeType) {

-        this.mimeType = mimeType;

-    }

-

-    /**

-     * Identifies the type of file that is desired to be transfered.

-     *

-     * @return The mime-type.

-     * @see #setMimeType(String)

-     */

-    public String getMimeType() {

-        return mimeType;

-    }

-

-    /**

-     * Sets the file which contains the information pertaining to the file to be

-     * transfered.

-     *

-     * @param file The file identified by the stream initiator to be sent.

-     */

-    public void setFile(final File file) {

-        this.file = file;

-    }

-

-    /**

-     * Returns the file containing the information about the request.

-     *

-     * @return Returns the file containing the information about the request.

-     */

-    public File getFile() {

-        return file;

-    }

-

-    /**

-     * Sets the data form which contains the valid methods of stream neotiation

-     * and transfer.

-     *

-     * @param form The dataform containing the methods.

-     */

-    public void setFeatureNegotiationForm(final DataForm form) {

-        this.featureNegotiation = new Feature(form);

-    }

-

-    /**

-     * Returns the data form which contains the valid methods of stream

-     * neotiation and transfer.

-     *

-     * @return Returns the data form which contains the valid methods of stream

-     *         neotiation and transfer.

-     */

-    public DataForm getFeatureNegotiationForm() {

-        return featureNegotiation.getData();

-    }

-

-    /*

-      * (non-Javadoc)

-      *

-      * @see org.jivesoftware.smack.packet.IQ#getChildElementXML()

-      */

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        if (this.getType().equals(IQ.Type.SET)) {

-            buf.append("<si xmlns=\"http://jabber.org/protocol/si\" ");

-            if (getSessionID() != null) {

-                buf.append("id=\"").append(getSessionID()).append("\" ");

-            }

-            if (getMimeType() != null) {

-                buf.append("mime-type=\"").append(getMimeType()).append("\" ");

-            }

-            buf

-                    .append("profile=\"http://jabber.org/protocol/si/profile/file-transfer\">");

-

-            // Add the file section if there is one.

-            String fileXML = file.toXML();

-            if (fileXML != null) {

-                buf.append(fileXML);

-            }

-        }

-        else if (this.getType().equals(IQ.Type.RESULT)) {

-            buf.append("<si xmlns=\"http://jabber.org/protocol/si\">");

-        }

-        else {

-            throw new IllegalArgumentException("IQ Type not understood");

-        }

-        if (featureNegotiation != null) {

-            buf.append(featureNegotiation.toXML());

-        }

-        buf.append("</si>");

-        return buf.toString();

-    }

-

-    /**

-     * <ul>

-     * <li>size: The size, in bytes, of the data to be sent.</li>

-     * <li>name: The name of the file that the Sender wishes to send.</li>

-     * <li>date: The last modification time of the file. This is specified

-     * using the DateTime profile as described in Jabber Date and Time Profiles.</li>

-     * <li>hash: The MD5 sum of the file contents.</li>

-     * </ul>

-     * <p/>

-     * <p/>

-     * &lt;desc&gt; is used to provide a sender-generated description of the

-     * file so the receiver can better understand what is being sent. It MUST

-     * NOT be sent in the result.

-     * <p/>

-     * <p/>

-     * When &lt;range&gt; is sent in the offer, it should have no attributes.

-     * This signifies that the sender can do ranged transfers. When a Stream

-     * Initiation result is sent with the <range> element, it uses these

-     * attributes:

-     * <p/>

-     * <ul>

-     * <li>offset: Specifies the position, in bytes, to start transferring the

-     * file data from. This defaults to zero (0) if not specified.</li>

-     * <li>length - Specifies the number of bytes to retrieve starting at

-     * offset. This defaults to the length of the file from offset to the end.</li>

-     * </ul>

-     * <p/>

-     * <p/>

-     * Both attributes are OPTIONAL on the &lt;range&gt; element. Sending no

-     * attributes is synonymous with not sending the &lt;range&gt; element. When

-     * no &lt;range&gt; element is sent in the Stream Initiation result, the

-     * Sender MUST send the complete file starting at offset 0. More generally,

-     * data is sent over the stream byte for byte starting at the offset

-     * position for the length specified.

-     *

-     * @author Alexander Wenckus

-     */

-    public static class File implements PacketExtension {

-

-        private final String name;

-

-        private final long size;

-

-        private String hash;

-

-        private Date date;

-

-        private String desc;

-

-        private boolean isRanged;

-

-        /**

-         * Constructor providing the name of the file and its size.

-         *

-         * @param name The name of the file.

-         * @param size The size of the file in bytes.

-         */

-        public File(final String name, final long size) {

-            if (name == null) {

-                throw new NullPointerException("name cannot be null");

-            }

-

-            this.name = name;

-            this.size = size;

-        }

-

-        /**

-         * Returns the file's name.

-         *

-         * @return Returns the file's name.

-         */

-        public String getName() {

-            return name;

-        }

-

-        /**

-         * Returns the file's size.

-         *

-         * @return Returns the file's size.

-         */

-        public long getSize() {

-            return size;

-        }

-

-        /**

-         * Sets the MD5 sum of the file's contents

-         *

-         * @param hash The MD5 sum of the file's contents.

-         */

-        public void setHash(final String hash) {

-            this.hash = hash;

-        }

-

-        /**

-         * Returns the MD5 sum of the file's contents

-         *

-         * @return Returns the MD5 sum of the file's contents

-         */

-        public String getHash() {

-            return hash;

-        }

-

-        /**

-         * Sets the date that the file was last modified.

-         *

-         * @param date The date that the file was last modified.

-         */

-        public void setDate(Date date) {

-            this.date = date;

-        }

-

-        /**

-         * Returns the date that the file was last modified.

-         *

-         * @return Returns the date that the file was last modified.

-         */

-        public Date getDate() {

-            return date;

-        }

-

-        /**

-         * Sets the description of the file.

-         *

-         * @param desc The description of the file so that the file reciever can

-         *             know what file it is.

-         */

-        public void setDesc(final String desc) {

-            this.desc = desc;

-        }

-

-        /**

-         * Returns the description of the file.

-         *

-         * @return Returns the description of the file.

-         */

-        public String getDesc() {

-            return desc;

-        }

-

-        /**

-         * True if a range can be provided and false if it cannot.

-         *

-         * @param isRanged True if a range can be provided and false if it cannot.

-         */

-        public void setRanged(final boolean isRanged) {

-            this.isRanged = isRanged;

-        }

-

-        /**

-         * Returns whether or not the initiator can support a range for the file

-         * tranfer.

-         *

-         * @return Returns whether or not the initiator can support a range for

-         *         the file tranfer.

-         */

-        public boolean isRanged() {

-            return isRanged;

-        }

-

-        public String getElementName() {

-            return "file";

-        }

-

-        public String getNamespace() {

-            return "http://jabber.org/protocol/si/profile/file-transfer";

-        }

-

-        public String toXML() {

-            StringBuilder buffer = new StringBuilder();

-

-            buffer.append("<").append(getElementName()).append(" xmlns=\"")

-                    .append(getNamespace()).append("\" ");

-

-            if (getName() != null) {

-                buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");

-            }

-

-            if (getSize() > 0) {

-                buffer.append("size=\"").append(getSize()).append("\" ");

-            }

-

-            if (getDate() != null) {

-                buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");

-            }

-

-            if (getHash() != null) {

-                buffer.append("hash=\"").append(getHash()).append("\" ");

-            }

-

-            if ((desc != null && desc.length() > 0) || isRanged) {

-                buffer.append(">");

-                if (getDesc() != null && desc.length() > 0) {

-                    buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>");

-                }

-                if (isRanged()) {

-                    buffer.append("<range/>");

-                }

-                buffer.append("</").append(getElementName()).append(">");

-            }

-            else {

-                buffer.append("/>");

-            }

-            return buffer.toString();

-        }

-    }

-

-    /**

-     * The feature negotiation portion of the StreamInitiation packet.

-     *

-     * @author Alexander Wenckus

-     *

-     */

-    public class Feature implements PacketExtension {

-

-        private final DataForm data;

-

-        /**

-         * The dataform can be provided as part of the constructor.

-         *

-         * @param data The dataform.

-         */

-        public Feature(final DataForm data) {

-            this.data = data;

-        }

-

-        /**

-         * Returns the dataform associated with the feature negotiation.

-         *

-         * @return Returns the dataform associated with the feature negotiation.

-         */

-        public DataForm getData() {

-            return data;

-        }

-

-        public String getNamespace() {

-            return "http://jabber.org/protocol/feature-neg";

-        }

-

-        public String getElementName() {

-            return "feature";

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-            buf

-                    .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");

-			buf.append(data.toXML());

-			buf.append("</feature>");

-			return buf.toString();

-		}

-	}

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.packet;
+
+import java.util.Date;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * The process by which two entities initiate a stream.
+ *
+ * @author Alexander Wenckus
+ */
+public class StreamInitiation extends IQ {
+
+    private String id;
+
+    private String mimeType;
+
+    private File file;
+
+    private Feature featureNegotiation;
+
+    /**
+     * The "id" attribute is an opaque identifier. This attribute MUST be
+     * present on type='set', and MUST be a valid string. This SHOULD NOT be
+     * sent back on type='result', since the <iq/> "id" attribute provides the
+     * only context needed. This value is generated by the Sender, and the same
+     * value MUST be used throughout a session when talking to the Receiver.
+     *
+     * @param id The "id" attribute.
+     */
+    public void setSesssionID(final String id) {
+        this.id = id;
+    }
+
+    /**
+     * Uniquely identifies a stream initiation to the recipient.
+     *
+     * @return The "id" attribute.
+     * @see #setSesssionID(String)
+     */
+    public String getSessionID() {
+        return id;
+    }
+
+    /**
+     * The "mime-type" attribute identifies the MIME-type for the data across
+     * the stream. This attribute MUST be a valid MIME-type as registered with
+     * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as
+     * listed at <http://www.iana.org/assignments/media-types>). During
+     * negotiation, this attribute SHOULD be present, and is otherwise not
+     * required. If not included during negotiation, its value is assumed to be
+     * "binary/octect-stream".
+     *
+     * @param mimeType The valid mime-type.
+     */
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
+    /**
+     * Identifies the type of file that is desired to be transfered.
+     *
+     * @return The mime-type.
+     * @see #setMimeType(String)
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    /**
+     * Sets the file which contains the information pertaining to the file to be
+     * transfered.
+     *
+     * @param file The file identified by the stream initiator to be sent.
+     */
+    public void setFile(final File file) {
+        this.file = file;
+    }
+
+    /**
+     * Returns the file containing the information about the request.
+     *
+     * @return Returns the file containing the information about the request.
+     */
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * Sets the data form which contains the valid methods of stream neotiation
+     * and transfer.
+     *
+     * @param form The dataform containing the methods.
+     */
+    public void setFeatureNegotiationForm(final DataForm form) {
+        this.featureNegotiation = new Feature(form);
+    }
+
+    /**
+     * Returns the data form which contains the valid methods of stream
+     * neotiation and transfer.
+     *
+     * @return Returns the data form which contains the valid methods of stream
+     *         neotiation and transfer.
+     */
+    public DataForm getFeatureNegotiationForm() {
+        return featureNegotiation.getData();
+    }
+
+    /*
+      * (non-Javadoc)
+      *
+      * @see org.jivesoftware.smack.packet.IQ#getChildElementXML()
+      */
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        if (this.getType().equals(IQ.Type.SET)) {
+            buf.append("<si xmlns=\"http://jabber.org/protocol/si\" ");
+            if (getSessionID() != null) {
+                buf.append("id=\"").append(getSessionID()).append("\" ");
+            }
+            if (getMimeType() != null) {
+                buf.append("mime-type=\"").append(getMimeType()).append("\" ");
+            }
+            buf
+                    .append("profile=\"http://jabber.org/protocol/si/profile/file-transfer\">");
+
+            // Add the file section if there is one.
+            String fileXML = file.toXML();
+            if (fileXML != null) {
+                buf.append(fileXML);
+            }
+        }
+        else if (this.getType().equals(IQ.Type.RESULT)) {
+            buf.append("<si xmlns=\"http://jabber.org/protocol/si\">");
+        }
+        else {
+            throw new IllegalArgumentException("IQ Type not understood");
+        }
+        if (featureNegotiation != null) {
+            buf.append(featureNegotiation.toXML());
+        }
+        buf.append("</si>");
+        return buf.toString();
+    }
+
+    /**
+     * <ul>
+     * <li>size: The size, in bytes, of the data to be sent.</li>
+     * <li>name: The name of the file that the Sender wishes to send.</li>
+     * <li>date: The last modification time of the file. This is specified
+     * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
+     * <li>hash: The MD5 sum of the file contents.</li>
+     * </ul>
+     * <p/>
+     * <p/>
+     * &lt;desc&gt; is used to provide a sender-generated description of the
+     * file so the receiver can better understand what is being sent. It MUST
+     * NOT be sent in the result.
+     * <p/>
+     * <p/>
+     * When &lt;range&gt; is sent in the offer, it should have no attributes.
+     * This signifies that the sender can do ranged transfers. When a Stream
+     * Initiation result is sent with the <range> element, it uses these
+     * attributes:
+     * <p/>
+     * <ul>
+     * <li>offset: Specifies the position, in bytes, to start transferring the
+     * file data from. This defaults to zero (0) if not specified.</li>
+     * <li>length - Specifies the number of bytes to retrieve starting at
+     * offset. This defaults to the length of the file from offset to the end.</li>
+     * </ul>
+     * <p/>
+     * <p/>
+     * Both attributes are OPTIONAL on the &lt;range&gt; element. Sending no
+     * attributes is synonymous with not sending the &lt;range&gt; element. When
+     * no &lt;range&gt; element is sent in the Stream Initiation result, the
+     * Sender MUST send the complete file starting at offset 0. More generally,
+     * data is sent over the stream byte for byte starting at the offset
+     * position for the length specified.
+     *
+     * @author Alexander Wenckus
+     */
+    public static class File implements PacketExtension {
+
+        private final String name;
+
+        private final long size;
+
+        private String hash;
+
+        private Date date;
+
+        private String desc;
+
+        private boolean isRanged;
+
+        /**
+         * Constructor providing the name of the file and its size.
+         *
+         * @param name The name of the file.
+         * @param size The size of the file in bytes.
+         */
+        public File(final String name, final long size) {
+            if (name == null) {
+                throw new NullPointerException("name cannot be null");
+            }
+
+            this.name = name;
+            this.size = size;
+        }
+
+        /**
+         * Returns the file's name.
+         *
+         * @return Returns the file's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Returns the file's size.
+         *
+         * @return Returns the file's size.
+         */
+        public long getSize() {
+            return size;
+        }
+
+        /**
+         * Sets the MD5 sum of the file's contents
+         *
+         * @param hash The MD5 sum of the file's contents.
+         */
+        public void setHash(final String hash) {
+            this.hash = hash;
+        }
+
+        /**
+         * Returns the MD5 sum of the file's contents
+         *
+         * @return Returns the MD5 sum of the file's contents
+         */
+        public String getHash() {
+            return hash;
+        }
+
+        /**
+         * Sets the date that the file was last modified.
+         *
+         * @param date The date that the file was last modified.
+         */
+        public void setDate(Date date) {
+            this.date = date;
+        }
+
+        /**
+         * Returns the date that the file was last modified.
+         *
+         * @return Returns the date that the file was last modified.
+         */
+        public Date getDate() {
+            return date;
+        }
+
+        /**
+         * Sets the description of the file.
+         *
+         * @param desc The description of the file so that the file reciever can
+         *             know what file it is.
+         */
+        public void setDesc(final String desc) {
+            this.desc = desc;
+        }
+
+        /**
+         * Returns the description of the file.
+         *
+         * @return Returns the description of the file.
+         */
+        public String getDesc() {
+            return desc;
+        }
+
+        /**
+         * True if a range can be provided and false if it cannot.
+         *
+         * @param isRanged True if a range can be provided and false if it cannot.
+         */
+        public void setRanged(final boolean isRanged) {
+            this.isRanged = isRanged;
+        }
+
+        /**
+         * Returns whether or not the initiator can support a range for the file
+         * tranfer.
+         *
+         * @return Returns whether or not the initiator can support a range for
+         *         the file tranfer.
+         */
+        public boolean isRanged() {
+            return isRanged;
+        }
+
+        public String getElementName() {
+            return "file";
+        }
+
+        public String getNamespace() {
+            return "http://jabber.org/protocol/si/profile/file-transfer";
+        }
+
+        public String toXML() {
+            StringBuilder buffer = new StringBuilder();
+
+            buffer.append("<").append(getElementName()).append(" xmlns=\"")
+                    .append(getNamespace()).append("\" ");
+
+            if (getName() != null) {
+                buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
+            }
+
+            if (getSize() > 0) {
+                buffer.append("size=\"").append(getSize()).append("\" ");
+            }
+
+            if (getDate() != null) {
+                buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
+            }
+
+            if (getHash() != null) {
+                buffer.append("hash=\"").append(getHash()).append("\" ");
+            }
+
+            if ((desc != null && desc.length() > 0) || isRanged) {
+                buffer.append(">");
+                if (getDesc() != null && desc.length() > 0) {
+                    buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>");
+                }
+                if (isRanged()) {
+                    buffer.append("<range/>");
+                }
+                buffer.append("</").append(getElementName()).append(">");
+            }
+            else {
+                buffer.append("/>");
+            }
+            return buffer.toString();
+        }
+    }
+
+    /**
+     * The feature negotiation portion of the StreamInitiation packet.
+     *
+     * @author Alexander Wenckus
+     *
+     */
+    public class Feature implements PacketExtension {
+
+        private final DataForm data;
+
+        /**
+         * The dataform can be provided as part of the constructor.
+         *
+         * @param data The dataform.
+         */
+        public Feature(final DataForm data) {
+            this.data = data;
+        }
+
+        /**
+         * Returns the dataform associated with the feature negotiation.
+         *
+         * @return Returns the dataform associated with the feature negotiation.
+         */
+        public DataForm getData() {
+            return data;
+        }
+
+        public String getNamespace() {
+            return "http://jabber.org/protocol/feature-neg";
+        }
+
+        public String getElementName() {
+            return "feature";
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+            buf
+                    .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");
+			buf.append(data.toXML());
+			buf.append("</feature>");
+			return buf.toString();
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/AdHocCommandDataProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/AdHocCommandDataProvider.java
index 63d24ec..3bc445e 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/AdHocCommandDataProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/AdHocCommandDataProvider.java
@@ -1,155 +1,155 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2005-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.smackx.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.packet.XMPPError;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.jivesoftware.smackx.commands.AdHocCommand;

-import org.jivesoftware.smackx.commands.AdHocCommand.Action;

-import org.jivesoftware.smackx.commands.AdHocCommandNote;

-import org.jivesoftware.smackx.packet.AdHocCommandData;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * The AdHocCommandDataProvider parses AdHocCommandData packets.

- * 

- * @author Gabriel Guardincerri

- */

-public class AdHocCommandDataProvider implements IQProvider {

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        boolean done = false;

-        AdHocCommandData adHocCommandData = new AdHocCommandData();

-        DataFormProvider dataFormProvider = new DataFormProvider();

-

-        int eventType;

-        String elementName;

-        String namespace;

-        adHocCommandData.setSessionID(parser.getAttributeValue("", "sessionid"));

-        adHocCommandData.setNode(parser.getAttributeValue("", "node"));

-

-        // Status

-        String status = parser.getAttributeValue("", "status");

-        if (AdHocCommand.Status.executing.toString().equalsIgnoreCase(status)) {

-            adHocCommandData.setStatus(AdHocCommand.Status.executing);

-        }

-        else if (AdHocCommand.Status.completed.toString().equalsIgnoreCase(status)) {

-            adHocCommandData.setStatus(AdHocCommand.Status.completed);

-        }

-        else if (AdHocCommand.Status.canceled.toString().equalsIgnoreCase(status)) {

-            adHocCommandData.setStatus(AdHocCommand.Status.canceled);

-        }

-

-        // Action

-        String action = parser.getAttributeValue("", "action");

-        if (action != null) {

-            Action realAction = AdHocCommand.Action.valueOf(action);

-            if (realAction == null || realAction.equals(Action.unknown)) {

-                adHocCommandData.setAction(Action.unknown);

-            }

-            else {

-                adHocCommandData.setAction(realAction);

-            }

-        }

-        while (!done) {

-            eventType = parser.next();

-            elementName = parser.getName();

-            namespace = parser.getNamespace();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("actions")) {

-                    String execute = parser.getAttributeValue("", "execute");

-                    if (execute != null) {

-                        adHocCommandData.setExecuteAction(AdHocCommand.Action.valueOf(execute));

-                    }

-                }

-                else if (parser.getName().equals("next")) {

-                    adHocCommandData.addAction(AdHocCommand.Action.next);

-                }

-                else if (parser.getName().equals("complete")) {

-                    adHocCommandData.addAction(AdHocCommand.Action.complete);

-                }

-                else if (parser.getName().equals("prev")) {

-                    adHocCommandData.addAction(AdHocCommand.Action.prev);

-                }

-                else if (elementName.equals("x") && namespace.equals("jabber:x:data")) {

-                    adHocCommandData.setForm((DataForm) dataFormProvider.parseExtension(parser));

-                }

-                else if (parser.getName().equals("note")) {

-                    AdHocCommandNote.Type type = AdHocCommandNote.Type.valueOf(

-                            parser.getAttributeValue("", "type"));

-                    String value = parser.nextText();

-                    adHocCommandData.addNote(new AdHocCommandNote(type, value));

-                }

-                else if (parser.getName().equals("error")) {

-                    XMPPError error = PacketParserUtils.parseError(parser);

-                    adHocCommandData.setError(error);

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("command")) {

-                    done = true;

-                }

-            }

-        }

-        return adHocCommandData;

-    }

-

-    public static class BadActionError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badAction);

-        }

-    }

-

-    public static class MalformedActionError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.malformedAction);

-        }

-    }

-

-    public static class BadLocaleError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badLocale);

-        }

-    }

-

-    public static class BadPayloadError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badPayload);

-        }

-    }

-

-    public static class BadSessionIDError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badSessionid);

-        }

-    }

-

-    public static class SessionExpiredError implements PacketExtensionProvider {

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.sessionExpired);

-        }

-    }

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-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.smackx.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.commands.AdHocCommand;
+import org.jivesoftware.smackx.commands.AdHocCommand.Action;
+import org.jivesoftware.smackx.commands.AdHocCommandNote;
+import org.jivesoftware.smackx.packet.AdHocCommandData;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * The AdHocCommandDataProvider parses AdHocCommandData packets.
+ * 
+ * @author Gabriel Guardincerri
+ */
+public class AdHocCommandDataProvider implements IQProvider {
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        boolean done = false;
+        AdHocCommandData adHocCommandData = new AdHocCommandData();
+        DataFormProvider dataFormProvider = new DataFormProvider();
+
+        int eventType;
+        String elementName;
+        String namespace;
+        adHocCommandData.setSessionID(parser.getAttributeValue("", "sessionid"));
+        adHocCommandData.setNode(parser.getAttributeValue("", "node"));
+
+        // Status
+        String status = parser.getAttributeValue("", "status");
+        if (AdHocCommand.Status.executing.toString().equalsIgnoreCase(status)) {
+            adHocCommandData.setStatus(AdHocCommand.Status.executing);
+        }
+        else if (AdHocCommand.Status.completed.toString().equalsIgnoreCase(status)) {
+            adHocCommandData.setStatus(AdHocCommand.Status.completed);
+        }
+        else if (AdHocCommand.Status.canceled.toString().equalsIgnoreCase(status)) {
+            adHocCommandData.setStatus(AdHocCommand.Status.canceled);
+        }
+
+        // Action
+        String action = parser.getAttributeValue("", "action");
+        if (action != null) {
+            Action realAction = AdHocCommand.Action.valueOf(action);
+            if (realAction == null || realAction.equals(Action.unknown)) {
+                adHocCommandData.setAction(Action.unknown);
+            }
+            else {
+                adHocCommandData.setAction(realAction);
+            }
+        }
+        while (!done) {
+            eventType = parser.next();
+            elementName = parser.getName();
+            namespace = parser.getNamespace();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("actions")) {
+                    String execute = parser.getAttributeValue("", "execute");
+                    if (execute != null) {
+                        adHocCommandData.setExecuteAction(AdHocCommand.Action.valueOf(execute));
+                    }
+                }
+                else if (parser.getName().equals("next")) {
+                    adHocCommandData.addAction(AdHocCommand.Action.next);
+                }
+                else if (parser.getName().equals("complete")) {
+                    adHocCommandData.addAction(AdHocCommand.Action.complete);
+                }
+                else if (parser.getName().equals("prev")) {
+                    adHocCommandData.addAction(AdHocCommand.Action.prev);
+                }
+                else if (elementName.equals("x") && namespace.equals("jabber:x:data")) {
+                    adHocCommandData.setForm((DataForm) dataFormProvider.parseExtension(parser));
+                }
+                else if (parser.getName().equals("note")) {
+                    AdHocCommandNote.Type type = AdHocCommandNote.Type.valueOf(
+                            parser.getAttributeValue("", "type"));
+                    String value = parser.nextText();
+                    adHocCommandData.addNote(new AdHocCommandNote(type, value));
+                }
+                else if (parser.getName().equals("error")) {
+                    XMPPError error = PacketParserUtils.parseError(parser);
+                    adHocCommandData.setError(error);
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("command")) {
+                    done = true;
+                }
+            }
+        }
+        return adHocCommandData;
+    }
+
+    public static class BadActionError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badAction);
+        }
+    }
+
+    public static class MalformedActionError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.malformedAction);
+        }
+    }
+
+    public static class BadLocaleError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badLocale);
+        }
+    }
+
+    public static class BadPayloadError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badPayload);
+        }
+    }
+
+    public static class BadSessionIDError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.badSessionid);
+        }
+    }
+
+    public static class SessionExpiredError implements PacketExtensionProvider {
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            return new AdHocCommandData.SpecificError(AdHocCommand.SpecificErrorCondition.sessionExpired);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/DelayInfoProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/DelayInfoProvider.java
index 9d1dcd8..6fa52b7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/DelayInfoProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/DelayInfoProvider.java
@@ -1,42 +1,42 @@
-/**

- * 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.smackx.provider;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.packet.DelayInfo;

-import org.jivesoftware.smackx.packet.DelayInformation;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * This provider simply creates a {@link DelayInfo} decorator for the {@link DelayInformation} that

- * is returned by the superclass.  This allows the new code using

- * <a href="http://xmpp.org/extensions/xep-0203.html">Delay Information XEP-0203</a> to be

- * backward compatible with <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.  

- * 

- * <p>This provider must be registered in the <b>smack.properties</b> file for the element 

- * <b>delay</b> with namespace <b>urn:xmpp:delay</b></p>

- *  

- * @author Robin Collier

- */

-public class DelayInfoProvider extends DelayInformationProvider

-{

-

-	@Override

-	public PacketExtension parseExtension(XmlPullParser parser) throws Exception

-	{

-		return new DelayInfo((DelayInformation)super.parseExtension(parser));

-	}

-

-}

+/**
+ * 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.smackx.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.packet.DelayInfo;
+import org.jivesoftware.smackx.packet.DelayInformation;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * This provider simply creates a {@link DelayInfo} decorator for the {@link DelayInformation} that
+ * is returned by the superclass.  This allows the new code using
+ * <a href="http://xmpp.org/extensions/xep-0203.html">Delay Information XEP-0203</a> to be
+ * backward compatible with <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>.  
+ * 
+ * <p>This provider must be registered in the <b>smack.properties</b> file for the element 
+ * <b>delay</b> with namespace <b>urn:xmpp:delay</b></p>
+ *  
+ * @author Robin Collier
+ */
+public class DelayInfoProvider extends DelayInformationProvider
+{
+
+	@Override
+	public PacketExtension parseExtension(XmlPullParser parser) throws Exception
+	{
+		return new DelayInfo((DelayInformation)super.parseExtension(parser));
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/EmbeddedExtensionProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/EmbeddedExtensionProvider.java
index 3d5ceb4..55c6726 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/EmbeddedExtensionProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/EmbeddedExtensionProvider.java
@@ -1,111 +1,111 @@
-/**

- * 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.smackx.provider;

-

-import java.util.ArrayList;

-import java.util.HashMap;

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.jivesoftware.smackx.pubsub.provider.ItemProvider;

-import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * 

- * This class simplifies parsing of embedded elements by using the 

- * <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template Method Pattern</a>.  

- * After extracting the current element attributes and content of any child elements, the template method 

- * ({@link #createReturnExtension(String, String, Map, List)} is called.  Subclasses

- * then override this method to create the specific return type.

- * 

- * <p>To use this class, you simply register your subclasses as extension providers in the 

- * <b>smack.properties</b> file.  Then they will be automatically picked up and used to parse

- * any child elements.  

- * 

- * <pre>

- * For example, given the following message

- * 

- * &lt;message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo&gt;

- *    &lt;event xmlns='http://jabber.org/protocol/pubsub#event&gt;

- *       &lt;items node='princely_musings'&gt;

- *          &lt;item id='asdjkwei3i34234n356'&gt;

- *             &lt;entry xmlns='http://www.w3.org/2005/Atom'&gt;

- *                &lt;title&gt;Soliloquy&lt;/title&gt;

- *                &lt;link rel='alternative' type='text/html'/&gt;

- *                &lt;id>tag:denmark.lit,2003:entry-32397&lt;/id&gt;

- *             &lt;/entry&gt;

- *          &lt;/item&gt;

- *       &lt;/items&gt;

- *    &lt;/event&gt;

- * &lt;/message&gt;

- * 

- * I would have a classes

- * {@link ItemsProvider} extends {@link EmbeddedExtensionProvider}

- * {@link ItemProvider} extends {@link EmbeddedExtensionProvider}

- * and

- * AtomProvider extends {@link PacketExtensionProvider}

- * 

- * These classes are then registered in the meta-inf/smack.providers file

- * as follows.

- * 

- *   &lt;extensionProvider&gt;

- *      &lt;elementName&gt;items&lt;/elementName&gt;

- *      &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;

- *      &lt;className&gt;org.jivesoftware.smackx.provider.ItemsEventProvider&lt;/className&gt;

- *   &lt;/extensionProvider&gt;

- *   &lt;extensionProvider&gt;

- *       &lt;elementName&gt;item&lt;/elementName&gt;

- *       &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;

- *       &lt;className&gt;org.jivesoftware.smackx.provider.ItemProvider&lt;/className&gt;

- *   &lt;/extensionProvider&gt;

- * 

- * </pre>

- * 

- * @author Robin Collier

- * 

- * @deprecated This has been moved to {@link org.jivesoftware.smack.provider.EmbeddedExtensionProvider}

- */

-abstract public class EmbeddedExtensionProvider implements PacketExtensionProvider

-{

-

-	final public PacketExtension parseExtension(XmlPullParser parser) throws Exception

-	{

-        String namespace = parser.getNamespace();

-        String name = parser.getName();

-        Map<String, String> attMap = new HashMap<String, String>();

-        

-        for(int i=0; i<parser.getAttributeCount(); i++)

-        {

-        	attMap.put(parser.getAttributeName(i), parser.getAttributeValue(i));

-        }

-        List<PacketExtension> extensions = new ArrayList<PacketExtension>();

-        

-        do

-        {

-            int tag = parser.next();

-

-            if (tag == XmlPullParser.START_TAG) 

-            	extensions.add(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));

-        } while (!name.equals(parser.getName()));

-

-		return createReturnExtension(name, namespace, attMap, extensions);

-	}

-

-	abstract protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content);

-}

+/**
+ * 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.smackx.provider;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
+import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * 
+ * This class simplifies parsing of embedded elements by using the 
+ * <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template Method Pattern</a>.  
+ * After extracting the current element attributes and content of any child elements, the template method 
+ * ({@link #createReturnExtension(String, String, Map, List)} is called.  Subclasses
+ * then override this method to create the specific return type.
+ * 
+ * <p>To use this class, you simply register your subclasses as extension providers in the 
+ * <b>smack.properties</b> file.  Then they will be automatically picked up and used to parse
+ * any child elements.  
+ * 
+ * <pre>
+ * For example, given the following message
+ * 
+ * &lt;message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo&gt;
+ *    &lt;event xmlns='http://jabber.org/protocol/pubsub#event&gt;
+ *       &lt;items node='princely_musings'&gt;
+ *          &lt;item id='asdjkwei3i34234n356'&gt;
+ *             &lt;entry xmlns='http://www.w3.org/2005/Atom'&gt;
+ *                &lt;title&gt;Soliloquy&lt;/title&gt;
+ *                &lt;link rel='alternative' type='text/html'/&gt;
+ *                &lt;id>tag:denmark.lit,2003:entry-32397&lt;/id&gt;
+ *             &lt;/entry&gt;
+ *          &lt;/item&gt;
+ *       &lt;/items&gt;
+ *    &lt;/event&gt;
+ * &lt;/message&gt;
+ * 
+ * I would have a classes
+ * {@link ItemsProvider} extends {@link EmbeddedExtensionProvider}
+ * {@link ItemProvider} extends {@link EmbeddedExtensionProvider}
+ * and
+ * AtomProvider extends {@link PacketExtensionProvider}
+ * 
+ * These classes are then registered in the meta-inf/smack.providers file
+ * as follows.
+ * 
+ *   &lt;extensionProvider&gt;
+ *      &lt;elementName&gt;items&lt;/elementName&gt;
+ *      &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;
+ *      &lt;className&gt;org.jivesoftware.smackx.provider.ItemsEventProvider&lt;/className&gt;
+ *   &lt;/extensionProvider&gt;
+ *   &lt;extensionProvider&gt;
+ *       &lt;elementName&gt;item&lt;/elementName&gt;
+ *       &lt;namespace&gt;http://jabber.org/protocol/pubsub#event&lt;/namespace&gt;
+ *       &lt;className&gt;org.jivesoftware.smackx.provider.ItemProvider&lt;/className&gt;
+ *   &lt;/extensionProvider&gt;
+ * 
+ * </pre>
+ * 
+ * @author Robin Collier
+ * 
+ * @deprecated This has been moved to {@link org.jivesoftware.smack.provider.EmbeddedExtensionProvider}
+ */
+abstract public class EmbeddedExtensionProvider implements PacketExtensionProvider
+{
+
+	final public PacketExtension parseExtension(XmlPullParser parser) throws Exception
+	{
+        String namespace = parser.getNamespace();
+        String name = parser.getName();
+        Map<String, String> attMap = new HashMap<String, String>();
+        
+        for(int i=0; i<parser.getAttributeCount(); i++)
+        {
+        	attMap.put(parser.getAttributeName(i), parser.getAttributeValue(i));
+        }
+        List<PacketExtension> extensions = new ArrayList<PacketExtension>();
+        
+        do
+        {
+            int tag = parser.next();
+
+            if (tag == XmlPullParser.START_TAG) 
+            	extensions.add(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));
+        } while (!name.equals(parser.getName()));
+
+		return createReturnExtension(name, namespace, attMap, extensions);
+	}
+
+	abstract protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeaderProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeaderProvider.java
index 7344880..b6e55a3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeaderProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeaderProvider.java
@@ -1,44 +1,44 @@
-/**

- * 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.smackx.provider;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smackx.packet.Header;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses the header element as defined in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>.

- * 

- * @author Robin Collier

- */

-public class HeaderProvider implements PacketExtensionProvider

-{

-	public PacketExtension parseExtension(XmlPullParser parser) throws Exception

-	{

-		String name = parser.getAttributeValue(null, "name");

-		String value = null;

-		

-		parser.next();

-		

-		if (parser.getEventType() == XmlPullParser.TEXT)

-			value = parser.getText();

-		

-		while(parser.getEventType() != XmlPullParser.END_TAG)

-			parser.next();

-		

-		return new Header(name, value);

-	}

-

-}

+/**
+ * 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.smackx.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smackx.packet.Header;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses the header element as defined in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>.
+ * 
+ * @author Robin Collier
+ */
+public class HeaderProvider implements PacketExtensionProvider
+{
+	public PacketExtension parseExtension(XmlPullParser parser) throws Exception
+	{
+		String name = parser.getAttributeValue(null, "name");
+		String value = null;
+		
+		parser.next();
+		
+		if (parser.getEventType() == XmlPullParser.TEXT)
+			value = parser.getText();
+		
+		while(parser.getEventType() != XmlPullParser.END_TAG)
+			parser.next();
+		
+		return new Header(name, value);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeadersProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeadersProvider.java
index 056dd58..0c3158d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeadersProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/HeadersProvider.java
@@ -1,37 +1,37 @@
-/**

- * 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.smackx.provider;

-

-import java.util.Collection;

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.packet.Header;

-import org.jivesoftware.smackx.packet.HeadersExtension;

-

-/**

- * Parses the headers element as defined in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>.

- * 

- * @author Robin Collier

- */

-public class HeadersProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-		return new HeadersExtension((Collection<Header>)content);

-	}

-

-}

+/**
+ * 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.smackx.provider;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.packet.Header;
+import org.jivesoftware.smackx.packet.HeadersExtension;
+
+/**
+ * Parses the headers element as defined in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>.
+ * 
+ * @author Robin Collier
+ */
+public class HeadersProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+		return new HeadersExtension((Collection<Header>)content);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/StreamInitiationProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
index 11ab339..d599811 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
@@ -1,124 +1,124 @@
-/**

- * $RCSfile$

- * $Revision$

- * $Date$

- *

- * Copyright 2003-2006 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.smackx.provider;

-

-import java.text.ParseException;

-import java.util.Date;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.packet.StreamInitiation;

-import org.jivesoftware.smackx.packet.StreamInitiation.File;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * The StreamInitiationProvider parses StreamInitiation packets.

- * 

- * @author Alexander Wenckus

- * 

- */

-public class StreamInitiationProvider implements IQProvider {

-

-	public IQ parseIQ(final XmlPullParser parser) throws Exception {

-		boolean done = false;

-

-		// si

-		String id = parser.getAttributeValue("", "id");

-		String mimeType = parser.getAttributeValue("", "mime-type");

-

-		StreamInitiation initiation = new StreamInitiation();

-

-		// file

-		String name = null;

-		String size = null;

-		String hash = null;

-		String date = null;

-		String desc = null;

-		boolean isRanged = false;

-

-		// feature

-		DataForm form = null;

-		DataFormProvider dataFormProvider = new DataFormProvider();

-

-		int eventType;

-		String elementName;

-		String namespace;

-		while (!done) {

-			eventType = parser.next();

-			elementName = parser.getName();

-			namespace = parser.getNamespace();

-			if (eventType == XmlPullParser.START_TAG) {

-				if (elementName.equals("file")) {

-					name = parser.getAttributeValue("", "name");

-					size = parser.getAttributeValue("", "size");

-					hash = parser.getAttributeValue("", "hash");

-					date = parser.getAttributeValue("", "date");

-				} else if (elementName.equals("desc")) {

-					desc = parser.nextText();

-				} else if (elementName.equals("range")) {

-					isRanged = true;

-				} else if (elementName.equals("x")

-						&& namespace.equals("jabber:x:data")) {

-					form = (DataForm) dataFormProvider.parseExtension(parser);

-				}

-			} else if (eventType == XmlPullParser.END_TAG) {

-				if (elementName.equals("si")) {

-					done = true;

-				} else if (elementName.equals("file")) {

-                    long fileSize = 0;

-                    if(size != null && size.trim().length() !=0){

-                        try {

-                            fileSize = Long.parseLong(size);

-                        }

-                        catch (NumberFormatException e) {

-                            e.printStackTrace();

-                        }

-                    }

-                    

-                    Date fileDate = new Date();

-                    if (date != null) {

-                        try {

-                            fileDate = StringUtils.parseXEP0082Date(date);

-                        } catch (ParseException e) {

-                            // couldn't parse date, use current date-time

-                        }

-                    }

-                    

-                    File file = new File(name, fileSize);

-					file.setHash(hash);

-					file.setDate(fileDate);

-					file.setDesc(desc);

-					file.setRanged(isRanged);

-					initiation.setFile(file);

-				}

-			}

-		}

-

-		initiation.setSesssionID(id);

-		initiation.setMimeType(mimeType);

-

-		initiation.setFeatureNegotiationForm(form);

-

-		return initiation;

-	}

-

-}

+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 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.smackx.provider;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.packet.StreamInitiation;
+import org.jivesoftware.smackx.packet.StreamInitiation.File;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * The StreamInitiationProvider parses StreamInitiation packets.
+ * 
+ * @author Alexander Wenckus
+ * 
+ */
+public class StreamInitiationProvider implements IQProvider {
+
+	public IQ parseIQ(final XmlPullParser parser) throws Exception {
+		boolean done = false;
+
+		// si
+		String id = parser.getAttributeValue("", "id");
+		String mimeType = parser.getAttributeValue("", "mime-type");
+
+		StreamInitiation initiation = new StreamInitiation();
+
+		// file
+		String name = null;
+		String size = null;
+		String hash = null;
+		String date = null;
+		String desc = null;
+		boolean isRanged = false;
+
+		// feature
+		DataForm form = null;
+		DataFormProvider dataFormProvider = new DataFormProvider();
+
+		int eventType;
+		String elementName;
+		String namespace;
+		while (!done) {
+			eventType = parser.next();
+			elementName = parser.getName();
+			namespace = parser.getNamespace();
+			if (eventType == XmlPullParser.START_TAG) {
+				if (elementName.equals("file")) {
+					name = parser.getAttributeValue("", "name");
+					size = parser.getAttributeValue("", "size");
+					hash = parser.getAttributeValue("", "hash");
+					date = parser.getAttributeValue("", "date");
+				} else if (elementName.equals("desc")) {
+					desc = parser.nextText();
+				} else if (elementName.equals("range")) {
+					isRanged = true;
+				} else if (elementName.equals("x")
+						&& namespace.equals("jabber:x:data")) {
+					form = (DataForm) dataFormProvider.parseExtension(parser);
+				}
+			} else if (eventType == XmlPullParser.END_TAG) {
+				if (elementName.equals("si")) {
+					done = true;
+				} else if (elementName.equals("file")) {
+                    long fileSize = 0;
+                    if(size != null && size.trim().length() !=0){
+                        try {
+                            fileSize = Long.parseLong(size);
+                        }
+                        catch (NumberFormatException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                    
+                    Date fileDate = new Date();
+                    if (date != null) {
+                        try {
+                            fileDate = StringUtils.parseXEP0082Date(date);
+                        } catch (ParseException e) {
+                            // couldn't parse date, use current date-time
+                        }
+                    }
+                    
+                    File file = new File(name, fileSize);
+					file.setHash(hash);
+					file.setDate(fileDate);
+					file.setDesc(desc);
+					file.setRanged(isRanged);
+					initiation.setFile(file);
+				}
+			}
+		}
+
+		initiation.setSesssionID(id);
+		initiation.setMimeType(mimeType);
+
+		initiation.setFeatureNegotiationForm(form);
+
+		return initiation;
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AccessModel.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AccessModel.java
index c1fa546..bd0294c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AccessModel.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AccessModel.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * This enumeration represents the access models for the pubsub node

- * as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>

- * 

- * @author Robin Collier

- */

-public enum AccessModel

-{

-	/** Anyone may subscribe and retrieve items	 */

-	open,

-

-	/** Subscription request must be approved and only subscribers may retrieve items */

-	authorize,

-	

-	/** Anyone with a presence subscription of both or from may subscribe and retrieve items */

-	presence,

-	

-	/** Anyone in the specified roster group(s) may subscribe and retrieve items */

-	roster,

-	

-	/** Only those on a whitelist may subscribe and retrieve items */

-	whitelist;

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * This enumeration represents the access models for the pubsub node
+ * as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>
+ * 
+ * @author Robin Collier
+ */
+public enum AccessModel
+{
+	/** Anyone may subscribe and retrieve items	 */
+	open,
+
+	/** Subscription request must be approved and only subscribers may retrieve items */
+	authorize,
+	
+	/** Anyone with a presence subscription of both or from may subscribe and retrieve items */
+	presence,
+	
+	/** Anyone in the specified roster group(s) may subscribe and retrieve items */
+	roster,
+	
+	/** Only those on a whitelist may subscribe and retrieve items */
+	whitelist;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Affiliation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Affiliation.java
index d0fc7dc..c2208c1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Affiliation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Affiliation.java
@@ -1,90 +1,90 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * Represents a affiliation between a user and a node, where the {@link #type} defines 

- * the type of affiliation.

- * 

- * Affiliations are retrieved from the {@link PubSubManager#getAffiliations()} method, which 

- * gets affiliations for the calling user, based on the identity that is associated with 

- * the {@link Connection}.

- * 

- * @author Robin Collier

- */

-public class Affiliation implements PacketExtension

-{

-	protected String node;

-	protected Type type;

-	

-	public enum Type

-	{

-		member, none, outcast, owner, publisher

-	}

-

-	/**

-	 * Constructs an affiliation.

-	 * 

-	 * @param nodeId The node the user is affiliated with.

-	 * @param affiliation The type of affiliation.

-	 */

-	public Affiliation(String nodeId, Type affiliation)

-	{

-		node = nodeId;

-		type = affiliation;

-	}

-	

-	public String getNodeId()

-	{

-		return node;

-	}

-	

-	public Type getType()

-	{

-		return type;

-	}

-	

-	public String getElementName()

-	{

-		return "subscription";

-	}

-

-	public String getNamespace()

-	{

-		return null;

-	}

-

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		appendAttribute(builder, "node", node);

-		appendAttribute(builder, "affiliation", type.toString());

-		

-		builder.append("/>");

-		return builder.toString();

-	}

-

-	private void appendAttribute(StringBuilder builder, String att, String value)

-	{

-		builder.append(" ");

-		builder.append(att);

-		builder.append("='");

-		builder.append(value);

-		builder.append("'");

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Represents a affiliation between a user and a node, where the {@link #type} defines 
+ * the type of affiliation.
+ * 
+ * Affiliations are retrieved from the {@link PubSubManager#getAffiliations()} method, which 
+ * gets affiliations for the calling user, based on the identity that is associated with 
+ * the {@link Connection}.
+ * 
+ * @author Robin Collier
+ */
+public class Affiliation implements PacketExtension
+{
+	protected String node;
+	protected Type type;
+	
+	public enum Type
+	{
+		member, none, outcast, owner, publisher
+	}
+
+	/**
+	 * Constructs an affiliation.
+	 * 
+	 * @param nodeId The node the user is affiliated with.
+	 * @param affiliation The type of affiliation.
+	 */
+	public Affiliation(String nodeId, Type affiliation)
+	{
+		node = nodeId;
+		type = affiliation;
+	}
+	
+	public String getNodeId()
+	{
+		return node;
+	}
+	
+	public Type getType()
+	{
+		return type;
+	}
+	
+	public String getElementName()
+	{
+		return "subscription";
+	}
+
+	public String getNamespace()
+	{
+		return null;
+	}
+
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		appendAttribute(builder, "node", node);
+		appendAttribute(builder, "affiliation", type.toString());
+		
+		builder.append("/>");
+		return builder.toString();
+	}
+
+	private void appendAttribute(StringBuilder builder, String att, String value)
+	{
+		builder.append(" ");
+		builder.append(att);
+		builder.append("='");
+		builder.append(value);
+		builder.append("'");
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java
index aa82dcb..4c0cb61 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java
@@ -1,69 +1,69 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Collections;

-import java.util.List;

-

-/**

- * Represents the <b>affiliations</b> element of the reply to a request for affiliations.

- * It is defined in the specification in section <a href="http://xmpp.org/extensions/xep-0060.html#entity-affiliations">5.7 Retrieve Affiliations</a>.

- * 

- * @author Robin Collier

- */

-public class AffiliationsExtension extends NodeExtension

-{

-	protected List<Affiliation> items = Collections.EMPTY_LIST;

-	

-	public AffiliationsExtension()

-	{

-		super(PubSubElementType.AFFILIATIONS);

-	}

-	

-	public AffiliationsExtension(List<Affiliation> subList)

-	{

-		super(PubSubElementType.AFFILIATIONS);

-		items = subList;

-	}

-

-	public List<Affiliation> getAffiliations()

-	{

-		return items;

-	}

-

-	@Override

-	public String toXML()

-	{

-		if ((items == null) || (items.size() == 0))

-		{

-			return super.toXML();

-		}

-		else

-		{

-			StringBuilder builder = new StringBuilder("<");

-			builder.append(getElementName());

-			builder.append(">");

-			

-			for (Affiliation item : items)

-			{

-				builder.append(item.toXML());

-			}

-			

-			builder.append("</");

-			builder.append(getElementName());

-			builder.append(">");

-			return builder.toString();

-		}

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents the <b>affiliations</b> element of the reply to a request for affiliations.
+ * It is defined in the specification in section <a href="http://xmpp.org/extensions/xep-0060.html#entity-affiliations">5.7 Retrieve Affiliations</a>.
+ * 
+ * @author Robin Collier
+ */
+public class AffiliationsExtension extends NodeExtension
+{
+	protected List<Affiliation> items = Collections.EMPTY_LIST;
+	
+	public AffiliationsExtension()
+	{
+		super(PubSubElementType.AFFILIATIONS);
+	}
+	
+	public AffiliationsExtension(List<Affiliation> subList)
+	{
+		super(PubSubElementType.AFFILIATIONS);
+		items = subList;
+	}
+
+	public List<Affiliation> getAffiliations()
+	{
+		return items;
+	}
+
+	@Override
+	public String toXML()
+	{
+		if ((items == null) || (items.size() == 0))
+		{
+			return super.toXML();
+		}
+		else
+		{
+			StringBuilder builder = new StringBuilder("<");
+			builder.append(getElementName());
+			builder.append(">");
+			
+			for (Affiliation item : items)
+			{
+				builder.append(item.toXML());
+			}
+			
+			builder.append("</");
+			builder.append(getElementName());
+			builder.append(">");
+			return builder.toString();
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java
index 933a39e..13fabe9 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java
@@ -1,32 +1,32 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * This enumeration represents the children association policy for associating leaf nodes

- * with collection nodes as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>

- * 

- * @author Robin Collier

- */

-public enum ChildrenAssociationPolicy

-{

-	/** Anyone may associate leaf nodes with the collection	 */

-	all,

-	

-	/** Only collection node owners may associate leaf nodes with the collection. */

-	owners,

-	

-	/** Only those on a whitelist may associate leaf nodes with the collection.	 */

-	whitelist;

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * This enumeration represents the children association policy for associating leaf nodes
+ * with collection nodes as defined in the pubsub specification section <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-config">16.4.3</a>
+ * 
+ * @author Robin Collier
+ */
+public enum ChildrenAssociationPolicy
+{
+	/** Anyone may associate leaf nodes with the collection	 */
+	all,
+	
+	/** Only collection node owners may associate leaf nodes with the collection. */
+	owners,
+	
+	/** Only those on a whitelist may associate leaf nodes with the collection.	 */
+	whitelist;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/CollectionNode.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/CollectionNode.java
index dcd1cc4..73b9165 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/CollectionNode.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/CollectionNode.java
@@ -17,15 +17,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx.pubsub;

-

-import org.jivesoftware.smack.Connection;

-

-public class CollectionNode extends Node

-{

-	CollectionNode(Connection connection, String nodeId)

-	{

-		super(connection, nodeId);

-	}

-

-}

+package org.jivesoftware.smackx.pubsub;
+
+import org.jivesoftware.smack.Connection;
+
+public class CollectionNode extends Node
+{
+	CollectionNode(Connection connection, String nodeId)
+	{
+		super(connection, nodeId);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java
index 67b8304..3f002cb 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java
@@ -1,56 +1,56 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Arrays;

-import java.util.Collections;

-import java.util.List;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * Represents the <b>configuration</b> element of a pubsub message event which 

- * associates a configuration form to the node which was configured.  The form 

- * contains the current node configuration.

- *  

- * @author Robin Collier

- */

-public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketExtension

-{

-	private ConfigureForm form;

-	

-	public ConfigurationEvent(String nodeId)

-	{

-		super(PubSubElementType.CONFIGURATION, nodeId);

-	}

-	

-	public ConfigurationEvent(String nodeId, ConfigureForm configForm)

-	{

-		super(PubSubElementType.CONFIGURATION, nodeId);

-		form = configForm;

-	}

-	

-	public ConfigureForm getConfiguration()

-	{

-		return form;

-	}

-

-	public List<PacketExtension> getExtensions()

-	{

-		if (getConfiguration() == null)

-			return Collections.EMPTY_LIST;

-		else

-			return Arrays.asList(((PacketExtension)getConfiguration().getDataFormToSend()));

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Represents the <b>configuration</b> element of a pubsub message event which 
+ * associates a configuration form to the node which was configured.  The form 
+ * contains the current node configuration.
+ *  
+ * @author Robin Collier
+ */
+public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketExtension
+{
+	private ConfigureForm form;
+	
+	public ConfigurationEvent(String nodeId)
+	{
+		super(PubSubElementType.CONFIGURATION, nodeId);
+	}
+	
+	public ConfigurationEvent(String nodeId, ConfigureForm configForm)
+	{
+		super(PubSubElementType.CONFIGURATION, nodeId);
+		form = configForm;
+	}
+	
+	public ConfigureForm getConfiguration()
+	{
+		return form;
+	}
+
+	public List<PacketExtension> getExtensions()
+	{
+		if (getConfiguration() == null)
+			return Collections.EMPTY_LIST;
+		else
+			return Arrays.asList(((PacketExtension)getConfiguration().getDataFormToSend()));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureForm.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureForm.java
index f6fe140..127c849 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureForm.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureForm.java
@@ -1,709 +1,709 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.ArrayList;

-import java.util.Iterator;

-import java.util.List;

-

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.packet.DataForm;

-

-/**

- * A decorator for a {@link Form} to easily enable reading and updating

- * of node configuration.  All operations read or update the underlying {@link DataForm}.

- * 

- * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not

- * exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form

- * if it does not already exist. 

- * 

- * @author Robin Collier

- */

-public class ConfigureForm extends Form

-{

-	/**

-	 * Create a decorator from an existing {@link DataForm} that has been

-	 * retrieved from parsing a node configuration request.

-	 * 

-	 * @param configDataForm

-	 */

-	public ConfigureForm(DataForm configDataForm)

-	{

-		super(configDataForm);

-	}

-	

-	/**

-	 * Create a decorator from an existing {@link Form} for node configuration.

-	 * Typically, this can be used to create a decorator for an answer form

-	 * by using the result of {@link #createAnswerForm()} as the input parameter.

-	 * 

-	 * @param nodeConfigForm

-	 */

-	public ConfigureForm(Form nodeConfigForm)

-	{

-		super(nodeConfigForm.getDataFormToSend());

-	}

-	

-	/**

-	 * Create a new form for configuring a node.  This would typically only be used 

-	 * when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since 

-	 * configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and

-	 * using the resulting form to create a answer form.  See {@link #ConfigureForm(Form)}.

-	 * @param formType

-	 */

-	public ConfigureForm(FormType formType)

-	{

-		super(formType.toString());

-	}

-	

-	/**

-	 * Get the currently configured {@link AccessModel}, null if it is not set.

-	 * 

-	 * @return The current {@link AccessModel}

-	 */

-	public AccessModel getAccessModel()

-	{

-		String value = getFieldValue(ConfigureNodeFields.access_model);

-		

-		if (value == null)

-			return null;

-		else

-			return AccessModel.valueOf(value);

-	}

-	

-	/**

-	 * Sets the value of access model.

-	 * 

-	 * @param accessModel

-	 */

-	public void setAccessModel(AccessModel accessModel)

-	{

-		addField(ConfigureNodeFields.access_model, FormField.TYPE_LIST_SINGLE);

-		setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));

-	}

-

-	/**

-	 * Returns the URL of an XSL transformation which can be applied to payloads in order to 

-	 * generate an appropriate message body element.

-	 * 

-	 * @return URL to an XSL

-	 */

-	public String getBodyXSLT()

-	{

-		return getFieldValue(ConfigureNodeFields.body_xslt);

-	}

-

-	/**

-	 * Set the URL of an XSL transformation which can be applied to payloads in order to 

-	 * generate an appropriate message body element.

-	 * 

-	 * @param bodyXslt The URL of an XSL

-	 */

-	public void setBodyXSLT(String bodyXslt)

-	{

-		addField(ConfigureNodeFields.body_xslt, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);

-	}

-	

-	/**

-	 * The id's of the child nodes associated with a collection node (both leaf and collection).

-	 * 

-	 * @return Iterator over the list of child nodes.

-	 */

-	public Iterator<String> getChildren()

-	{

-		return getFieldValues(ConfigureNodeFields.children);

-	}

-	

-	/**

-	 * Set the list of child node ids that are associated with a collection node.

-	 * 

-	 * @param children

-	 */

-	public void setChildren(List<String> children)

-	{

-		addField(ConfigureNodeFields.children, FormField.TYPE_TEXT_MULTI);

-		setAnswer(ConfigureNodeFields.children.getFieldName(), children);

-	}

-	

-	/**

-	 * Returns the policy that determines who may associate children with the node.

-	 *  

-	 * @return The current policy

-	 */

-	public ChildrenAssociationPolicy getChildrenAssociationPolicy()

-	{

-		String value = getFieldValue(ConfigureNodeFields.children_association_policy);

-		

-		if (value == null)

-			return null;

-		else

-			return ChildrenAssociationPolicy.valueOf(value);

-	}

-	

-	/**

-	 * Sets the policy that determines who may associate children with the node.

-	 * 

-	 * @param policy The policy being set

-	 */

-	public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)

-	{

-		addField(ConfigureNodeFields.children_association_policy, FormField.TYPE_LIST_SINGLE);

-        List<String> values = new ArrayList<String>(1);

-        values.add(policy.toString());

-        setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);

-	}

-	

-	/**

-	 * Iterator of JID's that are on the whitelist that determines who can associate child nodes 

-	 * with the collection node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to

-	 * {@link ChildrenAssociationPolicy#whitelist}.

-	 * 

-	 * @return Iterator over whitelist

-	 */

-	public Iterator<String> getChildrenAssociationWhitelist()

-	{

-		return getFieldValues(ConfigureNodeFields.children_association_whitelist);

-	}

-	

-	/**

-	 * Set the JID's in the whitelist of users that can associate child nodes with the collection 

-	 * node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to

-	 * {@link ChildrenAssociationPolicy#whitelist}.

-	 * 

-	 * @param whitelist The list of JID's

-	 */

-	public void setChildrenAssociationWhitelist(List<String> whitelist)

-	{

-		addField(ConfigureNodeFields.children_association_whitelist, FormField.TYPE_JID_MULTI);

-		setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);

-	}

-

-	/**

-	 * Gets the maximum number of child nodes that can be associated with the collection node.

-	 * 

-	 * @return The maximum number of child nodes

-	 */

-	public int getChildrenMax()

-	{

-		return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));

-	}

-

-	/**

-	 * Set the maximum number of child nodes that can be associated with a collection node.

-	 * 

-	 * @param max The maximum number of child nodes.

-	 */

-	public void setChildrenMax(int max)

-	{

-		addField(ConfigureNodeFields.children_max, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);

-	}

-

-	/**

-	 * Gets the collection node which the node is affiliated with.

-	 * 

-	 * @return The collection node id

-	 */

-	public String getCollection()

-	{

-		return getFieldValue(ConfigureNodeFields.collection);

-	}

-

-	/**

-	 * Sets the collection node which the node is affiliated with.

-	 * 

-	 * @param collection The node id of the collection node

-	 */

-	public void setCollection(String collection)

-	{

-		addField(ConfigureNodeFields.collection, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);

-	}

-

-	/**

-	 * Gets the URL of an XSL transformation which can be applied to the payload

-	 * format in order to generate a valid Data Forms result that the client could

-	 * display using a generic Data Forms rendering engine.

-	 * 

-	 * @return The URL of an XSL transformation

-	 */

-	public String getDataformXSLT()

-	{

-		return getFieldValue(ConfigureNodeFields.dataform_xslt);

-	}

-

-	/**

-	 * Sets the URL of an XSL transformation which can be applied to the payload

-	 * format in order to generate a valid Data Forms result that the client could

-	 * display using a generic Data Forms rendering engine.

-	 * 

-	 * @param url The URL of an XSL transformation

-	 */

-	public void setDataformXSLT(String url)

-	{

-		addField(ConfigureNodeFields.dataform_xslt, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);

-	}

-

-	/**

-	 * Does the node deliver payloads with event notifications.

-	 * 

-	 * @return true if it does, false otherwise

-	 */

-	public boolean isDeliverPayloads()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));

-	}

-	

-	/**

-	 * Sets whether the node will deliver payloads with event notifications.

-	 * 

-	 * @param deliver true if the payload will be delivered, false otherwise

-	 */

-	public void setDeliverPayloads(boolean deliver)

-	{

-		addField(ConfigureNodeFields.deliver_payloads, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);

-	}

-

-	/**

-	 * Determines who should get replies to items

-	 * 

-	 * @return Who should get the reply

-	 */

-	public ItemReply getItemReply()

-	{

-		String value = getFieldValue(ConfigureNodeFields.itemreply);

-		

-		if (value == null)

-			return null;

-		else

-			return ItemReply.valueOf(value);

-	}

-

-	/**

-	 * Sets who should get the replies to items

-	 * 

-	 * @param reply Defines who should get the reply

-	 */

-	public void setItemReply(ItemReply reply)

-	{

-		addField(ConfigureNodeFields.itemreply, FormField.TYPE_LIST_SINGLE);

-		setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));

-	}

-

-	/**

-	 * Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is

-	 * true.

-	 * 

-	 * @return The maximum number of items to persist

-	 */

-	public int getMaxItems()

-	{

-		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));

-	}

-

-	/**

-	 * Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is

-	 * true.

-	 * 

-	 * @param max The maximum number of items to persist

-	 */

-	public void setMaxItems(int max)

-	{

-		addField(ConfigureNodeFields.max_items, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);

-	}

-	

-	/**

-	 * Gets the maximum payload size in bytes.

-	 * 

-	 * @return The maximum payload size

-	 */

-	public int getMaxPayloadSize()

-	{

-		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));

-	}

-

-	/**

-	 * Sets the maximum payload size in bytes

-	 * 

-	 * @param max The maximum payload size

-	 */

-	public void setMaxPayloadSize(int max)

-	{

-		addField(ConfigureNodeFields.max_payload_size, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);

-	}

-	

-	/**

-	 * Gets the node type

-	 * 

-	 * @return The node type

-	 */

-	public NodeType getNodeType()

-	{

-		String value = getFieldValue(ConfigureNodeFields.node_type);

-		

-		if (value == null)

-			return null;

-		else

-			return NodeType.valueOf(value);

-	}

-	

-	/**

-	 * Sets the node type

-	 * 

-	 * @param type The node type

-	 */

-	public void setNodeType(NodeType type)

-	{

-		addField(ConfigureNodeFields.node_type, FormField.TYPE_LIST_SINGLE);

-		setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));

-	}

-

-	/**

-	 * Determines if subscribers should be notified when the configuration changes.

-	 * 

-	 * @return true if they should be notified, false otherwise

-	 */

-	public boolean isNotifyConfig()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));

-	}

-	

-	/**

-	 * Sets whether subscribers should be notified when the configuration changes.

-	 * 

-	 * @param notify true if subscribers should be notified, false otherwise

-	 */

-	public void setNotifyConfig(boolean notify)

-	{

-		addField(ConfigureNodeFields.notify_config, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);

-	}

-

-	/**

-	 * Determines whether subscribers should be notified when the node is deleted.

-	 * 

-	 * @return true if subscribers should be notified, false otherwise

-	 */

-	public boolean isNotifyDelete()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));

-	}

-	

-	/**

-	 * Sets whether subscribers should be notified when the node is deleted.

-	 * 

-	 * @param notify true if subscribers should be notified, false otherwise

-	 */

-	public void setNotifyDelete(boolean notify) 

-	{

-		addField(ConfigureNodeFields.notify_delete, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);

-	}

-

-	/**

-	 * Determines whether subscribers should be notified when items are deleted 

-	 * from the node.

-	 * 

-	 * @return true if subscribers should be notified, false otherwise

-	 */

-	public boolean isNotifyRetract()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));

-	}

-	

-	/**

-	 * Sets whether subscribers should be notified when items are deleted 

-	 * from the node.

-	 * 

-	 * @param notify true if subscribers should be notified, false otherwise

-	 */

-	public void setNotifyRetract(boolean notify) 

-	{

-		addField(ConfigureNodeFields.notify_retract, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);

-	}

-	

-	/**

-	 * Determines whether items should be persisted in the node.

-	 * 

-	 * @return true if items are persisted

-	 */

-	public boolean isPersistItems()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));

-	}

-	

-	/**

-	 * Sets whether items should be persisted in the node.

-	 * 

-	 * @param persist true if items should be persisted, false otherwise

-	 */

-	public void setPersistentItems(boolean persist) 

-	{

-		addField(ConfigureNodeFields.persist_items, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);

-	}

-

-	/**

-	 * Determines whether to deliver notifications to available users only.

-	 * 

-	 * @return true if users must be available

-	 */

-	public boolean isPresenceBasedDelivery()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));

-	}

-	

-	/**

-	 * Sets whether to deliver notifications to available users only.

-	 * 

-	 * @param presenceBased true if user must be available, false otherwise

-	 */

-	public void setPresenceBasedDelivery(boolean presenceBased) 

-	{

-		addField(ConfigureNodeFields.presence_based_delivery, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);

-	}

-

-	/**

-	 * Gets the publishing model for the node, which determines who may publish to it.

-	 * 

-	 * @return The publishing model

-	 */

-	public PublishModel getPublishModel()

-	{

-		String value = getFieldValue(ConfigureNodeFields.publish_model);

-		

-		if (value == null)

-			return null;

-		else

-			return PublishModel.valueOf(value);

-	}

-

-	/**

-	 * Sets the publishing model for the node, which determines who may publish to it.

-	 * 

-	 * @param publish The enum representing the possible options for the publishing model

-	 */

-	public void setPublishModel(PublishModel publish) 

-	{

-		addField(ConfigureNodeFields.publish_model, FormField.TYPE_LIST_SINGLE);

-		setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));

-	}

-	

-	/**

-	 * Iterator over the multi user chat rooms that are specified as reply rooms.

-	 * 

-	 * @return The reply room JID's

-	 */

-	public Iterator<String> getReplyRoom()

-	{

-		return getFieldValues(ConfigureNodeFields.replyroom);

-	}

-	

-	/**

-	 * Sets the multi user chat rooms that are specified as reply rooms.

-	 * 

-	 * @param replyRooms The multi user chat room to use as reply rooms

-	 */

-	public void setReplyRoom(List<String> replyRooms) 

-	{

-		addField(ConfigureNodeFields.replyroom, FormField.TYPE_LIST_MULTI);

-		setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);

-	}

-	

-	/**

-	 * Gets the specific JID's for reply to.

-	 *  

-	 * @return The JID's

-	 */

-	public Iterator<String> getReplyTo()

-	{

-		return getFieldValues(ConfigureNodeFields.replyto);

-	}

-	

-	/**

-	 * Sets the specific JID's for reply to.

-	 * 

-	 * @param replyTos The JID's to reply to

-	 */

-	public void setReplyTo(List<String> replyTos)

-	{

-		addField(ConfigureNodeFields.replyto, FormField.TYPE_LIST_MULTI);

-		setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);

-	}

-	

-	/**

-	 * Gets the roster groups that are allowed to subscribe and retrieve items.

-	 *  

-	 * @return The roster groups

-	 */

-	public Iterator<String> getRosterGroupsAllowed()

-	{

-		return getFieldValues(ConfigureNodeFields.roster_groups_allowed);

-	}

-	

-	/**

-	 * Sets the roster groups that are allowed to subscribe and retrieve items.

-	 * 

-	 * @param groups The roster groups

-	 */

-	public void setRosterGroupsAllowed(List<String> groups)

-	{

-		addField(ConfigureNodeFields.roster_groups_allowed, FormField.TYPE_LIST_MULTI);

-		setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);

-	}

-	

-	/**

-	 * Determines if subscriptions are allowed.

-	 * 

-	 * @return true if subscriptions are allowed, false otherwise

-	 */

-	public boolean isSubscibe()

-	{

-		return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));

-	}

-

-	/**

-	 * Sets whether subscriptions are allowed.

-	 * 

-	 * @param subscribe true if they are, false otherwise

-	 */

-	public void setSubscribe(boolean subscribe)

-	{

-		addField(ConfigureNodeFields.subscribe, FormField.TYPE_BOOLEAN);

-		setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);

-	}

-	

-	/**

-	 * Gets the human readable node title.

-	 * 

-	 * @return The node title

-	 */

-	public String getTitle()

-	{

-		return getFieldValue(ConfigureNodeFields.title);

-	}

-

-	/**

-	 * Sets a human readable title for the node.

-	 * 

-	 * @param title The node title

-	 */

-	public void setTitle(String title) 

-	{

-		addField(ConfigureNodeFields.title, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.title.getFieldName(), title);

-	}

-	

-	/**

-	 * The type of node data, usually specified by the namespace of the payload (if any).

-	 * 

-	 * @return The type of node data

-	 */

-	public String getDataType()

-	{

-		return getFieldValue(ConfigureNodeFields.type);

-	}

-

-	/**

-	 * Sets the type of node data, usually specified by the namespace of the payload (if any).

-	 * 

-	 * @param type The type of node data

-	 */

-	public void setDataType(String type) 

-	{

-		addField(ConfigureNodeFields.type, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(ConfigureNodeFields.type.getFieldName(), type);

-	}

-	

-	@Override

-	public String toString()

-	{

-		StringBuilder result = new StringBuilder(getClass().getName() + " Content [");

-		

-		Iterator<FormField> fields = getFields();

-		

-		while (fields.hasNext())

-		{

-			FormField formField = fields.next();

-			result.append('(');

-			result.append(formField.getVariable());

-			result.append(':');

-			

-			Iterator<String> values = formField.getValues();

-			StringBuilder valuesBuilder = new StringBuilder();

-				

-			while (values.hasNext())

-			{

-				if (valuesBuilder.length() > 0)

-					result.append(',');

-				String value = (String)values.next();

-				valuesBuilder.append(value);

-			}

-			

-			if (valuesBuilder.length() == 0)

-				valuesBuilder.append("NOT SET");

-			result.append(valuesBuilder);

-			result.append(')');

-		}

-		result.append(']');

-		return result.toString();

-	}

-

-	static private boolean parseBoolean(String fieldValue)

-	{

-		return ("1".equals(fieldValue) || "true".equals(fieldValue));

-	}

-

-	private String getFieldValue(ConfigureNodeFields field)

-	{

-		FormField formField = getField(field.getFieldName());

-		

-		return (formField.getValues().hasNext()) ? formField.getValues().next() : null;

-	}

-

-	private Iterator<String> getFieldValues(ConfigureNodeFields field)

-	{

-		FormField formField = getField(field.getFieldName());

-		

-		return formField.getValues();

-	}

-

-	private void addField(ConfigureNodeFields nodeField, String type)

-	{

-		String fieldName = nodeField.getFieldName();

-		

-		if (getField(fieldName) == null)

-		{

-			FormField field = new FormField(fieldName);

-			field.setType(type);

-			addField(field);

-		}

-	}

-

-	private List<String> getListSingle(String value)

-	{

-		List<String> list = new ArrayList<String>(1);

-		list.add(value);

-		return list;

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.packet.DataForm;
+
+/**
+ * A decorator for a {@link Form} to easily enable reading and updating
+ * of node configuration.  All operations read or update the underlying {@link DataForm}.
+ * 
+ * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
+ * exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
+ * if it does not already exist. 
+ * 
+ * @author Robin Collier
+ */
+public class ConfigureForm extends Form
+{
+	/**
+	 * Create a decorator from an existing {@link DataForm} that has been
+	 * retrieved from parsing a node configuration request.
+	 * 
+	 * @param configDataForm
+	 */
+	public ConfigureForm(DataForm configDataForm)
+	{
+		super(configDataForm);
+	}
+	
+	/**
+	 * Create a decorator from an existing {@link Form} for node configuration.
+	 * Typically, this can be used to create a decorator for an answer form
+	 * by using the result of {@link #createAnswerForm()} as the input parameter.
+	 * 
+	 * @param nodeConfigForm
+	 */
+	public ConfigureForm(Form nodeConfigForm)
+	{
+		super(nodeConfigForm.getDataFormToSend());
+	}
+	
+	/**
+	 * Create a new form for configuring a node.  This would typically only be used 
+	 * when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since 
+	 * configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
+	 * using the resulting form to create a answer form.  See {@link #ConfigureForm(Form)}.
+	 * @param formType
+	 */
+	public ConfigureForm(FormType formType)
+	{
+		super(formType.toString());
+	}
+	
+	/**
+	 * Get the currently configured {@link AccessModel}, null if it is not set.
+	 * 
+	 * @return The current {@link AccessModel}
+	 */
+	public AccessModel getAccessModel()
+	{
+		String value = getFieldValue(ConfigureNodeFields.access_model);
+		
+		if (value == null)
+			return null;
+		else
+			return AccessModel.valueOf(value);
+	}
+	
+	/**
+	 * Sets the value of access model.
+	 * 
+	 * @param accessModel
+	 */
+	public void setAccessModel(AccessModel accessModel)
+	{
+		addField(ConfigureNodeFields.access_model, FormField.TYPE_LIST_SINGLE);
+		setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
+	}
+
+	/**
+	 * Returns the URL of an XSL transformation which can be applied to payloads in order to 
+	 * generate an appropriate message body element.
+	 * 
+	 * @return URL to an XSL
+	 */
+	public String getBodyXSLT()
+	{
+		return getFieldValue(ConfigureNodeFields.body_xslt);
+	}
+
+	/**
+	 * Set the URL of an XSL transformation which can be applied to payloads in order to 
+	 * generate an appropriate message body element.
+	 * 
+	 * @param bodyXslt The URL of an XSL
+	 */
+	public void setBodyXSLT(String bodyXslt)
+	{
+		addField(ConfigureNodeFields.body_xslt, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
+	}
+	
+	/**
+	 * The id's of the child nodes associated with a collection node (both leaf and collection).
+	 * 
+	 * @return Iterator over the list of child nodes.
+	 */
+	public Iterator<String> getChildren()
+	{
+		return getFieldValues(ConfigureNodeFields.children);
+	}
+	
+	/**
+	 * Set the list of child node ids that are associated with a collection node.
+	 * 
+	 * @param children
+	 */
+	public void setChildren(List<String> children)
+	{
+		addField(ConfigureNodeFields.children, FormField.TYPE_TEXT_MULTI);
+		setAnswer(ConfigureNodeFields.children.getFieldName(), children);
+	}
+	
+	/**
+	 * Returns the policy that determines who may associate children with the node.
+	 *  
+	 * @return The current policy
+	 */
+	public ChildrenAssociationPolicy getChildrenAssociationPolicy()
+	{
+		String value = getFieldValue(ConfigureNodeFields.children_association_policy);
+		
+		if (value == null)
+			return null;
+		else
+			return ChildrenAssociationPolicy.valueOf(value);
+	}
+	
+	/**
+	 * Sets the policy that determines who may associate children with the node.
+	 * 
+	 * @param policy The policy being set
+	 */
+	public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)
+	{
+		addField(ConfigureNodeFields.children_association_policy, FormField.TYPE_LIST_SINGLE);
+        List<String> values = new ArrayList<String>(1);
+        values.add(policy.toString());
+        setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);
+	}
+	
+	/**
+	 * Iterator of JID's that are on the whitelist that determines who can associate child nodes 
+	 * with the collection node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
+	 * {@link ChildrenAssociationPolicy#whitelist}.
+	 * 
+	 * @return Iterator over whitelist
+	 */
+	public Iterator<String> getChildrenAssociationWhitelist()
+	{
+		return getFieldValues(ConfigureNodeFields.children_association_whitelist);
+	}
+	
+	/**
+	 * Set the JID's in the whitelist of users that can associate child nodes with the collection 
+	 * node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
+	 * {@link ChildrenAssociationPolicy#whitelist}.
+	 * 
+	 * @param whitelist The list of JID's
+	 */
+	public void setChildrenAssociationWhitelist(List<String> whitelist)
+	{
+		addField(ConfigureNodeFields.children_association_whitelist, FormField.TYPE_JID_MULTI);
+		setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
+	}
+
+	/**
+	 * Gets the maximum number of child nodes that can be associated with the collection node.
+	 * 
+	 * @return The maximum number of child nodes
+	 */
+	public int getChildrenMax()
+	{
+		return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
+	}
+
+	/**
+	 * Set the maximum number of child nodes that can be associated with a collection node.
+	 * 
+	 * @param max The maximum number of child nodes.
+	 */
+	public void setChildrenMax(int max)
+	{
+		addField(ConfigureNodeFields.children_max, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
+	}
+
+	/**
+	 * Gets the collection node which the node is affiliated with.
+	 * 
+	 * @return The collection node id
+	 */
+	public String getCollection()
+	{
+		return getFieldValue(ConfigureNodeFields.collection);
+	}
+
+	/**
+	 * Sets the collection node which the node is affiliated with.
+	 * 
+	 * @param collection The node id of the collection node
+	 */
+	public void setCollection(String collection)
+	{
+		addField(ConfigureNodeFields.collection, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
+	}
+
+	/**
+	 * Gets the URL of an XSL transformation which can be applied to the payload
+	 * format in order to generate a valid Data Forms result that the client could
+	 * display using a generic Data Forms rendering engine.
+	 * 
+	 * @return The URL of an XSL transformation
+	 */
+	public String getDataformXSLT()
+	{
+		return getFieldValue(ConfigureNodeFields.dataform_xslt);
+	}
+
+	/**
+	 * Sets the URL of an XSL transformation which can be applied to the payload
+	 * format in order to generate a valid Data Forms result that the client could
+	 * display using a generic Data Forms rendering engine.
+	 * 
+	 * @param url The URL of an XSL transformation
+	 */
+	public void setDataformXSLT(String url)
+	{
+		addField(ConfigureNodeFields.dataform_xslt, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
+	}
+
+	/**
+	 * Does the node deliver payloads with event notifications.
+	 * 
+	 * @return true if it does, false otherwise
+	 */
+	public boolean isDeliverPayloads()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
+	}
+	
+	/**
+	 * Sets whether the node will deliver payloads with event notifications.
+	 * 
+	 * @param deliver true if the payload will be delivered, false otherwise
+	 */
+	public void setDeliverPayloads(boolean deliver)
+	{
+		addField(ConfigureNodeFields.deliver_payloads, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
+	}
+
+	/**
+	 * Determines who should get replies to items
+	 * 
+	 * @return Who should get the reply
+	 */
+	public ItemReply getItemReply()
+	{
+		String value = getFieldValue(ConfigureNodeFields.itemreply);
+		
+		if (value == null)
+			return null;
+		else
+			return ItemReply.valueOf(value);
+	}
+
+	/**
+	 * Sets who should get the replies to items
+	 * 
+	 * @param reply Defines who should get the reply
+	 */
+	public void setItemReply(ItemReply reply)
+	{
+		addField(ConfigureNodeFields.itemreply, FormField.TYPE_LIST_SINGLE);
+		setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
+	}
+
+	/**
+	 * Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
+	 * true.
+	 * 
+	 * @return The maximum number of items to persist
+	 */
+	public int getMaxItems()
+	{
+		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
+	}
+
+	/**
+	 * Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
+	 * true.
+	 * 
+	 * @param max The maximum number of items to persist
+	 */
+	public void setMaxItems(int max)
+	{
+		addField(ConfigureNodeFields.max_items, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
+	}
+	
+	/**
+	 * Gets the maximum payload size in bytes.
+	 * 
+	 * @return The maximum payload size
+	 */
+	public int getMaxPayloadSize()
+	{
+		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
+	}
+
+	/**
+	 * Sets the maximum payload size in bytes
+	 * 
+	 * @param max The maximum payload size
+	 */
+	public void setMaxPayloadSize(int max)
+	{
+		addField(ConfigureNodeFields.max_payload_size, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
+	}
+	
+	/**
+	 * Gets the node type
+	 * 
+	 * @return The node type
+	 */
+	public NodeType getNodeType()
+	{
+		String value = getFieldValue(ConfigureNodeFields.node_type);
+		
+		if (value == null)
+			return null;
+		else
+			return NodeType.valueOf(value);
+	}
+	
+	/**
+	 * Sets the node type
+	 * 
+	 * @param type The node type
+	 */
+	public void setNodeType(NodeType type)
+	{
+		addField(ConfigureNodeFields.node_type, FormField.TYPE_LIST_SINGLE);
+		setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
+	}
+
+	/**
+	 * Determines if subscribers should be notified when the configuration changes.
+	 * 
+	 * @return true if they should be notified, false otherwise
+	 */
+	public boolean isNotifyConfig()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));
+	}
+	
+	/**
+	 * Sets whether subscribers should be notified when the configuration changes.
+	 * 
+	 * @param notify true if subscribers should be notified, false otherwise
+	 */
+	public void setNotifyConfig(boolean notify)
+	{
+		addField(ConfigureNodeFields.notify_config, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
+	}
+
+	/**
+	 * Determines whether subscribers should be notified when the node is deleted.
+	 * 
+	 * @return true if subscribers should be notified, false otherwise
+	 */
+	public boolean isNotifyDelete()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
+	}
+	
+	/**
+	 * Sets whether subscribers should be notified when the node is deleted.
+	 * 
+	 * @param notify true if subscribers should be notified, false otherwise
+	 */
+	public void setNotifyDelete(boolean notify) 
+	{
+		addField(ConfigureNodeFields.notify_delete, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
+	}
+
+	/**
+	 * Determines whether subscribers should be notified when items are deleted 
+	 * from the node.
+	 * 
+	 * @return true if subscribers should be notified, false otherwise
+	 */
+	public boolean isNotifyRetract()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
+	}
+	
+	/**
+	 * Sets whether subscribers should be notified when items are deleted 
+	 * from the node.
+	 * 
+	 * @param notify true if subscribers should be notified, false otherwise
+	 */
+	public void setNotifyRetract(boolean notify) 
+	{
+		addField(ConfigureNodeFields.notify_retract, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
+	}
+	
+	/**
+	 * Determines whether items should be persisted in the node.
+	 * 
+	 * @return true if items are persisted
+	 */
+	public boolean isPersistItems()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));
+	}
+	
+	/**
+	 * Sets whether items should be persisted in the node.
+	 * 
+	 * @param persist true if items should be persisted, false otherwise
+	 */
+	public void setPersistentItems(boolean persist) 
+	{
+		addField(ConfigureNodeFields.persist_items, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
+	}
+
+	/**
+	 * Determines whether to deliver notifications to available users only.
+	 * 
+	 * @return true if users must be available
+	 */
+	public boolean isPresenceBasedDelivery()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
+	}
+	
+	/**
+	 * Sets whether to deliver notifications to available users only.
+	 * 
+	 * @param presenceBased true if user must be available, false otherwise
+	 */
+	public void setPresenceBasedDelivery(boolean presenceBased) 
+	{
+		addField(ConfigureNodeFields.presence_based_delivery, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
+	}
+
+	/**
+	 * Gets the publishing model for the node, which determines who may publish to it.
+	 * 
+	 * @return The publishing model
+	 */
+	public PublishModel getPublishModel()
+	{
+		String value = getFieldValue(ConfigureNodeFields.publish_model);
+		
+		if (value == null)
+			return null;
+		else
+			return PublishModel.valueOf(value);
+	}
+
+	/**
+	 * Sets the publishing model for the node, which determines who may publish to it.
+	 * 
+	 * @param publish The enum representing the possible options for the publishing model
+	 */
+	public void setPublishModel(PublishModel publish) 
+	{
+		addField(ConfigureNodeFields.publish_model, FormField.TYPE_LIST_SINGLE);
+		setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
+	}
+	
+	/**
+	 * Iterator over the multi user chat rooms that are specified as reply rooms.
+	 * 
+	 * @return The reply room JID's
+	 */
+	public Iterator<String> getReplyRoom()
+	{
+		return getFieldValues(ConfigureNodeFields.replyroom);
+	}
+	
+	/**
+	 * Sets the multi user chat rooms that are specified as reply rooms.
+	 * 
+	 * @param replyRooms The multi user chat room to use as reply rooms
+	 */
+	public void setReplyRoom(List<String> replyRooms) 
+	{
+		addField(ConfigureNodeFields.replyroom, FormField.TYPE_LIST_MULTI);
+		setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
+	}
+	
+	/**
+	 * Gets the specific JID's for reply to.
+	 *  
+	 * @return The JID's
+	 */
+	public Iterator<String> getReplyTo()
+	{
+		return getFieldValues(ConfigureNodeFields.replyto);
+	}
+	
+	/**
+	 * Sets the specific JID's for reply to.
+	 * 
+	 * @param replyTos The JID's to reply to
+	 */
+	public void setReplyTo(List<String> replyTos)
+	{
+		addField(ConfigureNodeFields.replyto, FormField.TYPE_LIST_MULTI);
+		setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
+	}
+	
+	/**
+	 * Gets the roster groups that are allowed to subscribe and retrieve items.
+	 *  
+	 * @return The roster groups
+	 */
+	public Iterator<String> getRosterGroupsAllowed()
+	{
+		return getFieldValues(ConfigureNodeFields.roster_groups_allowed);
+	}
+	
+	/**
+	 * Sets the roster groups that are allowed to subscribe and retrieve items.
+	 * 
+	 * @param groups The roster groups
+	 */
+	public void setRosterGroupsAllowed(List<String> groups)
+	{
+		addField(ConfigureNodeFields.roster_groups_allowed, FormField.TYPE_LIST_MULTI);
+		setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
+	}
+	
+	/**
+	 * Determines if subscriptions are allowed.
+	 * 
+	 * @return true if subscriptions are allowed, false otherwise
+	 */
+	public boolean isSubscibe()
+	{
+		return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));
+	}
+
+	/**
+	 * Sets whether subscriptions are allowed.
+	 * 
+	 * @param subscribe true if they are, false otherwise
+	 */
+	public void setSubscribe(boolean subscribe)
+	{
+		addField(ConfigureNodeFields.subscribe, FormField.TYPE_BOOLEAN);
+		setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
+	}
+	
+	/**
+	 * Gets the human readable node title.
+	 * 
+	 * @return The node title
+	 */
+	public String getTitle()
+	{
+		return getFieldValue(ConfigureNodeFields.title);
+	}
+
+	/**
+	 * Sets a human readable title for the node.
+	 * 
+	 * @param title The node title
+	 */
+	public void setTitle(String title) 
+	{
+		addField(ConfigureNodeFields.title, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.title.getFieldName(), title);
+	}
+	
+	/**
+	 * The type of node data, usually specified by the namespace of the payload (if any).
+	 * 
+	 * @return The type of node data
+	 */
+	public String getDataType()
+	{
+		return getFieldValue(ConfigureNodeFields.type);
+	}
+
+	/**
+	 * Sets the type of node data, usually specified by the namespace of the payload (if any).
+	 * 
+	 * @param type The type of node data
+	 */
+	public void setDataType(String type) 
+	{
+		addField(ConfigureNodeFields.type, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(ConfigureNodeFields.type.getFieldName(), type);
+	}
+	
+	@Override
+	public String toString()
+	{
+		StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
+		
+		Iterator<FormField> fields = getFields();
+		
+		while (fields.hasNext())
+		{
+			FormField formField = fields.next();
+			result.append('(');
+			result.append(formField.getVariable());
+			result.append(':');
+			
+			Iterator<String> values = formField.getValues();
+			StringBuilder valuesBuilder = new StringBuilder();
+				
+			while (values.hasNext())
+			{
+				if (valuesBuilder.length() > 0)
+					result.append(',');
+				String value = (String)values.next();
+				valuesBuilder.append(value);
+			}
+			
+			if (valuesBuilder.length() == 0)
+				valuesBuilder.append("NOT SET");
+			result.append(valuesBuilder);
+			result.append(')');
+		}
+		result.append(']');
+		return result.toString();
+	}
+
+	static private boolean parseBoolean(String fieldValue)
+	{
+		return ("1".equals(fieldValue) || "true".equals(fieldValue));
+	}
+
+	private String getFieldValue(ConfigureNodeFields field)
+	{
+		FormField formField = getField(field.getFieldName());
+		
+		return (formField.getValues().hasNext()) ? formField.getValues().next() : null;
+	}
+
+	private Iterator<String> getFieldValues(ConfigureNodeFields field)
+	{
+		FormField formField = getField(field.getFieldName());
+		
+		return formField.getValues();
+	}
+
+	private void addField(ConfigureNodeFields nodeField, String type)
+	{
+		String fieldName = nodeField.getFieldName();
+		
+		if (getField(fieldName) == null)
+		{
+			FormField field = new FormField(fieldName);
+			field.setType(type);
+			addField(field);
+		}
+	}
+
+	private List<String> getListSingle(String value)
+	{
+		List<String> list = new ArrayList<String>(1);
+		list.add(value);
+		return list;
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java
index 3912483..5b9f662 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java
@@ -1,218 +1,218 @@
-/**

- * 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.smackx.pubsub;

-

-import java.net.URL;

-

-import org.jivesoftware.smackx.Form;

-

-/**

- * This enumeration represents all the fields of a node configuration form.  This enumeration

- * is not required when using the {@link ConfigureForm} to configure nodes, but may be helpful

- * for generic UI's using only a {@link Form} for configuration.

- * 

- * @author Robin Collier

- */

-public enum ConfigureNodeFields

-{

-	/**

-	 * Determines who may subscribe and retrieve items

-	 * 

-	 * <p><b>Value: {@link AccessModel}</b></p>

-	 */

-	access_model,

-

-	/**

-	 * The URL of an XSL transformation which can be applied to 

-	 * payloads in order to generate an appropriate message

-	 * body element

-	 * 

-	 * <p><b>Value: {@link URL}</b></p>

-	 */

-	body_xslt,

-	

-	/**

-	 * The collection with which a node is affiliated

-	 * 

-	 * <p><b>Value: String</b></p>

-	 */

-	collection,

-

-	/**

-	 * The URL of an XSL transformation which can be applied to 

-	 * payload format in order to generate a valid Data Forms result 

-	 * that the client could display using a generic Data Forms 

-	 * rendering engine body element.

-	 * 

-	 * <p><b>Value: {@link URL}</b></p>

-	 */

-	dataform_xslt,

-

-	/**

-	 * Whether to deliver payloads with event notifications

-	 *

-	 * <p><b>Value: boolean</b></p>

-	 */

-	deliver_payloads,

-	

-	/**

-	 * Whether owners or publisher should receive replies to items

-	 *

-	 * <p><b>Value: {@link ItemReply}</b></p>

-	 */

-	itemreply,

-	

-	/**

-	 * Who may associate leaf nodes with a collection

-	 * 

-	 * <p><b>Value: {@link ChildrenAssociationPolicy}</b></p>

-	 */

-	children_association_policy,

-	

-	/**

-	 * The list of JIDs that may associate leaf nodes with a 

-	 * collection

-	 * 

-	 * <p><b>Value: List of JIDs as Strings</b></p>

-	 */

-	children_association_whitelist,

-	

-	/**

-	 * The child nodes (leaf or collection) associated with a collection

-	 * 

-	 * <p><b>Value: List of Strings</b></p>

-	 */

-	children,

-	

-	/**

-	 * The maximum number of child nodes that can be associated with a 

-	 * collection

-	 * 

-	 * <p><b>Value: int</b></p>

-	 */

-	children_max,

-	

-	/**

-	 * The maximum number of items to persist

-	 * 

-	 * <p><b>Value: int</b></p>

-	 */

-	max_items,

-	

-	/**

-	 * The maximum payload size in bytes

-	 * 

-	 * <p><b>Value: int</b></p>

-	 */

-	max_payload_size,

-	

-	/**

-	 * Whether the node is a leaf (default) or collection

-	 * 

-	 * <p><b>Value: {@link NodeType}</b></p>

-	 */

-	node_type,

-	

-	/**

-	 * Whether to notify subscribers when the node configuration changes

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	notify_config,

-	

-	/**

-	 * Whether to notify subscribers when the node is deleted

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	notify_delete,

-

-	/**

-	 * Whether to notify subscribers when items are removed from the node

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	notify_retract,

-	

-	/**

-	 * Whether to persist items to storage.  This is required to have multiple 

-	 * items in the node. 

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	persist_items,

-	

-	/**

-	 * Whether to deliver notifications to available users only

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	presence_based_delivery,

-

-	/**

-	 * Defines who can publish to the node

-	 * 

-	 * <p><b>Value: {@link PublishModel}</b></p>

-	 */

-	publish_model,

-	

-	/**

-	 * The specific multi-user chat rooms to specify for replyroom

-	 * 

-	 * <p><b>Value: List of JIDs as Strings</b></p>

-	 */

-	replyroom,

-	

-	/**

-	 * The specific JID(s) to specify for replyto

-	 * 

-	 * <p><b>Value: List of JIDs as Strings</b></p>

-	 */

-	replyto,

-	

-	/**

-	 * The roster group(s) allowed to subscribe and retrieve items

-	 * 

-	 * <p><b>Value: List of strings</b></p>

-	 */

-	roster_groups_allowed,

-	

-	/**

-	 * Whether to allow subscriptions

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	subscribe,

-	

-	/**

-	 * A friendly name for the node

-	 * 

-	 * <p><b>Value: String</b></p>

-	 */

-	title,

-	

-	/**

-	 * The type of node data, ussually specified by the namespace 

-	 * of the payload(if any);MAY be a list-single rather than a 

-	 * text single

-	 * 

-	 * <p><b>Value: String</b></p>

-	 */

-	type;

-	

-	public String getFieldName()

-	{

-		return "pubsub#" + toString();

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.net.URL;
+
+import org.jivesoftware.smackx.Form;
+
+/**
+ * This enumeration represents all the fields of a node configuration form.  This enumeration
+ * is not required when using the {@link ConfigureForm} to configure nodes, but may be helpful
+ * for generic UI's using only a {@link Form} for configuration.
+ * 
+ * @author Robin Collier
+ */
+public enum ConfigureNodeFields
+{
+	/**
+	 * Determines who may subscribe and retrieve items
+	 * 
+	 * <p><b>Value: {@link AccessModel}</b></p>
+	 */
+	access_model,
+
+	/**
+	 * The URL of an XSL transformation which can be applied to 
+	 * payloads in order to generate an appropriate message
+	 * body element
+	 * 
+	 * <p><b>Value: {@link URL}</b></p>
+	 */
+	body_xslt,
+	
+	/**
+	 * The collection with which a node is affiliated
+	 * 
+	 * <p><b>Value: String</b></p>
+	 */
+	collection,
+
+	/**
+	 * The URL of an XSL transformation which can be applied to 
+	 * payload format in order to generate a valid Data Forms result 
+	 * that the client could display using a generic Data Forms 
+	 * rendering engine body element.
+	 * 
+	 * <p><b>Value: {@link URL}</b></p>
+	 */
+	dataform_xslt,
+
+	/**
+	 * Whether to deliver payloads with event notifications
+	 *
+	 * <p><b>Value: boolean</b></p>
+	 */
+	deliver_payloads,
+	
+	/**
+	 * Whether owners or publisher should receive replies to items
+	 *
+	 * <p><b>Value: {@link ItemReply}</b></p>
+	 */
+	itemreply,
+	
+	/**
+	 * Who may associate leaf nodes with a collection
+	 * 
+	 * <p><b>Value: {@link ChildrenAssociationPolicy}</b></p>
+	 */
+	children_association_policy,
+	
+	/**
+	 * The list of JIDs that may associate leaf nodes with a 
+	 * collection
+	 * 
+	 * <p><b>Value: List of JIDs as Strings</b></p>
+	 */
+	children_association_whitelist,
+	
+	/**
+	 * The child nodes (leaf or collection) associated with a collection
+	 * 
+	 * <p><b>Value: List of Strings</b></p>
+	 */
+	children,
+	
+	/**
+	 * The maximum number of child nodes that can be associated with a 
+	 * collection
+	 * 
+	 * <p><b>Value: int</b></p>
+	 */
+	children_max,
+	
+	/**
+	 * The maximum number of items to persist
+	 * 
+	 * <p><b>Value: int</b></p>
+	 */
+	max_items,
+	
+	/**
+	 * The maximum payload size in bytes
+	 * 
+	 * <p><b>Value: int</b></p>
+	 */
+	max_payload_size,
+	
+	/**
+	 * Whether the node is a leaf (default) or collection
+	 * 
+	 * <p><b>Value: {@link NodeType}</b></p>
+	 */
+	node_type,
+	
+	/**
+	 * Whether to notify subscribers when the node configuration changes
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	notify_config,
+	
+	/**
+	 * Whether to notify subscribers when the node is deleted
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	notify_delete,
+
+	/**
+	 * Whether to notify subscribers when items are removed from the node
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	notify_retract,
+	
+	/**
+	 * Whether to persist items to storage.  This is required to have multiple 
+	 * items in the node. 
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	persist_items,
+	
+	/**
+	 * Whether to deliver notifications to available users only
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	presence_based_delivery,
+
+	/**
+	 * Defines who can publish to the node
+	 * 
+	 * <p><b>Value: {@link PublishModel}</b></p>
+	 */
+	publish_model,
+	
+	/**
+	 * The specific multi-user chat rooms to specify for replyroom
+	 * 
+	 * <p><b>Value: List of JIDs as Strings</b></p>
+	 */
+	replyroom,
+	
+	/**
+	 * The specific JID(s) to specify for replyto
+	 * 
+	 * <p><b>Value: List of JIDs as Strings</b></p>
+	 */
+	replyto,
+	
+	/**
+	 * The roster group(s) allowed to subscribe and retrieve items
+	 * 
+	 * <p><b>Value: List of strings</b></p>
+	 */
+	roster_groups_allowed,
+	
+	/**
+	 * Whether to allow subscriptions
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	subscribe,
+	
+	/**
+	 * A friendly name for the node
+	 * 
+	 * <p><b>Value: String</b></p>
+	 */
+	title,
+	
+	/**
+	 * The type of node data, ussually specified by the namespace 
+	 * of the payload(if any);MAY be a list-single rather than a 
+	 * text single
+	 * 
+	 * <p><b>Value: String</b></p>
+	 */
+	type;
+	
+	public String getFieldName()
+	{
+		return "pubsub#" + toString();
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java
index b17a66a..a3b71e5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java
@@ -1,45 +1,45 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.List;

-

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.util.PacketParserUtils;

-

-/**

- * This interface defines {@link PacketExtension} implementations that contain other

- * extensions.  This effectively extends the idea of an extension within one of the 

- * top level {@link Packet} types to consider any embedded element to be an extension

- * of its parent.  This more easily enables the usage of some of Smacks parsing

- * utilities such as {@link PacketParserUtils#parsePacketExtension(String, String, org.xmlpull.v1.XmlPullParser)} to be used

- * to parse any element of the XML being parsed.

- * 

- * <p>Top level extensions have only one element, but they can have multiple children, or

- * their children can have multiple children.  This interface is a way of allowing extensions 

- * to be embedded within one another as a partial or complete one to one mapping of extension 

- * to element.

- * 

- * @author Robin Collier

- */

-public interface EmbeddedPacketExtension extends PacketExtension

-{

-	/**

-	 * Get the list of embedded {@link PacketExtension} objects.

-	 *  

-	 * @return List of embedded {@link PacketExtension}

-	 */

-	List<PacketExtension> getExtensions();

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.List;
+
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.PacketParserUtils;
+
+/**
+ * This interface defines {@link PacketExtension} implementations that contain other
+ * extensions.  This effectively extends the idea of an extension within one of the 
+ * top level {@link Packet} types to consider any embedded element to be an extension
+ * of its parent.  This more easily enables the usage of some of Smacks parsing
+ * utilities such as {@link PacketParserUtils#parsePacketExtension(String, String, org.xmlpull.v1.XmlPullParser)} to be used
+ * to parse any element of the XML being parsed.
+ * 
+ * <p>Top level extensions have only one element, but they can have multiple children, or
+ * their children can have multiple children.  This interface is a way of allowing extensions 
+ * to be embedded within one another as a partial or complete one to one mapping of extension 
+ * to element.
+ * 
+ * @author Robin Collier
+ */
+public interface EmbeddedPacketExtension extends PacketExtension
+{
+	/**
+	 * Get the list of embedded {@link PacketExtension} objects.
+	 *  
+	 * @return List of embedded {@link PacketExtension}
+	 */
+	List<PacketExtension> getExtensions();
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElement.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElement.java
index 165970f..e91b437 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElement.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElement.java
@@ -1,74 +1,74 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Arrays;

-import java.util.List;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-

-/**

- * Represents the top level element of a pubsub event extension.  All types of pubsub events are

- * represented by this class.  The specific type can be found by {@link #getEventType()}.  The 

- * embedded event information, which is specific to the event type, can be retrieved by the {@link #getEvent()}

- * method.

- * 

- * @author Robin Collier

- */

-public class EventElement implements EmbeddedPacketExtension

-{

-	private EventElementType type;

-	private NodeExtension ext;

-	

-	public EventElement(EventElementType eventType, NodeExtension eventExt)

-	{

-		type = eventType;

-		ext = eventExt;

-	}

-	

-	public EventElementType getEventType()

-	{

-		return type;

-	}

-

-	public List<PacketExtension> getExtensions()

-	{

-		return Arrays.asList(new PacketExtension[]{getEvent()});

-	}

-

-	public NodeExtension getEvent()

-	{

-		return ext;

-	}

-

-	public String getElementName()

-	{

-		return "event";

-	}

-

-	public String getNamespace()

-	{

-		return PubSubNamespace.EVENT.getXmlns();

-	}

-

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<event xmlns='" + PubSubNamespace.EVENT.getXmlns() + "'>");

-

-		builder.append(ext.toXML());

-		builder.append("</event>");

-		return builder.toString();

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+
+/**
+ * Represents the top level element of a pubsub event extension.  All types of pubsub events are
+ * represented by this class.  The specific type can be found by {@link #getEventType()}.  The 
+ * embedded event information, which is specific to the event type, can be retrieved by the {@link #getEvent()}
+ * method.
+ * 
+ * @author Robin Collier
+ */
+public class EventElement implements EmbeddedPacketExtension
+{
+	private EventElementType type;
+	private NodeExtension ext;
+	
+	public EventElement(EventElementType eventType, NodeExtension eventExt)
+	{
+		type = eventType;
+		ext = eventExt;
+	}
+	
+	public EventElementType getEventType()
+	{
+		return type;
+	}
+
+	public List<PacketExtension> getExtensions()
+	{
+		return Arrays.asList(new PacketExtension[]{getEvent()});
+	}
+
+	public NodeExtension getEvent()
+	{
+		return ext;
+	}
+
+	public String getElementName()
+	{
+		return "event";
+	}
+
+	public String getNamespace()
+	{
+		return PubSubNamespace.EVENT.getXmlns();
+	}
+
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<event xmlns='" + PubSubNamespace.EVENT.getXmlns() + "'>");
+
+		builder.append(ext.toXML());
+		builder.append("</event>");
+		return builder.toString();
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElementType.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElementType.java
index 343edbe..038b979 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElementType.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/EventElementType.java
@@ -1,41 +1,41 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * This enumeration defines the possible event types that are supported within pubsub

- * event messages.

- * 

- * @author Robin Collier

- */

-public enum EventElementType

-{

-	/** A node has been associated or dissassociated with a collection node */

-	collection, 

-

-	/** A node has had its configuration changed */

-	configuration, 

-	

-	/** A node has been deleted */

-	delete, 

-	

-	/** Items have been published to a node */

-	items, 

-	

-	/** All items have been purged from a node */

-	purge, 

-	

-	/** A node has been subscribed to */

-	subscription

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * This enumeration defines the possible event types that are supported within pubsub
+ * event messages.
+ * 
+ * @author Robin Collier
+ */
+public enum EventElementType
+{
+	/** A node has been associated or dissassociated with a collection node */
+	collection, 
+
+	/** A node has had its configuration changed */
+	configuration, 
+	
+	/** A node has been deleted */
+	delete, 
+	
+	/** Items have been published to a node */
+	items, 
+	
+	/** All items have been purged from a node */
+	purge, 
+	
+	/** A node has been subscribed to */
+	subscription
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNode.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNode.java
index e08bed2..992c265 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNode.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNode.java
@@ -1,99 +1,99 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.Form;

-

-/**

- * Generic packet extension which represents any pubsub form that is

- * parsed from the incoming stream or being sent out to the server.

- * 

- * Form types are defined in {@link FormNodeType}.

- * 

- * @author Robin Collier

- */

-public class FormNode extends NodeExtension

-{

-	private Form configForm;

-	

-	/**

-	 * Create a {@link FormNode} which contains the specified form.

-	 * 

-	 * @param formType The type of form being sent

-	 * @param submitForm The form

-	 */

-	public FormNode(FormNodeType formType, Form submitForm)

-	{

-		super(formType.getNodeElement());

-

-		if (submitForm == null)

-			throw new IllegalArgumentException("Submit form cannot be null");

-		configForm = submitForm;

-	}

-	

-	/**

-	 * Create a {@link FormNode} which contains the specified form, which is 

-	 * associated with the specified node.

-	 * 

-	 * @param formType The type of form being sent

-	 * @param nodeId The node the form is associated with

-	 * @param submitForm The form

-	 */

-	public FormNode(FormNodeType formType, String nodeId, Form submitForm)

-	{

-		super(formType.getNodeElement(), nodeId);

-

-		if (submitForm == null)

-			throw new IllegalArgumentException("Submit form cannot be null");

-		configForm = submitForm;

-	}

-	

-	/**

-	 * Get the Form that is to be sent, or was retrieved from the server.

-	 * 

-	 * @return The form

-	 */

-	public Form getForm()

-	{

-		return configForm;

-	}

-	

-	@Override

-	public String toXML()

-	{

-		if (configForm == null)

-		{

-			return super.toXML();

-		}

-		else

-		{

-			StringBuilder builder = new StringBuilder("<");

-			builder.append(getElementName());

-			

-			if (getNode() != null)

-			{

-				builder.append(" node='");

-				builder.append(getNode());

-				builder.append("'>");

-			}

-			else

-				builder.append('>');

-			builder.append(configForm.getDataFormToSend().toXML());

-			builder.append("</");

-			builder.append(getElementName() + '>');

-			return builder.toString();

-		}

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.Form;
+
+/**
+ * Generic packet extension which represents any pubsub form that is
+ * parsed from the incoming stream or being sent out to the server.
+ * 
+ * Form types are defined in {@link FormNodeType}.
+ * 
+ * @author Robin Collier
+ */
+public class FormNode extends NodeExtension
+{
+	private Form configForm;
+	
+	/**
+	 * Create a {@link FormNode} which contains the specified form.
+	 * 
+	 * @param formType The type of form being sent
+	 * @param submitForm The form
+	 */
+	public FormNode(FormNodeType formType, Form submitForm)
+	{
+		super(formType.getNodeElement());
+
+		if (submitForm == null)
+			throw new IllegalArgumentException("Submit form cannot be null");
+		configForm = submitForm;
+	}
+	
+	/**
+	 * Create a {@link FormNode} which contains the specified form, which is 
+	 * associated with the specified node.
+	 * 
+	 * @param formType The type of form being sent
+	 * @param nodeId The node the form is associated with
+	 * @param submitForm The form
+	 */
+	public FormNode(FormNodeType formType, String nodeId, Form submitForm)
+	{
+		super(formType.getNodeElement(), nodeId);
+
+		if (submitForm == null)
+			throw new IllegalArgumentException("Submit form cannot be null");
+		configForm = submitForm;
+	}
+	
+	/**
+	 * Get the Form that is to be sent, or was retrieved from the server.
+	 * 
+	 * @return The form
+	 */
+	public Form getForm()
+	{
+		return configForm;
+	}
+	
+	@Override
+	public String toXML()
+	{
+		if (configForm == null)
+		{
+			return super.toXML();
+		}
+		else
+		{
+			StringBuilder builder = new StringBuilder("<");
+			builder.append(getElementName());
+			
+			if (getNode() != null)
+			{
+				builder.append(" node='");
+				builder.append(getNode());
+				builder.append("'>");
+			}
+			else
+				builder.append('>');
+			builder.append(configForm.getDataFormToSend().toXML());
+			builder.append("</");
+			builder.append(getElementName() + '>');
+			return builder.toString();
+		}
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNodeType.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNodeType.java
index 6a163ee..48a600a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNodeType.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormNodeType.java
@@ -1,50 +1,50 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-

-/**

- * The types of forms supported by the pubsub specification.

- * 

- * @author Robin Collier

- */

-public enum FormNodeType

-{

-	/** Form for configuring an existing node */

-	CONFIGURE_OWNER,

-	

-	/** Form for configuring a node during creation */

-	CONFIGURE,

-	

-	/** Form for configuring subscription options */

-	OPTIONS,

-

-	/** Form which represents the default node configuration options */

-	DEFAULT;

-	

-	public PubSubElementType getNodeElement()

-	{

-		return PubSubElementType.valueOf(toString());

-	}

-

-	public static FormNodeType valueOfFromElementName(String elem, String configNamespace)

-	{

-		if ("configure".equals(elem) && PubSubNamespace.OWNER.getXmlns().equals(configNamespace))

-		{

-			return CONFIGURE_OWNER;

-		}

-		return valueOf(elem.toUpperCase());

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+
+/**
+ * The types of forms supported by the pubsub specification.
+ * 
+ * @author Robin Collier
+ */
+public enum FormNodeType
+{
+	/** Form for configuring an existing node */
+	CONFIGURE_OWNER,
+	
+	/** Form for configuring a node during creation */
+	CONFIGURE,
+	
+	/** Form for configuring subscription options */
+	OPTIONS,
+
+	/** Form which represents the default node configuration options */
+	DEFAULT;
+	
+	public PubSubElementType getNodeElement()
+	{
+		return PubSubElementType.valueOf(toString());
+	}
+
+	public static FormNodeType valueOfFromElementName(String elem, String configNamespace)
+	{
+		if ("configure".equals(elem) && PubSubNamespace.OWNER.getXmlns().equals(configNamespace))
+		{
+			return CONFIGURE_OWNER;
+		}
+		return valueOf(elem.toUpperCase());
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormType.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormType.java
index e0fff51..f4df255 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormType.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/FormType.java
@@ -1,26 +1,26 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.Form;

-

-/**

- * Defines the allowable types for a {@link Form}

- * 

- * @author Robin Collier

- */

-public enum FormType

-{

-    form, submit, cancel, result;

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.Form;
+
+/**
+ * Defines the allowable types for a {@link Form}
+ * 
+ * @author Robin Collier
+ */
+public enum FormType
+{
+    form, submit, cancel, result;
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/GetItemsRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/GetItemsRequest.java
index 0935571..341b7b5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/GetItemsRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/GetItemsRequest.java
@@ -1,85 +1,85 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * Represents a request to subscribe to a node.

- * 

- * @author Robin Collier

- */

-public class GetItemsRequest extends NodeExtension

-{

-	protected String subId;

-	protected int maxItems;

-	

-	public GetItemsRequest(String nodeId)

-	{

-		super(PubSubElementType.ITEMS, nodeId);

-	}

-	

-	public GetItemsRequest(String nodeId, String subscriptionId)

-	{

-		super(PubSubElementType.ITEMS, nodeId);

-		subId = subscriptionId;

-	}

-

-	public GetItemsRequest(String nodeId, int maxItemsToReturn)

-	{

-		super(PubSubElementType.ITEMS, nodeId);

-		maxItems = maxItemsToReturn;

-	}

-

-	public GetItemsRequest(String nodeId, String subscriptionId, int maxItemsToReturn)

-	{

-		this(nodeId, maxItemsToReturn);

-		subId = subscriptionId;

-	}

-

-	public String getSubscriptionId()

-	{

-		return subId;

-	}

-

-	public int getMaxItems()

-	{

-		return maxItems;

-	}

-

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		

-		builder.append(" node='");

-		builder.append(getNode());

-		builder.append("'");

-

-		if (getSubscriptionId() != null)

-		{

-			builder.append(" subid='");

-			builder.append(getSubscriptionId());

-			builder.append("'");

-		}

-

-		if (getMaxItems() > 0)

-		{

-			builder.append(" max_items='");

-			builder.append(getMaxItems());

-			builder.append("'");

-		}

-		builder.append("/>");

-		return builder.toString();

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * Represents a request to subscribe to a node.
+ * 
+ * @author Robin Collier
+ */
+public class GetItemsRequest extends NodeExtension
+{
+	protected String subId;
+	protected int maxItems;
+	
+	public GetItemsRequest(String nodeId)
+	{
+		super(PubSubElementType.ITEMS, nodeId);
+	}
+	
+	public GetItemsRequest(String nodeId, String subscriptionId)
+	{
+		super(PubSubElementType.ITEMS, nodeId);
+		subId = subscriptionId;
+	}
+
+	public GetItemsRequest(String nodeId, int maxItemsToReturn)
+	{
+		super(PubSubElementType.ITEMS, nodeId);
+		maxItems = maxItemsToReturn;
+	}
+
+	public GetItemsRequest(String nodeId, String subscriptionId, int maxItemsToReturn)
+	{
+		this(nodeId, maxItemsToReturn);
+		subId = subscriptionId;
+	}
+
+	public String getSubscriptionId()
+	{
+		return subId;
+	}
+
+	public int getMaxItems()
+	{
+		return maxItems;
+	}
+
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		
+		builder.append(" node='");
+		builder.append(getNode());
+		builder.append("'");
+
+		if (getSubscriptionId() != null)
+		{
+			builder.append(" subid='");
+			builder.append(getSubscriptionId());
+			builder.append("'");
+		}
+
+		if (getMaxItems() > 0)
+		{
+			builder.append(" max_items='");
+			builder.append(getMaxItems());
+			builder.append("'");
+		}
+		builder.append("/>");
+		return builder.toString();
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Item.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Item.java
index 2ce0baa..8e897c7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Item.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Item.java
@@ -1,132 +1,132 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.packet.Message;

-import org.jivesoftware.smackx.pubsub.provider.ItemProvider;

-

-/**

- * This class represents an item that has been, or will be published to a

- * pubsub node.  An <tt>Item</tt> has several properties that are dependent

- * on the configuration of the node to which it has been or will be published.

- * 

- * <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>

- * <li>Will always have an id (either user or server generated) unless node configuration has both

- * {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.

- * <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set 

- * to true, otherwise it will be null.

- * 

- * <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>

- * <li>The id is optional, since the server will generate one if necessary, but should be used if it is 

- * meaningful in the context of the node.  This value must be unique within the node that it is sent to, since

- * resending an item with the same id will overwrite the one that already exists if the items are persisted.

- * <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set

- * to true. 

- * 

- * <p>To customise the payload object being returned from the {@link #getPayload()} method, you can

- * add a custom parser as explained in {@link ItemProvider}.

- * 

- * @author Robin Collier

- */

-public class Item extends NodeExtension

-{

-	private String id;

-	

-	/**

-	 * Create an empty <tt>Item</tt> with no id.  This is a valid item for nodes which are configured

-	 * so that {@link ConfigureForm#isDeliverPayloads()} is false.  In most cases an id will be generated by the server.

-	 * For nodes configured with {@link ConfigureForm#isDeliverPayloads()} and {@link ConfigureForm#isPersistItems()} 

-	 * set to false, no <tt>Item</tt> is sent to the node, you have to use {@link LeafNode#send()} or {@link LeafNode#publish()}

-	 * methods in this case. 

-	 */

-	public Item()

-	{

-		super(PubSubElementType.ITEM);

-	}

-	

-	/**

-	 * Create an <tt>Item</tt> with an id but no payload.  This is a valid item for nodes which are configured

-	 * so that {@link ConfigureForm#isDeliverPayloads()} is false.

-	 * 

-	 * @param itemId The id if the item.  It must be unique within the node unless overwriting and existing item.

-	 * Passing null is the equivalent of calling {@link #Item()}.

-	 */

-	public Item(String itemId)

-	{

-		// The element type is actually irrelevant since we override getNamespace() to return null

-		super(PubSubElementType.ITEM);

-		id = itemId;

-	}

-

-	/**

-	 * Create an <tt>Item</tt> with an id and a node id.  

-	 * <p>

-	 * <b>Note:</b> This is not valid for publishing an item to a node, only receiving from 

-	 * one as part of {@link Message}.  If used to create an Item to publish 

-	 * (via {@link LeafNode#publish(Item)}, the server <i>may</i> return an

-	 * error for an invalid packet.

-	 * 

-	 * @param itemId The id of the item.

-	 * @param nodeId The id of the node which the item was published to.

-	 */

-    public Item(String itemId, String nodeId) 

-    {

-    	super(PubSubElementType.ITEM_EVENT, nodeId);

-        id = itemId;

-    }

-	

-	/**

-	 * Get the item id.  Unique to the node it is associated with.

-	 * 

-	 * @return The id

-	 */

-	public String getId()

-	{

-		return id;

-	}

-	

-	@Override

-	public String getNamespace()

-	{

-		return null;

-	}

-

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<item");

-		

-		if (id != null)

-		{

-			builder.append(" id='");

-			builder.append(id);

-			builder.append("'");

-		}

-		

-        if (getNode() != null) {

-            builder.append(" node='");

-            builder.append(getNode());

-            builder.append("'");

-        }

-		builder.append("/>");

-

-		return builder.toString();

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + " | Content [" + toXML() + "]";

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
+
+/**
+ * This class represents an item that has been, or will be published to a
+ * pubsub node.  An <tt>Item</tt> has several properties that are dependent
+ * on the configuration of the node to which it has been or will be published.
+ * 
+ * <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>
+ * <li>Will always have an id (either user or server generated) unless node configuration has both
+ * {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.
+ * <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set 
+ * to true, otherwise it will be null.
+ * 
+ * <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>
+ * <li>The id is optional, since the server will generate one if necessary, but should be used if it is 
+ * meaningful in the context of the node.  This value must be unique within the node that it is sent to, since
+ * resending an item with the same id will overwrite the one that already exists if the items are persisted.
+ * <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
+ * to true. 
+ * 
+ * <p>To customise the payload object being returned from the {@link #getPayload()} method, you can
+ * add a custom parser as explained in {@link ItemProvider}.
+ * 
+ * @author Robin Collier
+ */
+public class Item extends NodeExtension
+{
+	private String id;
+	
+	/**
+	 * Create an empty <tt>Item</tt> with no id.  This is a valid item for nodes which are configured
+	 * so that {@link ConfigureForm#isDeliverPayloads()} is false.  In most cases an id will be generated by the server.
+	 * For nodes configured with {@link ConfigureForm#isDeliverPayloads()} and {@link ConfigureForm#isPersistItems()} 
+	 * set to false, no <tt>Item</tt> is sent to the node, you have to use {@link LeafNode#send()} or {@link LeafNode#publish()}
+	 * methods in this case. 
+	 */
+	public Item()
+	{
+		super(PubSubElementType.ITEM);
+	}
+	
+	/**
+	 * Create an <tt>Item</tt> with an id but no payload.  This is a valid item for nodes which are configured
+	 * so that {@link ConfigureForm#isDeliverPayloads()} is false.
+	 * 
+	 * @param itemId The id if the item.  It must be unique within the node unless overwriting and existing item.
+	 * Passing null is the equivalent of calling {@link #Item()}.
+	 */
+	public Item(String itemId)
+	{
+		// The element type is actually irrelevant since we override getNamespace() to return null
+		super(PubSubElementType.ITEM);
+		id = itemId;
+	}
+
+	/**
+	 * Create an <tt>Item</tt> with an id and a node id.  
+	 * <p>
+	 * <b>Note:</b> This is not valid for publishing an item to a node, only receiving from 
+	 * one as part of {@link Message}.  If used to create an Item to publish 
+	 * (via {@link LeafNode#publish(Item)}, the server <i>may</i> return an
+	 * error for an invalid packet.
+	 * 
+	 * @param itemId The id of the item.
+	 * @param nodeId The id of the node which the item was published to.
+	 */
+    public Item(String itemId, String nodeId) 
+    {
+    	super(PubSubElementType.ITEM_EVENT, nodeId);
+        id = itemId;
+    }
+	
+	/**
+	 * Get the item id.  Unique to the node it is associated with.
+	 * 
+	 * @return The id
+	 */
+	public String getId()
+	{
+		return id;
+	}
+	
+	@Override
+	public String getNamespace()
+	{
+		return null;
+	}
+
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<item");
+		
+		if (id != null)
+		{
+			builder.append(" id='");
+			builder.append(id);
+			builder.append("'");
+		}
+		
+        if (getNode() != null) {
+            builder.append(" node='");
+            builder.append(getNode());
+            builder.append("'");
+        }
+		builder.append("/>");
+
+		return builder.toString();
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + " | Content [" + toXML() + "]";
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java
index 82ab7df..b26dd6f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java
@@ -1,62 +1,62 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Collections;

-import java.util.List;

-

-/**

- * Represents an event in which items have been deleted from the node.

- * 

- * @author Robin Collier

- */

-public class ItemDeleteEvent extends SubscriptionEvent

-{

-	private List<String> itemIds = Collections.EMPTY_LIST;

-	

-	/**

-	 * Constructs an <tt>ItemDeleteEvent</tt> that indicates the the supplied

-	 * items (by id) have been deleted, and that the event matches the listed

-	 * subscriptions.  The subscriptions would have been created by calling 

-	 * {@link LeafNode#subscribe(String)}.

-	 * 

-	 * @param nodeId The id of the node the event came from

-	 * @param deletedItemIds The item ids of the items that were deleted.

-	 * @param subscriptionIds The subscriptions that match the event.

-	 */

-	public ItemDeleteEvent(String nodeId, List<String> deletedItemIds, List<String> subscriptionIds)

-	{

-		super(nodeId, subscriptionIds);

-		

-		if (deletedItemIds == null)

-			throw new IllegalArgumentException("deletedItemIds cannot be null");

-		itemIds = deletedItemIds;

-	}

-	

-	/**

-	 * Get the item id's of the items that have been deleted.

-	 * 

-	 * @return List of item id's

-	 */

-	public List<String> getItemIds()

-	{

-		return Collections.unmodifiableList(itemIds);

-	}

-	

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + "  [subscriptions: " + getSubscriptions() + "], [Deleted Items: " + itemIds + ']';

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents an event in which items have been deleted from the node.
+ * 
+ * @author Robin Collier
+ */
+public class ItemDeleteEvent extends SubscriptionEvent
+{
+	private List<String> itemIds = Collections.EMPTY_LIST;
+	
+	/**
+	 * Constructs an <tt>ItemDeleteEvent</tt> that indicates the the supplied
+	 * items (by id) have been deleted, and that the event matches the listed
+	 * subscriptions.  The subscriptions would have been created by calling 
+	 * {@link LeafNode#subscribe(String)}.
+	 * 
+	 * @param nodeId The id of the node the event came from
+	 * @param deletedItemIds The item ids of the items that were deleted.
+	 * @param subscriptionIds The subscriptions that match the event.
+	 */
+	public ItemDeleteEvent(String nodeId, List<String> deletedItemIds, List<String> subscriptionIds)
+	{
+		super(nodeId, subscriptionIds);
+		
+		if (deletedItemIds == null)
+			throw new IllegalArgumentException("deletedItemIds cannot be null");
+		itemIds = deletedItemIds;
+	}
+	
+	/**
+	 * Get the item id's of the items that have been deleted.
+	 * 
+	 * @return List of item id's
+	 */
+	public List<String> getItemIds()
+	{
+		return Collections.unmodifiableList(itemIds);
+	}
+	
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + "  [subscriptions: " + getSubscriptions() + "], [Deleted Items: " + itemIds + ']';
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java
index 1ef1f67..7c0574c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java
@@ -1,123 +1,123 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Collections;

-import java.util.Date;

-import java.util.List;

-

-/**

- * Represents an event generated by an item(s) being published to a node.

- * 

- * @author Robin Collier

- */

-public class ItemPublishEvent <T extends Item> extends SubscriptionEvent

-{

-	private List<T> items;

-	private Date originalDate;

-	

-	/**

-	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list

-	 * of {@link Item} that were published.

-	 * 

-	 * @param nodeId The id of the node the event came from

-	 * @param eventItems The list of {@link Item} that were published 

-	 */

-	public ItemPublishEvent(String nodeId, List<T> eventItems)

-	{

-		super(nodeId);

-		items = eventItems;

-	}

-	

-	/**

-	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list

-	 * of {@link Item} that were published.  The list of subscription ids

-	 * represents the subscriptions that matched the event, in the case 

-	 * of the user having multiple subscriptions.

-	 * 

-	 * @param nodeId The id of the node the event came from

-	 * @param eventItems The list of {@link Item} that were published 

-	 * @param subscriptionIds The list of subscriptionIds

-	 */

-	public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds)

-	{

-		super(nodeId, subscriptionIds);

-		items = eventItems;

-	}

-	

-	/**

-	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list

-	 * of {@link Item} that were published in the past.  The published

-	 * date signifies that this is delayed event.  The list of subscription ids

-	 * represents the subscriptions that matched the event, in the case 

-	 * of the user having multiple subscriptions. 

-	 *

-	 * @param nodeId The id of the node the event came from

-	 * @param eventItems The list of {@link Item} that were published 

-	 * @param subscriptionIds The list of subscriptionIds

-	 * @param publishedDate The original publishing date of the events

-	 */

-	public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds, Date publishedDate)

-	{

-		super(nodeId, subscriptionIds);

-		items = eventItems;

-		

-		if (publishedDate != null)

-			originalDate = publishedDate;

-	}

-	

-	/**

-	 * Get the list of {@link Item} that were published.

-	 * 

-	 * @return The list of published {@link Item}

-	 */

-	public List<T> getItems()

-	{

-		return Collections.unmodifiableList(items);

-	}

-	

-	/**

-	 * Indicates whether this event was delayed.  That is, the items

-	 * were published to the node at some time in the past.  This will 

-	 * typically happen if there is an item already published to the node

-	 * before a user subscribes to it.  In this case, when the user 

-	 * subscribes, the server may send the last item published to the node

-	 * with a delay date showing its time of original publication.

-	 * 

-	 * @return true if the items are delayed, false otherwise.

-	 */

-	public boolean isDelayed()

-	{

-		return (originalDate != null);

-	}

-	

-	/**

-	 * Gets the original date the items were published.  This is only 

-	 * valid if {@link #isDelayed()} is true.

-	 * 

-	 * @return Date items were published if {@link #isDelayed()} is true, null otherwise.

-	 */

-	public Date getPublishedDate()

-	{

-		return originalDate;

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + "  [subscriptions: " + getSubscriptions() + "], [Delayed: " + 

-			(isDelayed() ? originalDate.toString() : "false") + ']';

-	}

-	

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Represents an event generated by an item(s) being published to a node.
+ * 
+ * @author Robin Collier
+ */
+public class ItemPublishEvent <T extends Item> extends SubscriptionEvent
+{
+	private List<T> items;
+	private Date originalDate;
+	
+	/**
+	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list
+	 * of {@link Item} that were published.
+	 * 
+	 * @param nodeId The id of the node the event came from
+	 * @param eventItems The list of {@link Item} that were published 
+	 */
+	public ItemPublishEvent(String nodeId, List<T> eventItems)
+	{
+		super(nodeId);
+		items = eventItems;
+	}
+	
+	/**
+	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list
+	 * of {@link Item} that were published.  The list of subscription ids
+	 * represents the subscriptions that matched the event, in the case 
+	 * of the user having multiple subscriptions.
+	 * 
+	 * @param nodeId The id of the node the event came from
+	 * @param eventItems The list of {@link Item} that were published 
+	 * @param subscriptionIds The list of subscriptionIds
+	 */
+	public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds)
+	{
+		super(nodeId, subscriptionIds);
+		items = eventItems;
+	}
+	
+	/**
+	 * Constructs an <tt>ItemPublishEvent</tt> with the provided list
+	 * of {@link Item} that were published in the past.  The published
+	 * date signifies that this is delayed event.  The list of subscription ids
+	 * represents the subscriptions that matched the event, in the case 
+	 * of the user having multiple subscriptions. 
+	 *
+	 * @param nodeId The id of the node the event came from
+	 * @param eventItems The list of {@link Item} that were published 
+	 * @param subscriptionIds The list of subscriptionIds
+	 * @param publishedDate The original publishing date of the events
+	 */
+	public ItemPublishEvent(String nodeId, List<T> eventItems, List<String> subscriptionIds, Date publishedDate)
+	{
+		super(nodeId, subscriptionIds);
+		items = eventItems;
+		
+		if (publishedDate != null)
+			originalDate = publishedDate;
+	}
+	
+	/**
+	 * Get the list of {@link Item} that were published.
+	 * 
+	 * @return The list of published {@link Item}
+	 */
+	public List<T> getItems()
+	{
+		return Collections.unmodifiableList(items);
+	}
+	
+	/**
+	 * Indicates whether this event was delayed.  That is, the items
+	 * were published to the node at some time in the past.  This will 
+	 * typically happen if there is an item already published to the node
+	 * before a user subscribes to it.  In this case, when the user 
+	 * subscribes, the server may send the last item published to the node
+	 * with a delay date showing its time of original publication.
+	 * 
+	 * @return true if the items are delayed, false otherwise.
+	 */
+	public boolean isDelayed()
+	{
+		return (originalDate != null);
+	}
+	
+	/**
+	 * Gets the original date the items were published.  This is only 
+	 * valid if {@link #isDelayed()} is true.
+	 * 
+	 * @return Date items were published if {@link #isDelayed()} is true, null otherwise.
+	 */
+	public Date getPublishedDate()
+	{
+		return originalDate;
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + "  [subscriptions: " + getSubscriptions() + "], [Delayed: " + 
+			(isDelayed() ? originalDate.toString() : "false") + ']';
+	}
+	
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemReply.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemReply.java
index 3e090d9..99a0827 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemReply.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemReply.java
@@ -1,29 +1,29 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * These are the options for the node configuration setting {@link ConfigureForm#setItemReply(ItemReply)},

- * which defines who should receive replies to items.

- * 

- * @author Robin Collier

- */

-public enum ItemReply

-{

-	/** The node owner */

-	owner,

-	

-	/** The item publisher */

-	publisher;

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * These are the options for the node configuration setting {@link ConfigureForm#setItemReply(ItemReply)},
+ * which defines who should receive replies to items.
+ * 
+ * @author Robin Collier
+ */
+public enum ItemReply
+{
+	/** The node owner */
+	owner,
+	
+	/** The item publisher */
+	publisher;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemsExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemsExtension.java
index c98d93a..855ed06 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemsExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/ItemsExtension.java
@@ -1,196 +1,196 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.List;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * This class is used to for multiple purposes.  

- * <li>It can represent an event containing a list of items that have been published

- * <li>It can represent an event containing a list of retracted (deleted) items.

- * <li>It can represent a request to delete a list of items.

- * <li>It can represent a request to get existing items.

- * 

- * <p><b>Please note, this class is used for internal purposes, and is not required for usage of 

- * pubsub functionality.</b>

- * 

- * @author Robin Collier

- */

-public class ItemsExtension extends NodeExtension implements EmbeddedPacketExtension

-{

-	protected ItemsElementType type;

-	protected Boolean notify;

-	protected List<? extends PacketExtension> items;

-

-	public enum ItemsElementType

-	{

-		/** An items element, which has an optional <b>max_items</b> attribute when requesting items */

-		items(PubSubElementType.ITEMS, "max_items"),

-		

-		/** A retract element, which has an optional <b>notify</b> attribute when publishing deletions */

-		retract(PubSubElementType.RETRACT, "notify");

-		

-		private PubSubElementType elem;

-		private String att;

-		

-		private ItemsElementType(PubSubElementType nodeElement, String attribute)

-		{

-			elem = nodeElement;

-			att = attribute;

-		}

-		

-		public PubSubElementType getNodeElement()

-		{

-			return elem;

-		}

-

-		public String getElementAttribute()

-		{

-			return att;

-		}

-	}

-

-	/**

-	 * Construct an instance with a list representing items that have been published or deleted.

-	 * 

-	 * <p>Valid scenarios are:

-	 * <li>Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an

-	 * optional value for the <b>max_items</b> attribute.

-	 * <li>Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing

-	 * only id's and an optional value for the <b>notify</b> attribute.

-	 * <li>Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and 

-	 * attributeValue = <code>null</code>

-	 * <li>Items deleted event -  itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and 

-	 * attributeValue = <code>null</code> 

-	 * 

-	 * @param itemsType Type of representation

-	 * @param nodeId The node to which the items are being sent or deleted

-	 * @param items The list of {@link Item} or {@link RetractItem}

-	 * @param attributeValue The value of the <b>max_items</b>  

-	 */

-	public ItemsExtension(ItemsElementType itemsType, String nodeId, List<? extends PacketExtension> items)

-	{

-		super(itemsType.getNodeElement(), nodeId);

-		type = itemsType;

-		this.items = items;

-	}

-	

-	/**

-	 * Construct an instance with a list representing items that have been published or deleted.

-	 * 

-	 * <p>Valid scenarios are:

-	 * <li>Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an

-	 * optional value for the <b>max_items</b> attribute.

-	 * <li>Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing

-	 * only id's and an optional value for the <b>notify</b> attribute.

-	 * <li>Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and 

-	 * attributeValue = <code>null</code>

-	 * <li>Items deleted event -  itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and 

-	 * attributeValue = <code>null</code> 

-	 * 

-	 * @param itemsType Type of representation

-	 * @param nodeId The node to which the items are being sent or deleted

-	 * @param items The list of {@link Item} or {@link RetractItem}

-	 * @param attributeValue The value of the <b>max_items</b>  

-	 */

-	public ItemsExtension(String nodeId, List<? extends PacketExtension> items, boolean notify)

-	{

-		super(ItemsElementType.retract.getNodeElement(), nodeId);

-		type = ItemsElementType.retract;

-		this.items = items; 

-		this.notify = notify;

-	}

-	

-	/**

-	 * Get the type of element

-	 * 

-	 * @return The element type

-	 */

-	public ItemsElementType getItemsElementType()

-	{

-		return type;

-	}

-	

-	public List<PacketExtension> getExtensions()

-	{

-		return (List<PacketExtension>)getItems();

-	}

-	

-	/**

-	 * Gets the items related to the type of request or event.

-	 * 

-	 * return List of {@link Item}, {@link RetractItem}, or null

-	 */

-	public List<? extends PacketExtension> getItems()

-	{

-		return items;

-	}

-

-	/**

-	 * Gets the value of the optional attribute related to the {@link ItemsElementType}.

-	 * 

-	 * @return The attribute value

-	 */

-	public boolean getNotify()

-	{

-		return notify;

-	}

-	

-	@Override

-	public String toXML()

-	{

-		if ((items == null) || (items.size() == 0))

-		{

-			return super.toXML();

-		}

-		else

-		{

-			StringBuilder builder = new StringBuilder("<");

-			builder.append(getElementName());

-			builder.append(" node='");

-			builder.append(getNode());

-			

-			if (notify != null)

-			{

-				builder.append("' ");

-				builder.append(type.getElementAttribute());

-				builder.append("='");

-				builder.append(notify.equals(Boolean.TRUE) ? 1 : 0);

-				builder.append("'>");

-			}

-			else

-			{

-				builder.append("'>");

-				for (PacketExtension item : items)

-				{

-					builder.append(item.toXML());

-				}

-			}

-			

-			builder.append("</");

-			builder.append(getElementName());

-			builder.append(">");

-			return builder.toString();

-		}

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + "Content [" + toXML() + "]";

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.List;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * This class is used to for multiple purposes.  
+ * <li>It can represent an event containing a list of items that have been published
+ * <li>It can represent an event containing a list of retracted (deleted) items.
+ * <li>It can represent a request to delete a list of items.
+ * <li>It can represent a request to get existing items.
+ * 
+ * <p><b>Please note, this class is used for internal purposes, and is not required for usage of 
+ * pubsub functionality.</b>
+ * 
+ * @author Robin Collier
+ */
+public class ItemsExtension extends NodeExtension implements EmbeddedPacketExtension
+{
+	protected ItemsElementType type;
+	protected Boolean notify;
+	protected List<? extends PacketExtension> items;
+
+	public enum ItemsElementType
+	{
+		/** An items element, which has an optional <b>max_items</b> attribute when requesting items */
+		items(PubSubElementType.ITEMS, "max_items"),
+		
+		/** A retract element, which has an optional <b>notify</b> attribute when publishing deletions */
+		retract(PubSubElementType.RETRACT, "notify");
+		
+		private PubSubElementType elem;
+		private String att;
+		
+		private ItemsElementType(PubSubElementType nodeElement, String attribute)
+		{
+			elem = nodeElement;
+			att = attribute;
+		}
+		
+		public PubSubElementType getNodeElement()
+		{
+			return elem;
+		}
+
+		public String getElementAttribute()
+		{
+			return att;
+		}
+	}
+
+	/**
+	 * Construct an instance with a list representing items that have been published or deleted.
+	 * 
+	 * <p>Valid scenarios are:
+	 * <li>Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an
+	 * optional value for the <b>max_items</b> attribute.
+	 * <li>Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing
+	 * only id's and an optional value for the <b>notify</b> attribute.
+	 * <li>Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and 
+	 * attributeValue = <code>null</code>
+	 * <li>Items deleted event -  itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and 
+	 * attributeValue = <code>null</code> 
+	 * 
+	 * @param itemsType Type of representation
+	 * @param nodeId The node to which the items are being sent or deleted
+	 * @param items The list of {@link Item} or {@link RetractItem}
+	 * @param attributeValue The value of the <b>max_items</b>  
+	 */
+	public ItemsExtension(ItemsElementType itemsType, String nodeId, List<? extends PacketExtension> items)
+	{
+		super(itemsType.getNodeElement(), nodeId);
+		type = itemsType;
+		this.items = items;
+	}
+	
+	/**
+	 * Construct an instance with a list representing items that have been published or deleted.
+	 * 
+	 * <p>Valid scenarios are:
+	 * <li>Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an
+	 * optional value for the <b>max_items</b> attribute.
+	 * <li>Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing
+	 * only id's and an optional value for the <b>notify</b> attribute.
+	 * <li>Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and 
+	 * attributeValue = <code>null</code>
+	 * <li>Items deleted event -  itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and 
+	 * attributeValue = <code>null</code> 
+	 * 
+	 * @param itemsType Type of representation
+	 * @param nodeId The node to which the items are being sent or deleted
+	 * @param items The list of {@link Item} or {@link RetractItem}
+	 * @param attributeValue The value of the <b>max_items</b>  
+	 */
+	public ItemsExtension(String nodeId, List<? extends PacketExtension> items, boolean notify)
+	{
+		super(ItemsElementType.retract.getNodeElement(), nodeId);
+		type = ItemsElementType.retract;
+		this.items = items; 
+		this.notify = notify;
+	}
+	
+	/**
+	 * Get the type of element
+	 * 
+	 * @return The element type
+	 */
+	public ItemsElementType getItemsElementType()
+	{
+		return type;
+	}
+	
+	public List<PacketExtension> getExtensions()
+	{
+		return (List<PacketExtension>)getItems();
+	}
+	
+	/**
+	 * Gets the items related to the type of request or event.
+	 * 
+	 * return List of {@link Item}, {@link RetractItem}, or null
+	 */
+	public List<? extends PacketExtension> getItems()
+	{
+		return items;
+	}
+
+	/**
+	 * Gets the value of the optional attribute related to the {@link ItemsElementType}.
+	 * 
+	 * @return The attribute value
+	 */
+	public boolean getNotify()
+	{
+		return notify;
+	}
+	
+	@Override
+	public String toXML()
+	{
+		if ((items == null) || (items.size() == 0))
+		{
+			return super.toXML();
+		}
+		else
+		{
+			StringBuilder builder = new StringBuilder("<");
+			builder.append(getElementName());
+			builder.append(" node='");
+			builder.append(getNode());
+			
+			if (notify != null)
+			{
+				builder.append("' ");
+				builder.append(type.getElementAttribute());
+				builder.append("='");
+				builder.append(notify.equals(Boolean.TRUE) ? 1 : 0);
+				builder.append("'>");
+			}
+			else
+			{
+				builder.append("'>");
+				for (PacketExtension item : items)
+				{
+					builder.append(item.toXML());
+				}
+			}
+			
+			builder.append("</");
+			builder.append(getElementName());
+			builder.append(">");
+			return builder.toString();
+		}
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + "Content [" + toXML() + "]";
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/LeafNode.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/LeafNode.java
index eee6293..96ef515 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/LeafNode.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/LeafNode.java
@@ -1,352 +1,352 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.List;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ.Type;

-import org.jivesoftware.smackx.packet.DiscoverItems;

-import org.jivesoftware.smackx.pubsub.packet.PubSub;

-import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;

-

-/**

- * The main class for the majority of pubsub functionality.  In general

- * almost all pubsub capabilities are related to the concept of a node.

- * All items are published to a node, and typically subscribed to by other

- * users.  These users then retrieve events based on this subscription.

- * 

- * @author Robin Collier

- */

-public class LeafNode extends Node

-{

-	LeafNode(Connection connection, String nodeName)

-	{

-		super(connection, nodeName);

-	}

-	

-	/**

-	 * Get information on the items in the node in standard

-	 * {@link DiscoverItems} format.

-	 * 

-	 * @return The item details in {@link DiscoverItems} format

-	 * 

-	 * @throws XMPPException

-	 */

-	public DiscoverItems discoverItems()

-		throws XMPPException

-	{

-		DiscoverItems items = new DiscoverItems();

-		items.setTo(to);

-		items.setNode(getId());

-		return (DiscoverItems)SyncPacketSend.getReply(con, items);

-	}

-

-	/**

-	 * Get the current items stored in the node.

-	 * 

-	 * @return List of {@link Item} in the node

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> List<T> getItems()

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId()));

-		

-		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);

-		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);

-		return (List<T>)itemsElem.getItems();

-	}

-	

-	/**

-	 * Get the current items stored in the node based

-	 * on the subscription associated with the provided 

-	 * subscription id.

-	 * 

-	 * @param subscriptionId -  The subscription id for the 

-	 * associated subscription.

-	 * @return List of {@link Item} in the node

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> List<T> getItems(String subscriptionId)

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId));

-		

-		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);

-		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);

-		return (List<T>)itemsElem.getItems();

-	}

-

-	/**

-	 * Get the items specified from the node.  This would typically be

-	 * used when the server does not return the payload due to size 

-	 * constraints.  The user would be required to retrieve the payload 

-	 * after the items have been retrieved via {@link #getItems()} or an

-	 * event, that did not include the payload.

-	 * 

-	 * @param ids Item ids of the items to retrieve

-	 * 

-	 * @return The list of {@link Item} with payload

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> List<T> getItems(Collection<String> ids)

-		throws XMPPException

-	{

-		List<Item> itemList = new ArrayList<Item>(ids.size());

-		

-		for (String id : ids)

-		{

-			itemList.add(new Item(id));

-		}

-		PubSub request = createPubsubPacket(Type.GET, new ItemsExtension(ItemsExtension.ItemsElementType.items, getId(), itemList));

-		

-		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);

-		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);

-		return (List<T>)itemsElem.getItems();

-	}

-

-	/**

-	 * Get items persisted on the node, limited to the specified number.

-	 * 

-	 * @param maxItems Maximum number of items to return

-	 * 

-	 * @return List of {@link Item}

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> List<T> getItems(int maxItems)

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), maxItems));

-		

-		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);

-		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);

-		return (List<T>)itemsElem.getItems();

-	}

-	

-	/**

-	 * Get items persisted on the node, limited to the specified number

-	 * based on the subscription associated with the provided subscriptionId.

-	 * 

-	 * @param maxItems Maximum number of items to return

-	 * @param subscriptionId The subscription which the retrieval is based

-	 * on.

-	 * 

-	 * @return List of {@link Item}

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> List<T> getItems(int maxItems, String subscriptionId)

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId, maxItems));

-		

-		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);

-		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);

-		return (List<T>)itemsElem.getItems();

-	}

-	

-	/**

-	 * Publishes an event to the node.  This is an empty event

-	 * with no item.

-	 * 

-	 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false

-	 * and {@link ConfigureForm#isDeliverPayloads()}=false.

-	 * 

-	 * This is an asynchronous call which returns as soon as the 

-	 * packet has been sent.

-	 * 

-	 * For synchronous calls use {@link #send() send()}.

-	 */

-	public void publish()

-	{

-		PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));

-		

-		con.sendPacket(packet);

-	}

-	

-	/**

-	 * Publishes an event to the node.  This is a simple item

-	 * with no payload.

-	 * 

-	 * If the id is null, an empty item (one without an id) will be sent.

-	 * Please note that this is not the same as {@link #send()}, which

-	 * publishes an event with NO item.

-	 * 

-	 * This is an asynchronous call which returns as soon as the 

-	 * packet has been sent.

-	 * 

-	 * For synchronous calls use {@link #send(Item) send(Item))}.

-	 * 

-	 * @param item - The item being sent

-	 */

-	public <T extends Item> void publish(T item)

-	{

-		Collection<T> items = new ArrayList<T>(1);

-		items.add((T)(item == null ? new Item() : item));

-		publish(items);

-	}

-

-	/**

-	 * Publishes multiple events to the node.  Same rules apply as in {@link #publish(Item)}.

-	 * 

-	 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input

-	 * list will get stored on the node, assuming it stores the last sent item.

-	 * 

-	 * This is an asynchronous call which returns as soon as the 

-	 * packet has been sent.

-	 * 

-	 * For synchronous calls use {@link #send(Collection) send(Collection))}.

-	 * 

-	 * @param items - The collection of items being sent

-	 */

-	public <T extends Item> void publish(Collection<T> items)

-	{

-		PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));

-		

-		con.sendPacket(packet);

-	}

-

-	/**

-	 * Publishes an event to the node.  This is an empty event

-	 * with no item.

-	 * 

-	 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false

-	 * and {@link ConfigureForm#isDeliverPayloads()}=false.

-	 * 

-	 * This is a synchronous call which will throw an exception 

-	 * on failure.

-	 * 

-	 * For asynchronous calls, use {@link #publish() publish()}.

-	 * 

-	 * @throws XMPPException

-	 */

-	public void send()

-		throws XMPPException

-	{

-		PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));

-		

-		SyncPacketSend.getReply(con, packet);

-	}

-	

-	/**

-	 * Publishes an event to the node.  This can be either a simple item

-	 * with no payload, or one with it.  This is determined by the Node

-	 * configuration.

-	 * 

-	 * If the node has <b>deliver_payload=false</b>, the Item must not

-	 * have a payload.

-	 * 

-	 * If the id is null, an empty item (one without an id) will be sent.

-	 * Please note that this is not the same as {@link #send()}, which

-	 * publishes an event with NO item.

-	 * 

-	 * This is a synchronous call which will throw an exception 

-	 * on failure.

-	 * 

-	 * For asynchronous calls, use {@link #publish(Item) publish(Item)}.

-	 * 

-	 * @param item - The item being sent

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> void send(T item)

-		throws XMPPException

-	{

-		Collection<T> items = new ArrayList<T>(1);

-		items.add((item == null ? (T)new Item() : item));

-		send(items);

-	}

-	

-	/**

-	 * Publishes multiple events to the node.  Same rules apply as in {@link #send(Item)}.

-	 * 

-	 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input

-	 * list will get stored on the node, assuming it stores the last sent item.

-	 *  

-	 * This is a synchronous call which will throw an exception 

-	 * on failure.

-	 * 

-	 * For asynchronous calls, use {@link #publish(Collection) publish(Collection))}.

-	 * 

-	 * @param items - The collection of {@link Item} objects being sent

-	 * 

-	 * @throws XMPPException

-	 */

-	public <T extends Item> void send(Collection<T> items)

-		throws XMPPException

-	{

-		PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));

-		

-		SyncPacketSend.getReply(con, packet);

-	}

-	

-	/**

-	 * Purges the node of all items.

-	 *   

-	 * <p>Note: Some implementations may keep the last item

-	 * sent.

-	 * 

-	 * @throws XMPPException

-	 */

-	public void deleteAllItems()

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());

-		

-		SyncPacketSend.getReply(con, request);

-	}

-	

-	/**

-	 * Delete the item with the specified id from the node.

-	 * 

-	 * @param itemId The id of the item

-	 * 

-	 * @throws XMPPException

-	 */

-	public void deleteItem(String itemId)

-		throws XMPPException

-	{

-		Collection<String> items = new ArrayList<String>(1);

-		items.add(itemId);

-		deleteItem(items);

-	}

-	

-	/**

-	 * Delete the items with the specified id's from the node.

-	 * 

-	 * @param itemIds The list of id's of items to delete

-	 * 

-	 * @throws XMPPException

-	 */

-	public void deleteItem(Collection<String> itemIds)

-		throws XMPPException

-	{

-		List<Item> items = new ArrayList<Item>(itemIds.size());

-		

-		for (String id : itemIds)

-		{

-			items.add(new Item(id));

-		}

-		PubSub request = createPubsubPacket(Type.SET, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items));

-		SyncPacketSend.getReply(con, request);

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smackx.packet.DiscoverItems;
+import org.jivesoftware.smackx.pubsub.packet.PubSub;
+import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
+
+/**
+ * The main class for the majority of pubsub functionality.  In general
+ * almost all pubsub capabilities are related to the concept of a node.
+ * All items are published to a node, and typically subscribed to by other
+ * users.  These users then retrieve events based on this subscription.
+ * 
+ * @author Robin Collier
+ */
+public class LeafNode extends Node
+{
+	LeafNode(Connection connection, String nodeName)
+	{
+		super(connection, nodeName);
+	}
+	
+	/**
+	 * Get information on the items in the node in standard
+	 * {@link DiscoverItems} format.
+	 * 
+	 * @return The item details in {@link DiscoverItems} format
+	 * 
+	 * @throws XMPPException
+	 */
+	public DiscoverItems discoverItems()
+		throws XMPPException
+	{
+		DiscoverItems items = new DiscoverItems();
+		items.setTo(to);
+		items.setNode(getId());
+		return (DiscoverItems)SyncPacketSend.getReply(con, items);
+	}
+
+	/**
+	 * Get the current items stored in the node.
+	 * 
+	 * @return List of {@link Item} in the node
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> List<T> getItems()
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId()));
+		
+		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
+		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
+		return (List<T>)itemsElem.getItems();
+	}
+	
+	/**
+	 * Get the current items stored in the node based
+	 * on the subscription associated with the provided 
+	 * subscription id.
+	 * 
+	 * @param subscriptionId -  The subscription id for the 
+	 * associated subscription.
+	 * @return List of {@link Item} in the node
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> List<T> getItems(String subscriptionId)
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId));
+		
+		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
+		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
+		return (List<T>)itemsElem.getItems();
+	}
+
+	/**
+	 * Get the items specified from the node.  This would typically be
+	 * used when the server does not return the payload due to size 
+	 * constraints.  The user would be required to retrieve the payload 
+	 * after the items have been retrieved via {@link #getItems()} or an
+	 * event, that did not include the payload.
+	 * 
+	 * @param ids Item ids of the items to retrieve
+	 * 
+	 * @return The list of {@link Item} with payload
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> List<T> getItems(Collection<String> ids)
+		throws XMPPException
+	{
+		List<Item> itemList = new ArrayList<Item>(ids.size());
+		
+		for (String id : ids)
+		{
+			itemList.add(new Item(id));
+		}
+		PubSub request = createPubsubPacket(Type.GET, new ItemsExtension(ItemsExtension.ItemsElementType.items, getId(), itemList));
+		
+		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
+		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
+		return (List<T>)itemsElem.getItems();
+	}
+
+	/**
+	 * Get items persisted on the node, limited to the specified number.
+	 * 
+	 * @param maxItems Maximum number of items to return
+	 * 
+	 * @return List of {@link Item}
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> List<T> getItems(int maxItems)
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), maxItems));
+		
+		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
+		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
+		return (List<T>)itemsElem.getItems();
+	}
+	
+	/**
+	 * Get items persisted on the node, limited to the specified number
+	 * based on the subscription associated with the provided subscriptionId.
+	 * 
+	 * @param maxItems Maximum number of items to return
+	 * @param subscriptionId The subscription which the retrieval is based
+	 * on.
+	 * 
+	 * @return List of {@link Item}
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> List<T> getItems(int maxItems, String subscriptionId)
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.GET, new GetItemsRequest(getId(), subscriptionId, maxItems));
+		
+		PubSub result = (PubSub)SyncPacketSend.getReply(con, request);
+		ItemsExtension itemsElem = (ItemsExtension)result.getExtension(PubSubElementType.ITEMS);
+		return (List<T>)itemsElem.getItems();
+	}
+	
+	/**
+	 * Publishes an event to the node.  This is an empty event
+	 * with no item.
+	 * 
+	 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
+	 * and {@link ConfigureForm#isDeliverPayloads()}=false.
+	 * 
+	 * This is an asynchronous call which returns as soon as the 
+	 * packet has been sent.
+	 * 
+	 * For synchronous calls use {@link #send() send()}.
+	 */
+	public void publish()
+	{
+		PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
+		
+		con.sendPacket(packet);
+	}
+	
+	/**
+	 * Publishes an event to the node.  This is a simple item
+	 * with no payload.
+	 * 
+	 * If the id is null, an empty item (one without an id) will be sent.
+	 * Please note that this is not the same as {@link #send()}, which
+	 * publishes an event with NO item.
+	 * 
+	 * This is an asynchronous call which returns as soon as the 
+	 * packet has been sent.
+	 * 
+	 * For synchronous calls use {@link #send(Item) send(Item))}.
+	 * 
+	 * @param item - The item being sent
+	 */
+	public <T extends Item> void publish(T item)
+	{
+		Collection<T> items = new ArrayList<T>(1);
+		items.add((T)(item == null ? new Item() : item));
+		publish(items);
+	}
+
+	/**
+	 * Publishes multiple events to the node.  Same rules apply as in {@link #publish(Item)}.
+	 * 
+	 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
+	 * list will get stored on the node, assuming it stores the last sent item.
+	 * 
+	 * This is an asynchronous call which returns as soon as the 
+	 * packet has been sent.
+	 * 
+	 * For synchronous calls use {@link #send(Collection) send(Collection))}.
+	 * 
+	 * @param items - The collection of items being sent
+	 */
+	public <T extends Item> void publish(Collection<T> items)
+	{
+		PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
+		
+		con.sendPacket(packet);
+	}
+
+	/**
+	 * Publishes an event to the node.  This is an empty event
+	 * with no item.
+	 * 
+	 * This is only acceptable for nodes with {@link ConfigureForm#isPersistItems()}=false
+	 * and {@link ConfigureForm#isDeliverPayloads()}=false.
+	 * 
+	 * This is a synchronous call which will throw an exception 
+	 * on failure.
+	 * 
+	 * For asynchronous calls, use {@link #publish() publish()}.
+	 * 
+	 * @throws XMPPException
+	 */
+	public void send()
+		throws XMPPException
+	{
+		PubSub packet = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PUBLISH, getId()));
+		
+		SyncPacketSend.getReply(con, packet);
+	}
+	
+	/**
+	 * Publishes an event to the node.  This can be either a simple item
+	 * with no payload, or one with it.  This is determined by the Node
+	 * configuration.
+	 * 
+	 * If the node has <b>deliver_payload=false</b>, the Item must not
+	 * have a payload.
+	 * 
+	 * If the id is null, an empty item (one without an id) will be sent.
+	 * Please note that this is not the same as {@link #send()}, which
+	 * publishes an event with NO item.
+	 * 
+	 * This is a synchronous call which will throw an exception 
+	 * on failure.
+	 * 
+	 * For asynchronous calls, use {@link #publish(Item) publish(Item)}.
+	 * 
+	 * @param item - The item being sent
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> void send(T item)
+		throws XMPPException
+	{
+		Collection<T> items = new ArrayList<T>(1);
+		items.add((item == null ? (T)new Item() : item));
+		send(items);
+	}
+	
+	/**
+	 * Publishes multiple events to the node.  Same rules apply as in {@link #send(Item)}.
+	 * 
+	 * In addition, if {@link ConfigureForm#isPersistItems()}=false, only the last item in the input
+	 * list will get stored on the node, assuming it stores the last sent item.
+	 *  
+	 * This is a synchronous call which will throw an exception 
+	 * on failure.
+	 * 
+	 * For asynchronous calls, use {@link #publish(Collection) publish(Collection))}.
+	 * 
+	 * @param items - The collection of {@link Item} objects being sent
+	 * 
+	 * @throws XMPPException
+	 */
+	public <T extends Item> void send(Collection<T> items)
+		throws XMPPException
+	{
+		PubSub packet = createPubsubPacket(Type.SET, new PublishItem<T>(getId(), items));
+		
+		SyncPacketSend.getReply(con, packet);
+	}
+	
+	/**
+	 * Purges the node of all items.
+	 *   
+	 * <p>Note: Some implementations may keep the last item
+	 * sent.
+	 * 
+	 * @throws XMPPException
+	 */
+	public void deleteAllItems()
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());
+		
+		SyncPacketSend.getReply(con, request);
+	}
+	
+	/**
+	 * Delete the item with the specified id from the node.
+	 * 
+	 * @param itemId The id of the item
+	 * 
+	 * @throws XMPPException
+	 */
+	public void deleteItem(String itemId)
+		throws XMPPException
+	{
+		Collection<String> items = new ArrayList<String>(1);
+		items.add(itemId);
+		deleteItem(items);
+	}
+	
+	/**
+	 * Delete the items with the specified id's from the node.
+	 * 
+	 * @param itemIds The list of id's of items to delete
+	 * 
+	 * @throws XMPPException
+	 */
+	public void deleteItem(Collection<String> itemIds)
+		throws XMPPException
+	{
+		List<Item> items = new ArrayList<Item>(itemIds.size());
+		
+		for (String id : itemIds)
+		{
+			items.add(new Item(id));
+		}
+		PubSub request = createPubsubPacket(Type.SET, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items));
+		SyncPacketSend.getReply(con, request);
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Node.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Node.java
index 1b0ff5a..3e745f3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Node.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Node.java
@@ -17,525 +17,525 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx.pubsub;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Iterator;

-import java.util.List;

-import java.util.concurrent.ConcurrentHashMap;

-

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.OrFilter;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.packet.Message;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.packet.IQ.Type;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.packet.DelayInformation;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.Header;

-import org.jivesoftware.smackx.packet.HeadersExtension;

-import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;

-import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;

-import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;

-import org.jivesoftware.smackx.pubsub.packet.PubSub;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;

-import org.jivesoftware.smackx.pubsub.util.NodeUtils;

-

-abstract public class Node

-{

-	protected Connection con;

-	protected String id;

-	protected String to;

-	

-	protected ConcurrentHashMap<ItemEventListener<Item>, PacketListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener<Item>, PacketListener>();

-	protected ConcurrentHashMap<ItemDeleteListener, PacketListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, PacketListener>();

-	protected ConcurrentHashMap<NodeConfigListener, PacketListener> configEventToListenerMap = new ConcurrentHashMap<NodeConfigListener, PacketListener>();

-	

-	/**

-	 * Construct a node associated to the supplied connection with the specified 

-	 * node id.

-	 * 

-	 * @param connection The connection the node is associated with

-	 * @param nodeName The node id

-	 */

-	Node(Connection connection, String nodeName)

-	{

-		con = connection;

-		id = nodeName;

-	}

-

-	/**

-	 * Some XMPP servers may require a specific service to be addressed on the 

-	 * server.

-	 * 

-	 *   For example, OpenFire requires the server to be prefixed by <b>pubsub</b>

-	 */

-	void setTo(String toAddress)

-	{

-		to = toAddress;

-	}

-

-	/**

-	 * Get the NodeId

-	 * 

-	 * @return the node id

-	 */

-	public String getId() 

-	{

-		return id;

-	}

-	/**

-	 * Returns a configuration form, from which you can create an answer form to be submitted

-	 * via the {@link #sendConfigurationForm(Form)}.

-	 * 

-	 * @return the configuration form

-	 */

-	public ConfigureForm getNodeConfiguration()

-		throws XMPPException

-	{

-		Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.CONFIGURE_OWNER, getId()), PubSubNamespace.OWNER);

-		return NodeUtils.getFormFromPacket(reply, PubSubElementType.CONFIGURE_OWNER);

-	}

-	

-	/**

-	 * Update the configuration with the contents of the new {@link Form}

-	 * 

-	 * @param submitForm

-	 */

-	public void sendConfigurationForm(Form submitForm)

-		throws XMPPException

-	{

-		PubSub packet = createPubsubPacket(Type.SET, new FormNode(FormNodeType.CONFIGURE_OWNER, getId(), submitForm), PubSubNamespace.OWNER);

-		SyncPacketSend.getReply(con, packet);

-	}

-	

-	/**

-	 * Discover node information in standard {@link DiscoverInfo} format.

-	 * 

-	 * @return The discovery information about the node.

-	 * 

-	 * @throws XMPPException

-	 */

-	public DiscoverInfo discoverInfo()

-		throws XMPPException

-	{

-		DiscoverInfo info = new DiscoverInfo();

-		info.setTo(to);

-		info.setNode(getId());

-		return (DiscoverInfo)SyncPacketSend.getReply(con, info);

-	}

-	

-	/**

-	 * Get the subscriptions currently associated with this node.

-	 * 

-	 * @return List of {@link Subscription}

-	 * 

-	 * @throws XMPPException

-	 */

-	public List<Subscription> getSubscriptions()

-		throws XMPPException

-	{

-		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS, getId()));

-		SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS);

-		return subElem.getSubscriptions();

-	}

-

-	/**

-	 * The user subscribes to the node using the supplied jid.  The

-	 * bare jid portion of this one must match the jid for the connection.

-	 * 

-	 * Please note that the {@link Subscription.State} should be checked 

-	 * on return since more actions may be required by the caller.

-	 * {@link Subscription.State#pending} - The owner must approve the subscription 

-	 * request before messages will be received.

-	 * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, 

-	 * the caller must configure the subscription before messages will be received.  If it is false

-	 * the caller can configure it but is not required to do so.

-	 * @param jid The jid to subscribe as.

-	 * @return The subscription

-	 * @exception XMPPException

-	 */

-	public Subscription subscribe(String jid)

-		throws XMPPException

-	{

-		PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));

-		return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);

-	}

-	

-	/**

-	 * The user subscribes to the node using the supplied jid and subscription

-	 * options.  The bare jid portion of this one must match the jid for the 

-	 * connection.

-	 * 

-	 * Please note that the {@link Subscription.State} should be checked 

-	 * on return since more actions may be required by the caller.

-	 * {@link Subscription.State#pending} - The owner must approve the subscription 

-	 * request before messages will be received.

-	 * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, 

-	 * the caller must configure the subscription before messages will be received.  If it is false

-	 * the caller can configure it but is not required to do so.

-	 * @param jid The jid to subscribe as.

-	 * @return The subscription

-	 * @exception XMPPException

-	 */

-	public Subscription subscribe(String jid, SubscribeForm subForm)

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));

-		request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));

-		PubSub reply = (PubSub)PubSubManager.sendPubsubPacket(con, jid, Type.SET, request);

-		return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);

-	}

-

-	/**

-	 * Remove the subscription related to the specified JID.  This will only 

-	 * work if there is only 1 subscription.  If there are multiple subscriptions,

-	 * use {@link #unsubscribe(String, String)}.

-	 * 

-	 * @param jid The JID used to subscribe to the node

-	 * 

-	 * @throws XMPPException

-	 */

-	public void unsubscribe(String jid)

-		throws XMPPException

-	{

-		unsubscribe(jid, null);

-	}

-	

-	/**

-	 * Remove the specific subscription related to the specified JID.

-	 * 

-	 * @param jid The JID used to subscribe to the node

-	 * @param subscriptionId The id of the subscription being removed

-	 * 

-	 * @throws XMPPException

-	 */

-	public void unsubscribe(String jid, String subscriptionId)

-		throws XMPPException

-	{

-		sendPubsubPacket(Type.SET, new UnsubscribeExtension(jid, getId(), subscriptionId));

-	}

-

-	/**

-	 * Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted

-	 * via the {@link #sendConfigurationForm(Form)}.

-	 * 

-	 * @return A subscription options form

-	 * 

-	 * @throws XMPPException

-	 */

-	public SubscribeForm getSubscriptionOptions(String jid)

-		throws XMPPException

-	{

-		return getSubscriptionOptions(jid, null);

-	}

-

-

-	/**

-	 * Get the options for configuring the specified subscription.

-	 * 

-	 * @param jid JID the subscription is registered under

-	 * @param subscriptionId The subscription id

-	 * 

-	 * @return The subscription option form

-	 * 

-	 * @throws XMPPException

-	 */

-	public SubscribeForm getSubscriptionOptions(String jid, String subscriptionId)

-		throws XMPPException

-	{

-		PubSub packet = (PubSub)sendPubsubPacket(Type.GET, new OptionsExtension(jid, getId(), subscriptionId));

-		FormNode ext = (FormNode)packet.getExtension(PubSubElementType.OPTIONS);

-		return new SubscribeForm(ext.getForm());

-	}

-

-	/**

-	 * Register a listener for item publication events.  This 

-	 * listener will get called whenever an item is published to 

-	 * this node.

-	 * 

-	 * @param listener The handler for the event

-	 */

-	public void addItemEventListener(ItemEventListener listener)

-	{

-		PacketListener conListener = new ItemEventTranslator(listener); 

-		itemEventToListenerMap.put(listener, conListener);

-		con.addPacketListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));

-	}

-

-	/**

-	 * Unregister a listener for publication events.

-	 * 

-	 * @param listener The handler to unregister

-	 */

-	public void removeItemEventListener(ItemEventListener listener)

-	{

-		PacketListener conListener = itemEventToListenerMap.remove(listener);

-		

-		if (conListener != null)

-			con.removePacketListener(conListener);

-	}

-

-	/**

-	 * Register a listener for configuration events.  This listener

-	 * will get called whenever the node's configuration changes.

-	 * 

-	 * @param listener The handler for the event

-	 */

-	public void addConfigurationListener(NodeConfigListener listener)

-	{

-		PacketListener conListener = new NodeConfigTranslator(listener); 

-		configEventToListenerMap.put(listener, conListener);

-		con.addPacketListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));

-	}

-

-	/**

-	 * Unregister a listener for configuration events.

-	 * 

-	 * @param listener The handler to unregister

-	 */

-	public void removeConfigurationListener(NodeConfigListener listener)

-	{

-		PacketListener conListener = configEventToListenerMap .remove(listener);

-		

-		if (conListener != null)

-			con.removePacketListener(conListener);

-	}

-	

-	/**

-	 * Register an listener for item delete events.  This listener

-	 * gets called whenever an item is deleted from the node.

-	 * 

-	 * @param listener The handler for the event

-	 */

-	public void addItemDeleteListener(ItemDeleteListener listener)

-	{

-		PacketListener delListener = new ItemDeleteTranslator(listener); 

-		itemDeleteToListenerMap.put(listener, delListener);

-		EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");

-		EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());

-		

-		con.addPacketListener(delListener, new OrFilter(deleteItem, purge));

-	}

-

-	/**

-	 * Unregister a listener for item delete events.

-	 * 

-	 * @param listener The handler to unregister

-	 */

-	public void removeItemDeleteListener(ItemDeleteListener listener)

-	{

-		PacketListener conListener = itemDeleteToListenerMap .remove(listener);

-		

-		if (conListener != null)

-			con.removePacketListener(conListener);

-	}

-

-	@Override

-	public String toString()

-	{

-		return super.toString() + " " + getClass().getName() + " id: " + id;

-	}

-	

-	protected PubSub createPubsubPacket(Type type, PacketExtension ext)

-	{

-		return createPubsubPacket(type, ext, null);

-	}

-	

-	protected PubSub createPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)

-	{

-		return PubSubManager.createPubsubPacket(to, type, ext, ns);

-	}

-

-	protected Packet sendPubsubPacket(Type type, NodeExtension ext)

-		throws XMPPException

-	{

-		return PubSubManager.sendPubsubPacket(con, to, type, ext);

-	}

-

-	protected Packet sendPubsubPacket(Type type, NodeExtension ext, PubSubNamespace ns)

-		throws XMPPException

-	{

-		return PubSubManager.sendPubsubPacket(con, to, type, ext, ns);

-	}

-

-

-	private static List<String> getSubscriptionIds(Packet packet)

-	{

-		HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim");

-		List<String> values = null;

-		

-		if (headers != null)

-		{

-			values = new ArrayList<String>(headers.getHeaders().size());

-			

-			for (Header header : headers.getHeaders())

-			{

-				values.add(header.getValue());

-			}

-		}

-		return values;

-	}

-

-	/**

-	 * This class translates low level item publication events into api level objects for 

-	 * user consumption.

-	 * 

-	 * @author Robin Collier

-	 */

-	public class ItemEventTranslator implements PacketListener

-	{

-		private ItemEventListener listener;

-

-		public ItemEventTranslator(ItemEventListener eventListener)

-		{

-			listener = eventListener;

-		}

-		

-		public void processPacket(Packet packet)

-		{

-	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());

-			ItemsExtension itemsElem = (ItemsExtension)event.getEvent();

-			DelayInformation delay = (DelayInformation)packet.getExtension("delay", "urn:xmpp:delay");

-			

-			// If there was no delay based on XEP-0203, then try XEP-0091 for backward compatibility

-			if (delay == null)

-			{

-				delay = (DelayInformation)packet.getExtension("x", "jabber:x:delay");

-			}

-			ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), (List<Item>)itemsElem.getItems(), getSubscriptionIds(packet), (delay == null ? null : delay.getStamp()));

-			listener.handlePublishedItems(eventItems);

-		}

-	}

-

-	/**

-	 * This class translates low level item deletion events into api level objects for 

-	 * user consumption.

-	 * 

-	 * @author Robin Collier

-	 */

-	public class ItemDeleteTranslator implements PacketListener

-	{

-		private ItemDeleteListener listener;

-

-		public ItemDeleteTranslator(ItemDeleteListener eventListener)

-		{

-			listener = eventListener;

-		}

-		

-		public void processPacket(Packet packet)

-		{

-	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());

-	        

-	        List<PacketExtension> extList = event.getExtensions();

-	        

-	        if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName()))

-	        {

-	        	listener.handlePurge();

-	        }

-	        else

-	        {

-				ItemsExtension itemsElem = (ItemsExtension)event.getEvent();

-				Collection<? extends PacketExtension> pubItems = itemsElem.getItems();

-				Iterator<RetractItem> it = (Iterator<RetractItem>)pubItems.iterator();

-				List<String> items = new ArrayList<String>(pubItems.size());

-

-				while (it.hasNext())

-				{

-					RetractItem item = it.next();

-					items.add(item.getId());

-				}

-

-				ItemDeleteEvent eventItems = new ItemDeleteEvent(itemsElem.getNode(), items, getSubscriptionIds(packet));

-				listener.handleDeletedItems(eventItems);

-	        }

-		}

-	}

-	

-	/**

-	 * This class translates low level node configuration events into api level objects for 

-	 * user consumption.

-	 * 

-	 * @author Robin Collier

-	 */

-	public class NodeConfigTranslator implements PacketListener

-	{

-		private NodeConfigListener listener;

-

-		public NodeConfigTranslator(NodeConfigListener eventListener)

-		{

-			listener = eventListener;

-		}

-		

-		public void processPacket(Packet packet)

-		{

-	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());

-			ConfigurationEvent config = (ConfigurationEvent)event.getEvent();

-

-			listener.handleNodeConfiguration(config);

-		}

-	}

-

-	/**

-	 * Filter for {@link PacketListener} to filter out events not specific to the 

-	 * event type expected for this node.

-	 * 

-	 * @author Robin Collier

-	 */

-	class EventContentFilter implements PacketFilter

-	{

-		private String firstElement;

-		private String secondElement;

-		

-		EventContentFilter(String elementName)

-		{

-			firstElement = elementName;

-		}

-

-		EventContentFilter(String firstLevelEelement, String secondLevelElement)

-		{

-			firstElement = firstLevelEelement;

-			secondElement = secondLevelElement;

-		}

-

-		public boolean accept(Packet packet)

-		{

-			if (!(packet instanceof Message))

-				return false;

-

-			EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());

-			

-			if (event == null)

-				return false;

-

-			NodeExtension embedEvent = event.getEvent();

-			

-			if (embedEvent == null)

-				return false;

-			

-			if (embedEvent.getElementName().equals(firstElement))

-			{

-				if (!embedEvent.getNode().equals(getId()))

-					return false;

-				

-				if (secondElement == null)

-					return true;

-				

-				if (embedEvent instanceof EmbeddedPacketExtension)

-				{

-					List<PacketExtension> secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions();

-					

-					if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement))

-						return true;

-				}

-			}

-			return false;

-		}

-	}

-}

+package org.jivesoftware.smackx.pubsub;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.OrFilter;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.packet.DelayInformation;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.Header;
+import org.jivesoftware.smackx.packet.HeadersExtension;
+import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
+import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
+import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
+import org.jivesoftware.smackx.pubsub.packet.PubSub;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
+import org.jivesoftware.smackx.pubsub.util.NodeUtils;
+
+abstract public class Node
+{
+	protected Connection con;
+	protected String id;
+	protected String to;
+	
+	protected ConcurrentHashMap<ItemEventListener<Item>, PacketListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener<Item>, PacketListener>();
+	protected ConcurrentHashMap<ItemDeleteListener, PacketListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, PacketListener>();
+	protected ConcurrentHashMap<NodeConfigListener, PacketListener> configEventToListenerMap = new ConcurrentHashMap<NodeConfigListener, PacketListener>();
+	
+	/**
+	 * Construct a node associated to the supplied connection with the specified 
+	 * node id.
+	 * 
+	 * @param connection The connection the node is associated with
+	 * @param nodeName The node id
+	 */
+	Node(Connection connection, String nodeName)
+	{
+		con = connection;
+		id = nodeName;
+	}
+
+	/**
+	 * Some XMPP servers may require a specific service to be addressed on the 
+	 * server.
+	 * 
+	 *   For example, OpenFire requires the server to be prefixed by <b>pubsub</b>
+	 */
+	void setTo(String toAddress)
+	{
+		to = toAddress;
+	}
+
+	/**
+	 * Get the NodeId
+	 * 
+	 * @return the node id
+	 */
+	public String getId() 
+	{
+		return id;
+	}
+	/**
+	 * Returns a configuration form, from which you can create an answer form to be submitted
+	 * via the {@link #sendConfigurationForm(Form)}.
+	 * 
+	 * @return the configuration form
+	 */
+	public ConfigureForm getNodeConfiguration()
+		throws XMPPException
+	{
+		Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.CONFIGURE_OWNER, getId()), PubSubNamespace.OWNER);
+		return NodeUtils.getFormFromPacket(reply, PubSubElementType.CONFIGURE_OWNER);
+	}
+	
+	/**
+	 * Update the configuration with the contents of the new {@link Form}
+	 * 
+	 * @param submitForm
+	 */
+	public void sendConfigurationForm(Form submitForm)
+		throws XMPPException
+	{
+		PubSub packet = createPubsubPacket(Type.SET, new FormNode(FormNodeType.CONFIGURE_OWNER, getId(), submitForm), PubSubNamespace.OWNER);
+		SyncPacketSend.getReply(con, packet);
+	}
+	
+	/**
+	 * Discover node information in standard {@link DiscoverInfo} format.
+	 * 
+	 * @return The discovery information about the node.
+	 * 
+	 * @throws XMPPException
+	 */
+	public DiscoverInfo discoverInfo()
+		throws XMPPException
+	{
+		DiscoverInfo info = new DiscoverInfo();
+		info.setTo(to);
+		info.setNode(getId());
+		return (DiscoverInfo)SyncPacketSend.getReply(con, info);
+	}
+	
+	/**
+	 * Get the subscriptions currently associated with this node.
+	 * 
+	 * @return List of {@link Subscription}
+	 * 
+	 * @throws XMPPException
+	 */
+	public List<Subscription> getSubscriptions()
+		throws XMPPException
+	{
+		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS, getId()));
+		SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS);
+		return subElem.getSubscriptions();
+	}
+
+	/**
+	 * The user subscribes to the node using the supplied jid.  The
+	 * bare jid portion of this one must match the jid for the connection.
+	 * 
+	 * Please note that the {@link Subscription.State} should be checked 
+	 * on return since more actions may be required by the caller.
+	 * {@link Subscription.State#pending} - The owner must approve the subscription 
+	 * request before messages will be received.
+	 * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, 
+	 * the caller must configure the subscription before messages will be received.  If it is false
+	 * the caller can configure it but is not required to do so.
+	 * @param jid The jid to subscribe as.
+	 * @return The subscription
+	 * @exception XMPPException
+	 */
+	public Subscription subscribe(String jid)
+		throws XMPPException
+	{
+		PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
+		return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
+	}
+	
+	/**
+	 * The user subscribes to the node using the supplied jid and subscription
+	 * options.  The bare jid portion of this one must match the jid for the 
+	 * connection.
+	 * 
+	 * Please note that the {@link Subscription.State} should be checked 
+	 * on return since more actions may be required by the caller.
+	 * {@link Subscription.State#pending} - The owner must approve the subscription 
+	 * request before messages will be received.
+	 * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, 
+	 * the caller must configure the subscription before messages will be received.  If it is false
+	 * the caller can configure it but is not required to do so.
+	 * @param jid The jid to subscribe as.
+	 * @return The subscription
+	 * @exception XMPPException
+	 */
+	public Subscription subscribe(String jid, SubscribeForm subForm)
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
+		request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
+		PubSub reply = (PubSub)PubSubManager.sendPubsubPacket(con, jid, Type.SET, request);
+		return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
+	}
+
+	/**
+	 * Remove the subscription related to the specified JID.  This will only 
+	 * work if there is only 1 subscription.  If there are multiple subscriptions,
+	 * use {@link #unsubscribe(String, String)}.
+	 * 
+	 * @param jid The JID used to subscribe to the node
+	 * 
+	 * @throws XMPPException
+	 */
+	public void unsubscribe(String jid)
+		throws XMPPException
+	{
+		unsubscribe(jid, null);
+	}
+	
+	/**
+	 * Remove the specific subscription related to the specified JID.
+	 * 
+	 * @param jid The JID used to subscribe to the node
+	 * @param subscriptionId The id of the subscription being removed
+	 * 
+	 * @throws XMPPException
+	 */
+	public void unsubscribe(String jid, String subscriptionId)
+		throws XMPPException
+	{
+		sendPubsubPacket(Type.SET, new UnsubscribeExtension(jid, getId(), subscriptionId));
+	}
+
+	/**
+	 * Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted
+	 * via the {@link #sendConfigurationForm(Form)}.
+	 * 
+	 * @return A subscription options form
+	 * 
+	 * @throws XMPPException
+	 */
+	public SubscribeForm getSubscriptionOptions(String jid)
+		throws XMPPException
+	{
+		return getSubscriptionOptions(jid, null);
+	}
+
+
+	/**
+	 * Get the options for configuring the specified subscription.
+	 * 
+	 * @param jid JID the subscription is registered under
+	 * @param subscriptionId The subscription id
+	 * 
+	 * @return The subscription option form
+	 * 
+	 * @throws XMPPException
+	 */
+	public SubscribeForm getSubscriptionOptions(String jid, String subscriptionId)
+		throws XMPPException
+	{
+		PubSub packet = (PubSub)sendPubsubPacket(Type.GET, new OptionsExtension(jid, getId(), subscriptionId));
+		FormNode ext = (FormNode)packet.getExtension(PubSubElementType.OPTIONS);
+		return new SubscribeForm(ext.getForm());
+	}
+
+	/**
+	 * Register a listener for item publication events.  This 
+	 * listener will get called whenever an item is published to 
+	 * this node.
+	 * 
+	 * @param listener The handler for the event
+	 */
+	public void addItemEventListener(ItemEventListener listener)
+	{
+		PacketListener conListener = new ItemEventTranslator(listener); 
+		itemEventToListenerMap.put(listener, conListener);
+		con.addPacketListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));
+	}
+
+	/**
+	 * Unregister a listener for publication events.
+	 * 
+	 * @param listener The handler to unregister
+	 */
+	public void removeItemEventListener(ItemEventListener listener)
+	{
+		PacketListener conListener = itemEventToListenerMap.remove(listener);
+		
+		if (conListener != null)
+			con.removePacketListener(conListener);
+	}
+
+	/**
+	 * Register a listener for configuration events.  This listener
+	 * will get called whenever the node's configuration changes.
+	 * 
+	 * @param listener The handler for the event
+	 */
+	public void addConfigurationListener(NodeConfigListener listener)
+	{
+		PacketListener conListener = new NodeConfigTranslator(listener); 
+		configEventToListenerMap.put(listener, conListener);
+		con.addPacketListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));
+	}
+
+	/**
+	 * Unregister a listener for configuration events.
+	 * 
+	 * @param listener The handler to unregister
+	 */
+	public void removeConfigurationListener(NodeConfigListener listener)
+	{
+		PacketListener conListener = configEventToListenerMap .remove(listener);
+		
+		if (conListener != null)
+			con.removePacketListener(conListener);
+	}
+	
+	/**
+	 * Register an listener for item delete events.  This listener
+	 * gets called whenever an item is deleted from the node.
+	 * 
+	 * @param listener The handler for the event
+	 */
+	public void addItemDeleteListener(ItemDeleteListener listener)
+	{
+		PacketListener delListener = new ItemDeleteTranslator(listener); 
+		itemDeleteToListenerMap.put(listener, delListener);
+		EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");
+		EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());
+		
+		con.addPacketListener(delListener, new OrFilter(deleteItem, purge));
+	}
+
+	/**
+	 * Unregister a listener for item delete events.
+	 * 
+	 * @param listener The handler to unregister
+	 */
+	public void removeItemDeleteListener(ItemDeleteListener listener)
+	{
+		PacketListener conListener = itemDeleteToListenerMap .remove(listener);
+		
+		if (conListener != null)
+			con.removePacketListener(conListener);
+	}
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + " " + getClass().getName() + " id: " + id;
+	}
+	
+	protected PubSub createPubsubPacket(Type type, PacketExtension ext)
+	{
+		return createPubsubPacket(type, ext, null);
+	}
+	
+	protected PubSub createPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)
+	{
+		return PubSubManager.createPubsubPacket(to, type, ext, ns);
+	}
+
+	protected Packet sendPubsubPacket(Type type, NodeExtension ext)
+		throws XMPPException
+	{
+		return PubSubManager.sendPubsubPacket(con, to, type, ext);
+	}
+
+	protected Packet sendPubsubPacket(Type type, NodeExtension ext, PubSubNamespace ns)
+		throws XMPPException
+	{
+		return PubSubManager.sendPubsubPacket(con, to, type, ext, ns);
+	}
+
+
+	private static List<String> getSubscriptionIds(Packet packet)
+	{
+		HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim");
+		List<String> values = null;
+		
+		if (headers != null)
+		{
+			values = new ArrayList<String>(headers.getHeaders().size());
+			
+			for (Header header : headers.getHeaders())
+			{
+				values.add(header.getValue());
+			}
+		}
+		return values;
+	}
+
+	/**
+	 * This class translates low level item publication events into api level objects for 
+	 * user consumption.
+	 * 
+	 * @author Robin Collier
+	 */
+	public class ItemEventTranslator implements PacketListener
+	{
+		private ItemEventListener listener;
+
+		public ItemEventTranslator(ItemEventListener eventListener)
+		{
+			listener = eventListener;
+		}
+		
+		public void processPacket(Packet packet)
+		{
+	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
+			ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
+			DelayInformation delay = (DelayInformation)packet.getExtension("delay", "urn:xmpp:delay");
+			
+			// If there was no delay based on XEP-0203, then try XEP-0091 for backward compatibility
+			if (delay == null)
+			{
+				delay = (DelayInformation)packet.getExtension("x", "jabber:x:delay");
+			}
+			ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), (List<Item>)itemsElem.getItems(), getSubscriptionIds(packet), (delay == null ? null : delay.getStamp()));
+			listener.handlePublishedItems(eventItems);
+		}
+	}
+
+	/**
+	 * This class translates low level item deletion events into api level objects for 
+	 * user consumption.
+	 * 
+	 * @author Robin Collier
+	 */
+	public class ItemDeleteTranslator implements PacketListener
+	{
+		private ItemDeleteListener listener;
+
+		public ItemDeleteTranslator(ItemDeleteListener eventListener)
+		{
+			listener = eventListener;
+		}
+		
+		public void processPacket(Packet packet)
+		{
+	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
+	        
+	        List<PacketExtension> extList = event.getExtensions();
+	        
+	        if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName()))
+	        {
+	        	listener.handlePurge();
+	        }
+	        else
+	        {
+				ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
+				Collection<? extends PacketExtension> pubItems = itemsElem.getItems();
+				Iterator<RetractItem> it = (Iterator<RetractItem>)pubItems.iterator();
+				List<String> items = new ArrayList<String>(pubItems.size());
+
+				while (it.hasNext())
+				{
+					RetractItem item = it.next();
+					items.add(item.getId());
+				}
+
+				ItemDeleteEvent eventItems = new ItemDeleteEvent(itemsElem.getNode(), items, getSubscriptionIds(packet));
+				listener.handleDeletedItems(eventItems);
+	        }
+		}
+	}
+	
+	/**
+	 * This class translates low level node configuration events into api level objects for 
+	 * user consumption.
+	 * 
+	 * @author Robin Collier
+	 */
+	public class NodeConfigTranslator implements PacketListener
+	{
+		private NodeConfigListener listener;
+
+		public NodeConfigTranslator(NodeConfigListener eventListener)
+		{
+			listener = eventListener;
+		}
+		
+		public void processPacket(Packet packet)
+		{
+	        EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
+			ConfigurationEvent config = (ConfigurationEvent)event.getEvent();
+
+			listener.handleNodeConfiguration(config);
+		}
+	}
+
+	/**
+	 * Filter for {@link PacketListener} to filter out events not specific to the 
+	 * event type expected for this node.
+	 * 
+	 * @author Robin Collier
+	 */
+	class EventContentFilter implements PacketFilter
+	{
+		private String firstElement;
+		private String secondElement;
+		
+		EventContentFilter(String elementName)
+		{
+			firstElement = elementName;
+		}
+
+		EventContentFilter(String firstLevelEelement, String secondLevelElement)
+		{
+			firstElement = firstLevelEelement;
+			secondElement = secondLevelElement;
+		}
+
+		public boolean accept(Packet packet)
+		{
+			if (!(packet instanceof Message))
+				return false;
+
+			EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
+			
+			if (event == null)
+				return false;
+
+			NodeExtension embedEvent = event.getEvent();
+			
+			if (embedEvent == null)
+				return false;
+			
+			if (embedEvent.getElementName().equals(firstElement))
+			{
+				if (!embedEvent.getNode().equals(getId()))
+					return false;
+				
+				if (secondElement == null)
+					return true;
+				
+				if (embedEvent instanceof EmbeddedPacketExtension)
+				{
+					List<PacketExtension> secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions();
+					
+					if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement))
+						return true;
+				}
+			}
+			return false;
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeEvent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeEvent.java
index 1392e85..d55e218 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeEvent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeEvent.java
@@ -17,19 +17,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jivesoftware.smackx.pubsub;

-

-abstract public class NodeEvent

-{

-	private String nodeId;

-	

-	protected NodeEvent(String id)

-	{

-		nodeId = id;

-	}

-	

-	public String getNodeId()

-	{

-		return nodeId;

-	}

-}

+package org.jivesoftware.smackx.pubsub;
+
+abstract public class NodeEvent
+{
+	private String nodeId;
+	
+	protected NodeEvent(String id)
+	{
+		nodeId = id;
+	}
+	
+	public String getNodeId()
+	{
+		return nodeId;
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeExtension.java
index 7e4cdec..9a3e480 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeExtension.java
@@ -1,85 +1,85 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * A class which represents a common element within the pubsub defined

- * schemas.  One which has a <b>node</b> as an attribute.  This class is 

- * used on its own as well as a base class for many others, since the 

- * node is a central concept to most pubsub functionality.

- * 

- * @author Robin Collier

- */

-public class NodeExtension implements PacketExtension

-{

-	private PubSubElementType element;

-	private String node;

-	

-	/**

-	 * Constructs a <tt>NodeExtension</tt> with an element name specified

-	 * by {@link PubSubElementType} and the specified node id.

-	 * 

-	 * @param elem Defines the element name and namespace

-	 * @param nodeId Specifies the id of the node

-	 */

-	public NodeExtension(PubSubElementType elem, String nodeId)

-	{

-		element = elem;

-		this.node = nodeId;

-	}

-

-	/**

-	 * Constructs a <tt>NodeExtension</tt> with an element name specified

-	 * by {@link PubSubElementType}.

-	 * 

-	 * @param elem Defines the element name and namespace

-	 */

-	public NodeExtension(PubSubElementType elem)

-	{

-		this(elem, null);

-	}

-

-	/**

-	 * Gets the node id

-	 * 

-	 * @return The node id

-	 */

-	public String getNode()

-	{

-		return node;

-	}

-	

-	public String getElementName()

-	{

-		return element.getElementName();

-	}

-

-	public String getNamespace()

-	{

-		return element.getNamespace().getXmlns();

-	}

-

-	public String toXML()

-	{

-		return '<' + getElementName() + (node == null ? "" : " node='" + node + '\'') + "/>";

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + " - content [" + toXML() + "]";

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * A class which represents a common element within the pubsub defined
+ * schemas.  One which has a <b>node</b> as an attribute.  This class is 
+ * used on its own as well as a base class for many others, since the 
+ * node is a central concept to most pubsub functionality.
+ * 
+ * @author Robin Collier
+ */
+public class NodeExtension implements PacketExtension
+{
+	private PubSubElementType element;
+	private String node;
+	
+	/**
+	 * Constructs a <tt>NodeExtension</tt> with an element name specified
+	 * by {@link PubSubElementType} and the specified node id.
+	 * 
+	 * @param elem Defines the element name and namespace
+	 * @param nodeId Specifies the id of the node
+	 */
+	public NodeExtension(PubSubElementType elem, String nodeId)
+	{
+		element = elem;
+		this.node = nodeId;
+	}
+
+	/**
+	 * Constructs a <tt>NodeExtension</tt> with an element name specified
+	 * by {@link PubSubElementType}.
+	 * 
+	 * @param elem Defines the element name and namespace
+	 */
+	public NodeExtension(PubSubElementType elem)
+	{
+		this(elem, null);
+	}
+
+	/**
+	 * Gets the node id
+	 * 
+	 * @return The node id
+	 */
+	public String getNode()
+	{
+		return node;
+	}
+	
+	public String getElementName()
+	{
+		return element.getElementName();
+	}
+
+	public String getNamespace()
+	{
+		return element.getNamespace().getXmlns();
+	}
+
+	public String toXML()
+	{
+		return '<' + getElementName() + (node == null ? "" : " node='" + node + '\'') + "/>";
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + " - content [" + toXML() + "]";
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeType.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeType.java
index 5ee5a05..5d0eb69 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeType.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/NodeType.java
@@ -1,25 +1,25 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * Defines the available types of nodes

- * 

- * @author Robin Collier

- */

-public enum NodeType

-{

-	leaf,

-	collection;

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * Defines the available types of nodes
+ * 
+ * @author Robin Collier
+ */
+public enum NodeType
+{
+	leaf,
+	collection;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/OptionsExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/OptionsExtension.java
index 32c0331..f8775d8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/OptionsExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/OptionsExtension.java
@@ -1,72 +1,72 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.pubsub.util.XmlUtils;

-

-/**

- * A packet extension representing the <b>options</b> element. 

- * 

- * @author Robin Collier

- */

-public class OptionsExtension extends NodeExtension

-{

-	protected String jid;

-	protected String id;

-	

-	public OptionsExtension(String subscriptionJid)

-	{

-		this(subscriptionJid, null, null);

-	}

-	

-	public OptionsExtension(String subscriptionJid, String nodeId)

-	{

-		this(subscriptionJid, nodeId, null);

-	}

-	

-	public OptionsExtension(String jid, String nodeId, String subscriptionId)

-	{

-		super(PubSubElementType.OPTIONS, nodeId);

-		this.jid = jid;

-		id = subscriptionId;

-	}

-	

-	public String getJid()

-	{

-		return jid;

-	}

-	

-	public String getId()

-	{

-		return id;

-	}

-	

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		XmlUtils.appendAttribute(builder, "jid", jid);

-		

-		if (getNode() != null)

-			XmlUtils.appendAttribute(builder, "node", getNode());

-		

-		if (id != null)

-			XmlUtils.appendAttribute(builder, "subid", id);

-		

-		builder.append("/>");

-		return builder.toString();

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.pubsub.util.XmlUtils;
+
+/**
+ * A packet extension representing the <b>options</b> element. 
+ * 
+ * @author Robin Collier
+ */
+public class OptionsExtension extends NodeExtension
+{
+	protected String jid;
+	protected String id;
+	
+	public OptionsExtension(String subscriptionJid)
+	{
+		this(subscriptionJid, null, null);
+	}
+	
+	public OptionsExtension(String subscriptionJid, String nodeId)
+	{
+		this(subscriptionJid, nodeId, null);
+	}
+	
+	public OptionsExtension(String jid, String nodeId, String subscriptionId)
+	{
+		super(PubSubElementType.OPTIONS, nodeId);
+		this.jid = jid;
+		id = subscriptionId;
+	}
+	
+	public String getJid()
+	{
+		return jid;
+	}
+	
+	public String getId()
+	{
+		return id;
+	}
+	
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		XmlUtils.appendAttribute(builder, "jid", jid);
+		
+		if (getNode() != null)
+			XmlUtils.appendAttribute(builder, "node", getNode());
+		
+		if (id != null)
+			XmlUtils.appendAttribute(builder, "subid", id);
+		
+		builder.append("/>");
+		return builder.toString();
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PayloadItem.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PayloadItem.java
index e9497c5..a6e6365 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PayloadItem.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PayloadItem.java
@@ -1,138 +1,138 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.packet.Message;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.pubsub.provider.ItemProvider;

-

-/**

- * This class represents an item that has been, or will be published to a

- * pubsub node.  An <tt>Item</tt> has several properties that are dependent

- * on the configuration of the node to which it has been or will be published.

- * 

- * <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>

- * <li>Will always have an id (either user or server generated) unless node configuration has both

- * {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.

- * <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set 

- * to true, otherwise it will be null.

- * 

- * <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>

- * <li>The id is optional, since the server will generate one if necessary, but should be used if it is 

- * meaningful in the context of the node.  This value must be unique within the node that it is sent to, since

- * resending an item with the same id will overwrite the one that already exists if the items are persisted.

- * <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set

- * to true. 

- * 

- * <p>To customise the payload object being returned from the {@link #getPayload()} method, you can

- * add a custom parser as explained in {@link ItemProvider}.

- * 

- * @author Robin Collier

- */

-public class PayloadItem<E extends PacketExtension> extends Item

-{

-	private E payload;

-	

-	/**

-	 * Create an <tt>Item</tt> with no id and a payload  The id will be set by the server.  

-	 * 

-	 * @param payloadExt A {@link PacketExtension} which represents the payload data.

-	 */

-	public PayloadItem(E payloadExt)

-	{

-		super();

-		

-		if (payloadExt == null)

-			throw new IllegalArgumentException("payload cannot be 'null'");

-		payload = payloadExt;

-	}

-

-	/**

-	 * Create an <tt>Item</tt> with an id and payload.  

-	 * 

-	 * @param itemId The id of this item.  It can be null if we want the server to set the id.

-	 * @param payloadExt A {@link PacketExtension} which represents the payload data.

-	 */

-	public PayloadItem(String itemId, E payloadExt)

-	{

-		super(itemId);

-		

-		if (payloadExt == null)

-			throw new IllegalArgumentException("payload cannot be 'null'");

-		payload = payloadExt;

-	}

-	

-	/**

-	 * Create an <tt>Item</tt> with an id, node id and payload.  

-	 * 

-	 * <p>

-	 * <b>Note:</b> This is not valid for publishing an item to a node, only receiving from 

-	 * one as part of {@link Message}.  If used to create an Item to publish 

-	 * (via {@link LeafNode#publish(Item)}, the server <i>may</i> return an

-	 * error for an invalid packet.

-	 * 

-	 * @param itemId The id of this item.

-	 * @param nodeId The id of the node the item was published to.

-	 * @param payloadExt A {@link PacketExtension} which represents the payload data.

-	 */

-	public PayloadItem(String itemId, String nodeId, E payloadExt)

-	{

-		super(itemId, nodeId);

-		

-		if (payloadExt == null)

-			throw new IllegalArgumentException("payload cannot be 'null'");

-		payload = payloadExt;

-	}

-	

-	/**

-	 * Get the payload associated with this <tt>Item</tt>.  Customising the payload

-	 * parsing from the server can be accomplished as described in {@link ItemProvider}.

-	 * 

-	 * @return The payload as a {@link PacketExtension}.

-	 */

-	public E getPayload()

-	{

-		return payload;

-	}

-	

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<item");

-		

-		if (getId() != null)

-		{

-			builder.append(" id='");

-			builder.append(getId());

-			builder.append("'");

-		}

-		

-        if (getNode() != null) {

-            builder.append(" node='");

-            builder.append(getNode());

-            builder.append("'");

-        }

-		builder.append(">");

-		builder.append(payload.toXML());

-		builder.append("</item>");

-		

-		return builder.toString();

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + " | Content [" + toXML() + "]";

-	}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
+
+/**
+ * This class represents an item that has been, or will be published to a
+ * pubsub node.  An <tt>Item</tt> has several properties that are dependent
+ * on the configuration of the node to which it has been or will be published.
+ * 
+ * <h1>An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}</b>
+ * <li>Will always have an id (either user or server generated) unless node configuration has both
+ * {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false.
+ * <li>Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set 
+ * to true, otherwise it will be null.
+ * 
+ * <h1>An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}</b>
+ * <li>The id is optional, since the server will generate one if necessary, but should be used if it is 
+ * meaningful in the context of the node.  This value must be unique within the node that it is sent to, since
+ * resending an item with the same id will overwrite the one that already exists if the items are persisted.
+ * <li>Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set
+ * to true. 
+ * 
+ * <p>To customise the payload object being returned from the {@link #getPayload()} method, you can
+ * add a custom parser as explained in {@link ItemProvider}.
+ * 
+ * @author Robin Collier
+ */
+public class PayloadItem<E extends PacketExtension> extends Item
+{
+	private E payload;
+	
+	/**
+	 * Create an <tt>Item</tt> with no id and a payload  The id will be set by the server.  
+	 * 
+	 * @param payloadExt A {@link PacketExtension} which represents the payload data.
+	 */
+	public PayloadItem(E payloadExt)
+	{
+		super();
+		
+		if (payloadExt == null)
+			throw new IllegalArgumentException("payload cannot be 'null'");
+		payload = payloadExt;
+	}
+
+	/**
+	 * Create an <tt>Item</tt> with an id and payload.  
+	 * 
+	 * @param itemId The id of this item.  It can be null if we want the server to set the id.
+	 * @param payloadExt A {@link PacketExtension} which represents the payload data.
+	 */
+	public PayloadItem(String itemId, E payloadExt)
+	{
+		super(itemId);
+		
+		if (payloadExt == null)
+			throw new IllegalArgumentException("payload cannot be 'null'");
+		payload = payloadExt;
+	}
+	
+	/**
+	 * Create an <tt>Item</tt> with an id, node id and payload.  
+	 * 
+	 * <p>
+	 * <b>Note:</b> This is not valid for publishing an item to a node, only receiving from 
+	 * one as part of {@link Message}.  If used to create an Item to publish 
+	 * (via {@link LeafNode#publish(Item)}, the server <i>may</i> return an
+	 * error for an invalid packet.
+	 * 
+	 * @param itemId The id of this item.
+	 * @param nodeId The id of the node the item was published to.
+	 * @param payloadExt A {@link PacketExtension} which represents the payload data.
+	 */
+	public PayloadItem(String itemId, String nodeId, E payloadExt)
+	{
+		super(itemId, nodeId);
+		
+		if (payloadExt == null)
+			throw new IllegalArgumentException("payload cannot be 'null'");
+		payload = payloadExt;
+	}
+	
+	/**
+	 * Get the payload associated with this <tt>Item</tt>.  Customising the payload
+	 * parsing from the server can be accomplished as described in {@link ItemProvider}.
+	 * 
+	 * @return The payload as a {@link PacketExtension}.
+	 */
+	public E getPayload()
+	{
+		return payload;
+	}
+	
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<item");
+		
+		if (getId() != null)
+		{
+			builder.append(" id='");
+			builder.append(getId());
+			builder.append("'");
+		}
+		
+        if (getNode() != null) {
+            builder.append(" node='");
+            builder.append(getNode());
+            builder.append("'");
+        }
+		builder.append(">");
+		builder.append(payload.toXML());
+		builder.append("</item>");
+		
+		return builder.toString();
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + " | Content [" + toXML() + "]";
+	}
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PresenceState.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PresenceState.java
index 0612fc2..07342e2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PresenceState.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PresenceState.java
@@ -1,25 +1,25 @@
-/**

- * 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.smackx.pubsub;

-

-/** 

- * Defines the possible valid presence states for node subscription via

- * {@link SubscribeForm#getShowValues()}.

- * 

- * @author Robin Collier

- */

-public enum PresenceState

-{

-	chat, online, away, xa, dnd

-}

+/**
+ * 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.smackx.pubsub;
+
+/** 
+ * Defines the possible valid presence states for node subscription via
+ * {@link SubscribeForm#getShowValues()}.
+ * 
+ * @author Robin Collier
+ */
+public enum PresenceState
+{
+	chat, online, away, xa, dnd
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubElementType.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubElementType.java
index a887ca2..ff762e9 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubElementType.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubElementType.java
@@ -1,80 +1,80 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-

-/**

- * Defines all the possible element types as defined for all the pubsub

- * schemas in all 3 namespaces.

- * 

- * @author Robin Collier

- */

-public enum PubSubElementType

-{

-	CREATE("create", PubSubNamespace.BASIC),

-	DELETE("delete", PubSubNamespace.OWNER),

-	DELETE_EVENT("delete", PubSubNamespace.EVENT),

-	CONFIGURE("configure", PubSubNamespace.BASIC),

-	CONFIGURE_OWNER("configure", PubSubNamespace.OWNER),

-	CONFIGURATION("configuration", PubSubNamespace.EVENT),

-	OPTIONS("options", PubSubNamespace.BASIC),

-	DEFAULT("default", PubSubNamespace.OWNER),	

-	ITEMS("items", PubSubNamespace.BASIC),

-	ITEMS_EVENT("items", PubSubNamespace.EVENT),

-	ITEM("item", PubSubNamespace.BASIC),

-	ITEM_EVENT("item", PubSubNamespace.EVENT),

-	PUBLISH("publish", PubSubNamespace.BASIC),

-	PUBLISH_OPTIONS("publish-options", PubSubNamespace.BASIC), 

-	PURGE_OWNER("purge", PubSubNamespace.OWNER),

-	PURGE_EVENT("purge", PubSubNamespace.EVENT),

-	RETRACT("retract", PubSubNamespace.BASIC), 

-	AFFILIATIONS("affiliations", PubSubNamespace.BASIC), 

-	SUBSCRIBE("subscribe", PubSubNamespace.BASIC), 

-	SUBSCRIPTION("subscription", PubSubNamespace.BASIC),

-	SUBSCRIPTIONS("subscriptions", PubSubNamespace.BASIC), 

-	UNSUBSCRIBE("unsubscribe", PubSubNamespace.BASIC);

-

-	private String eName;

-	private PubSubNamespace nSpace;

-	

-	private PubSubElementType(String elemName, PubSubNamespace ns)

-	{

-		eName = elemName;

-		nSpace = ns;

-	}

-	

-	public PubSubNamespace getNamespace()

-	{

-		return nSpace;

-	}

-	

-	public String getElementName()

-	{

-		return eName;

-	}

-	

-	public static PubSubElementType valueOfFromElemName(String elemName, String namespace)

-	{

-		int index = namespace.lastIndexOf('#');

-		String fragment = (index == -1 ? null : namespace.substring(index+1));

-		

-		if (fragment != null)

-		{

-			return valueOf((elemName + '_' + fragment).toUpperCase());

-		}

-		return valueOf(elemName.toUpperCase().replace('-', '_'));

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+
+/**
+ * Defines all the possible element types as defined for all the pubsub
+ * schemas in all 3 namespaces.
+ * 
+ * @author Robin Collier
+ */
+public enum PubSubElementType
+{
+	CREATE("create", PubSubNamespace.BASIC),
+	DELETE("delete", PubSubNamespace.OWNER),
+	DELETE_EVENT("delete", PubSubNamespace.EVENT),
+	CONFIGURE("configure", PubSubNamespace.BASIC),
+	CONFIGURE_OWNER("configure", PubSubNamespace.OWNER),
+	CONFIGURATION("configuration", PubSubNamespace.EVENT),
+	OPTIONS("options", PubSubNamespace.BASIC),
+	DEFAULT("default", PubSubNamespace.OWNER),	
+	ITEMS("items", PubSubNamespace.BASIC),
+	ITEMS_EVENT("items", PubSubNamespace.EVENT),
+	ITEM("item", PubSubNamespace.BASIC),
+	ITEM_EVENT("item", PubSubNamespace.EVENT),
+	PUBLISH("publish", PubSubNamespace.BASIC),
+	PUBLISH_OPTIONS("publish-options", PubSubNamespace.BASIC), 
+	PURGE_OWNER("purge", PubSubNamespace.OWNER),
+	PURGE_EVENT("purge", PubSubNamespace.EVENT),
+	RETRACT("retract", PubSubNamespace.BASIC), 
+	AFFILIATIONS("affiliations", PubSubNamespace.BASIC), 
+	SUBSCRIBE("subscribe", PubSubNamespace.BASIC), 
+	SUBSCRIPTION("subscription", PubSubNamespace.BASIC),
+	SUBSCRIPTIONS("subscriptions", PubSubNamespace.BASIC), 
+	UNSUBSCRIBE("unsubscribe", PubSubNamespace.BASIC);
+
+	private String eName;
+	private PubSubNamespace nSpace;
+	
+	private PubSubElementType(String elemName, PubSubNamespace ns)
+	{
+		eName = elemName;
+		nSpace = ns;
+	}
+	
+	public PubSubNamespace getNamespace()
+	{
+		return nSpace;
+	}
+	
+	public String getElementName()
+	{
+		return eName;
+	}
+	
+	public static PubSubElementType valueOfFromElemName(String elemName, String namespace)
+	{
+		int index = namespace.lastIndexOf('#');
+		String fragment = (index == -1 ? null : namespace.substring(index+1));
+		
+		if (fragment != null)
+		{
+			return valueOf((elemName + '_' + fragment).toUpperCase());
+		}
+		return valueOf(elemName.toUpperCase().replace('-', '_'));
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubManager.java
index 4fb0158..483f6f2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PubSubManager.java
@@ -1,329 +1,329 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.List;

-import java.util.Map;

-import java.util.concurrent.ConcurrentHashMap;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.packet.IQ.Type;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.ServiceDiscoveryManager;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.DiscoverItems;

-import org.jivesoftware.smackx.pubsub.packet.PubSub;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;

-import org.jivesoftware.smackx.pubsub.util.NodeUtils;

-

-/**

- * This is the starting point for access to the pubsub service.  It

- * will provide access to general information about the service, as

- * well as create or retrieve pubsub {@link LeafNode} instances.  These 

- * instances provide the bulk of the functionality as defined in the 

- * pubsub specification <a href="http://xmpp.org/extensions/xep-0060.html">XEP-0060</a>.

- * 

- * @author Robin Collier

- */

-final public class PubSubManager

-{

-	private Connection con;

-	private String to;

-	private Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();

-	

-	/**

-	 * Create a pubsub manager associated to the specified connection.  Defaults the service

-	 * name to <i>pubsub</i>

-	 * 

-	 * @param connection The XMPP connection

-	 */

-	public PubSubManager(Connection connection)

-	{

-		con = connection;

-		to = "pubsub." + connection.getServiceName();

-	}

-	

-	/**

-	 * Create a pubsub manager associated to the specified connection where

-	 * the pubsub requests require a specific to address for packets.

-	 * 

-	 * @param connection The XMPP connection

-	 * @param toAddress The pubsub specific to address (required for some servers)

-	 */

-	public PubSubManager(Connection connection, String toAddress)

-	{

-		con = connection;

-		to = toAddress;

-	}

-	

-	/**

-	 * Creates an instant node, if supported.

-	 * 

-	 * @return The node that was created

-	 * @exception XMPPException

-	 */

-	public LeafNode createNode()

-		throws XMPPException

-	{

-		PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.CREATE));

-		NodeExtension elem = (NodeExtension)reply.getExtension("create", PubSubNamespace.BASIC.getXmlns());

-		

-		LeafNode newNode = new LeafNode(con, elem.getNode());

-		newNode.setTo(to);

-		nodeMap.put(newNode.getId(), newNode);

-		

-		return newNode;

-	}

-	

-	/**

-	 * Creates a node with default configuration.

-	 * 

-	 * @param id The id of the node, which must be unique within the 

-	 * pubsub service

-	 * @return The node that was created

-	 * @exception XMPPException

-	 */

-	public LeafNode createNode(String id)

-		throws XMPPException

-	{

-		return (LeafNode)createNode(id, null);

-	}

-	

-	/**

-	 * Creates a node with specified configuration.

-	 * 

-	 * Note: This is the only way to create a collection node.

-	 * 

-	 * @param name The name of the node, which must be unique within the 

-	 * pubsub service

-	 * @param config The configuration for the node

-	 * @return The node that was created

-	 * @exception XMPPException

-	 */

-	public Node createNode(String name, Form config)

-		throws XMPPException

-	{

-		PubSub request = createPubsubPacket(to, Type.SET, new NodeExtension(PubSubElementType.CREATE, name));

-		boolean isLeafNode = true;

-		

-		if (config != null)

-		{

-			request.addExtension(new FormNode(FormNodeType.CONFIGURE, config));

-			FormField nodeTypeField = config.getField(ConfigureNodeFields.node_type.getFieldName());

-			

-			if (nodeTypeField != null)

-				isLeafNode = nodeTypeField.getValues().next().equals(NodeType.leaf.toString());

-		}

-

-		// Errors will cause exceptions in getReply, so it only returns

-		// on success.

-		sendPubsubPacket(con, to, Type.SET, request);

-		Node newNode = isLeafNode ? new LeafNode(con, name) : new CollectionNode(con, name);

-		newNode.setTo(to);

-		nodeMap.put(newNode.getId(), newNode);

-		

-		return newNode;

-	}

-

-	/**

-	 * Retrieves the requested node, if it exists.  It will throw an 

-	 * exception if it does not.

-	 * 

-	 * @param id - The unique id of the node

-	 * @return the node

-	 * @throws XMPPException The node does not exist

-	 */

-	public <T extends Node> T getNode(String id)

-		throws XMPPException

-	{

-		Node node = nodeMap.get(id);

-		

-		if (node == null)

-		{

-			DiscoverInfo info = new DiscoverInfo();

-			info.setTo(to);

-			info.setNode(id);

-			

-			DiscoverInfo infoReply = (DiscoverInfo)SyncPacketSend.getReply(con, info);

-			

-			if (infoReply.getIdentities().next().getType().equals(NodeType.leaf.toString()))

-				node = new LeafNode(con, id);

-			else

-				node = new CollectionNode(con, id);

-			node.setTo(to);

-			nodeMap.put(id, node);

-		}

-		return (T) node;

-	}

-	

-	/**

-	 * Get all the nodes that currently exist as a child of the specified

-	 * collection node.  If the service does not support collection nodes

-	 * then all nodes will be returned.

-	 * 

-	 * To retrieve contents of the root collection node (if it exists), 

-	 * or there is no root collection node, pass null as the nodeId.

-	 * 

-	 * @param nodeId - The id of the collection node for which the child 

-	 * nodes will be returned.  

-	 * @return {@link DiscoverItems} representing the existing nodes

-	 * 

-	 * @throws XMPPException

-	 */

-	public DiscoverItems discoverNodes(String nodeId)

-		throws XMPPException

-	{

-		DiscoverItems items = new DiscoverItems();

-		

-		if (nodeId != null)

-			items.setNode(nodeId);

-		items.setTo(to);

-		DiscoverItems nodeItems = (DiscoverItems)SyncPacketSend.getReply(con, items);

-		return nodeItems;

-	}

-	

-	/**

-	 * Gets the subscriptions on the root node.

-	 * 

-	 * @return List of exceptions

-	 * 

-	 * @throws XMPPException

-	 */

-	public List<Subscription> getSubscriptions()

-		throws XMPPException

-	{

-		Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS));

-		SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS.getElementName(), PubSubElementType.SUBSCRIPTIONS.getNamespace().getXmlns());

-		return subElem.getSubscriptions();

-	}

-	

-	/**

-	 * Gets the affiliations on the root node.

-	 * 

-	 * @return List of affiliations

-	 * 

-	 * @throws XMPPException

-	 */

-	public List<Affiliation> getAffiliations()

-		throws XMPPException

-	{

-		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.AFFILIATIONS));

-		AffiliationsExtension listElem = (AffiliationsExtension)reply.getExtension(PubSubElementType.AFFILIATIONS);

-		return listElem.getAffiliations();

-	}

-

-	/**

-	 * Delete the specified node

-	 * 

-	 * @param nodeId

-	 * @throws XMPPException

-	 */

-	public void deleteNode(String nodeId)

-		throws XMPPException

-	{

-		sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.DELETE, nodeId), PubSubElementType.DELETE.getNamespace());

-		nodeMap.remove(nodeId);

-	}

-	

-	/**

-	 * Returns the default settings for Node configuration.

-	 * 

-	 * @return configuration form containing the default settings.

-	 */

-	public ConfigureForm getDefaultConfiguration()

-		throws XMPPException

-	{

-		// Errors will cause exceptions in getReply, so it only returns

-		// on success.

-		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.DEFAULT), PubSubElementType.DEFAULT.getNamespace());

-		return NodeUtils.getFormFromPacket(reply, PubSubElementType.DEFAULT);

-	}

-	

-	/**

-	 * Gets the supported features of the servers pubsub implementation

-	 * as a standard {@link DiscoverInfo} instance.

-	 * 

-	 * @return The supported features

-	 * 

-	 * @throws XMPPException

-	 */

-	public DiscoverInfo getSupportedFeatures()

-		throws XMPPException

-	{

-		ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(con);

-		return mgr.discoverInfo(to);

-	}

-	

-	private Packet sendPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)

-		throws XMPPException

-	{

-		return sendPubsubPacket(con, to, type, ext, ns);

-	}

-

-	private Packet sendPubsubPacket(Type type, PacketExtension ext)

-		throws XMPPException

-	{

-		return sendPubsubPacket(type, ext, null);

-	}

-

-	static PubSub createPubsubPacket(String to, Type type, PacketExtension ext)

-	{

-		return createPubsubPacket(to, type, ext, null);

-	}

-	

-	static PubSub createPubsubPacket(String to, Type type, PacketExtension ext, PubSubNamespace ns)

-	{

-		PubSub request = new PubSub();

-		request.setTo(to);

-		request.setType(type);

-		

-		if (ns != null)

-		{

-			request.setPubSubNamespace(ns);

-		}

-		request.addExtension(ext);

-		

-		return request;

-	}

-

-	static Packet sendPubsubPacket(Connection con, String to, Type type, PacketExtension ext)

-		throws XMPPException

-	{

-		return sendPubsubPacket(con, to, type, ext, null);

-	}

-	

-	static Packet sendPubsubPacket(Connection con, String to, Type type, PacketExtension ext, PubSubNamespace ns)

-		throws XMPPException

-	{

-		return SyncPacketSend.getReply(con, createPubsubPacket(to, type, ext, ns));

-	}

-

-	static Packet sendPubsubPacket(Connection con, String to, Type type, PubSub packet)

-		throws XMPPException

-	{

-		return sendPubsubPacket(con, to, type, packet, null);

-	}

-

-	static Packet sendPubsubPacket(Connection con, String to, Type type, PubSub packet, PubSubNamespace ns)

-		throws XMPPException

-	{

-		return SyncPacketSend.getReply(con, packet);

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.DiscoverItems;
+import org.jivesoftware.smackx.pubsub.packet.PubSub;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
+import org.jivesoftware.smackx.pubsub.util.NodeUtils;
+
+/**
+ * This is the starting point for access to the pubsub service.  It
+ * will provide access to general information about the service, as
+ * well as create or retrieve pubsub {@link LeafNode} instances.  These 
+ * instances provide the bulk of the functionality as defined in the 
+ * pubsub specification <a href="http://xmpp.org/extensions/xep-0060.html">XEP-0060</a>.
+ * 
+ * @author Robin Collier
+ */
+final public class PubSubManager
+{
+	private Connection con;
+	private String to;
+	private Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();
+	
+	/**
+	 * Create a pubsub manager associated to the specified connection.  Defaults the service
+	 * name to <i>pubsub</i>
+	 * 
+	 * @param connection The XMPP connection
+	 */
+	public PubSubManager(Connection connection)
+	{
+		con = connection;
+		to = "pubsub." + connection.getServiceName();
+	}
+	
+	/**
+	 * Create a pubsub manager associated to the specified connection where
+	 * the pubsub requests require a specific to address for packets.
+	 * 
+	 * @param connection The XMPP connection
+	 * @param toAddress The pubsub specific to address (required for some servers)
+	 */
+	public PubSubManager(Connection connection, String toAddress)
+	{
+		con = connection;
+		to = toAddress;
+	}
+	
+	/**
+	 * Creates an instant node, if supported.
+	 * 
+	 * @return The node that was created
+	 * @exception XMPPException
+	 */
+	public LeafNode createNode()
+		throws XMPPException
+	{
+		PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.CREATE));
+		NodeExtension elem = (NodeExtension)reply.getExtension("create", PubSubNamespace.BASIC.getXmlns());
+		
+		LeafNode newNode = new LeafNode(con, elem.getNode());
+		newNode.setTo(to);
+		nodeMap.put(newNode.getId(), newNode);
+		
+		return newNode;
+	}
+	
+	/**
+	 * Creates a node with default configuration.
+	 * 
+	 * @param id The id of the node, which must be unique within the 
+	 * pubsub service
+	 * @return The node that was created
+	 * @exception XMPPException
+	 */
+	public LeafNode createNode(String id)
+		throws XMPPException
+	{
+		return (LeafNode)createNode(id, null);
+	}
+	
+	/**
+	 * Creates a node with specified configuration.
+	 * 
+	 * Note: This is the only way to create a collection node.
+	 * 
+	 * @param name The name of the node, which must be unique within the 
+	 * pubsub service
+	 * @param config The configuration for the node
+	 * @return The node that was created
+	 * @exception XMPPException
+	 */
+	public Node createNode(String name, Form config)
+		throws XMPPException
+	{
+		PubSub request = createPubsubPacket(to, Type.SET, new NodeExtension(PubSubElementType.CREATE, name));
+		boolean isLeafNode = true;
+		
+		if (config != null)
+		{
+			request.addExtension(new FormNode(FormNodeType.CONFIGURE, config));
+			FormField nodeTypeField = config.getField(ConfigureNodeFields.node_type.getFieldName());
+			
+			if (nodeTypeField != null)
+				isLeafNode = nodeTypeField.getValues().next().equals(NodeType.leaf.toString());
+		}
+
+		// Errors will cause exceptions in getReply, so it only returns
+		// on success.
+		sendPubsubPacket(con, to, Type.SET, request);
+		Node newNode = isLeafNode ? new LeafNode(con, name) : new CollectionNode(con, name);
+		newNode.setTo(to);
+		nodeMap.put(newNode.getId(), newNode);
+		
+		return newNode;
+	}
+
+	/**
+	 * Retrieves the requested node, if it exists.  It will throw an 
+	 * exception if it does not.
+	 * 
+	 * @param id - The unique id of the node
+	 * @return the node
+	 * @throws XMPPException The node does not exist
+	 */
+	public <T extends Node> T getNode(String id)
+		throws XMPPException
+	{
+		Node node = nodeMap.get(id);
+		
+		if (node == null)
+		{
+			DiscoverInfo info = new DiscoverInfo();
+			info.setTo(to);
+			info.setNode(id);
+			
+			DiscoverInfo infoReply = (DiscoverInfo)SyncPacketSend.getReply(con, info);
+			
+			if (infoReply.getIdentities().next().getType().equals(NodeType.leaf.toString()))
+				node = new LeafNode(con, id);
+			else
+				node = new CollectionNode(con, id);
+			node.setTo(to);
+			nodeMap.put(id, node);
+		}
+		return (T) node;
+	}
+	
+	/**
+	 * Get all the nodes that currently exist as a child of the specified
+	 * collection node.  If the service does not support collection nodes
+	 * then all nodes will be returned.
+	 * 
+	 * To retrieve contents of the root collection node (if it exists), 
+	 * or there is no root collection node, pass null as the nodeId.
+	 * 
+	 * @param nodeId - The id of the collection node for which the child 
+	 * nodes will be returned.  
+	 * @return {@link DiscoverItems} representing the existing nodes
+	 * 
+	 * @throws XMPPException
+	 */
+	public DiscoverItems discoverNodes(String nodeId)
+		throws XMPPException
+	{
+		DiscoverItems items = new DiscoverItems();
+		
+		if (nodeId != null)
+			items.setNode(nodeId);
+		items.setTo(to);
+		DiscoverItems nodeItems = (DiscoverItems)SyncPacketSend.getReply(con, items);
+		return nodeItems;
+	}
+	
+	/**
+	 * Gets the subscriptions on the root node.
+	 * 
+	 * @return List of exceptions
+	 * 
+	 * @throws XMPPException
+	 */
+	public List<Subscription> getSubscriptions()
+		throws XMPPException
+	{
+		Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS));
+		SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS.getElementName(), PubSubElementType.SUBSCRIPTIONS.getNamespace().getXmlns());
+		return subElem.getSubscriptions();
+	}
+	
+	/**
+	 * Gets the affiliations on the root node.
+	 * 
+	 * @return List of affiliations
+	 * 
+	 * @throws XMPPException
+	 */
+	public List<Affiliation> getAffiliations()
+		throws XMPPException
+	{
+		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.AFFILIATIONS));
+		AffiliationsExtension listElem = (AffiliationsExtension)reply.getExtension(PubSubElementType.AFFILIATIONS);
+		return listElem.getAffiliations();
+	}
+
+	/**
+	 * Delete the specified node
+	 * 
+	 * @param nodeId
+	 * @throws XMPPException
+	 */
+	public void deleteNode(String nodeId)
+		throws XMPPException
+	{
+		sendPubsubPacket(Type.SET, new NodeExtension(PubSubElementType.DELETE, nodeId), PubSubElementType.DELETE.getNamespace());
+		nodeMap.remove(nodeId);
+	}
+	
+	/**
+	 * Returns the default settings for Node configuration.
+	 * 
+	 * @return configuration form containing the default settings.
+	 */
+	public ConfigureForm getDefaultConfiguration()
+		throws XMPPException
+	{
+		// Errors will cause exceptions in getReply, so it only returns
+		// on success.
+		PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.DEFAULT), PubSubElementType.DEFAULT.getNamespace());
+		return NodeUtils.getFormFromPacket(reply, PubSubElementType.DEFAULT);
+	}
+	
+	/**
+	 * Gets the supported features of the servers pubsub implementation
+	 * as a standard {@link DiscoverInfo} instance.
+	 * 
+	 * @return The supported features
+	 * 
+	 * @throws XMPPException
+	 */
+	public DiscoverInfo getSupportedFeatures()
+		throws XMPPException
+	{
+		ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(con);
+		return mgr.discoverInfo(to);
+	}
+	
+	private Packet sendPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)
+		throws XMPPException
+	{
+		return sendPubsubPacket(con, to, type, ext, ns);
+	}
+
+	private Packet sendPubsubPacket(Type type, PacketExtension ext)
+		throws XMPPException
+	{
+		return sendPubsubPacket(type, ext, null);
+	}
+
+	static PubSub createPubsubPacket(String to, Type type, PacketExtension ext)
+	{
+		return createPubsubPacket(to, type, ext, null);
+	}
+	
+	static PubSub createPubsubPacket(String to, Type type, PacketExtension ext, PubSubNamespace ns)
+	{
+		PubSub request = new PubSub();
+		request.setTo(to);
+		request.setType(type);
+		
+		if (ns != null)
+		{
+			request.setPubSubNamespace(ns);
+		}
+		request.addExtension(ext);
+		
+		return request;
+	}
+
+	static Packet sendPubsubPacket(Connection con, String to, Type type, PacketExtension ext)
+		throws XMPPException
+	{
+		return sendPubsubPacket(con, to, type, ext, null);
+	}
+	
+	static Packet sendPubsubPacket(Connection con, String to, Type type, PacketExtension ext, PubSubNamespace ns)
+		throws XMPPException
+	{
+		return SyncPacketSend.getReply(con, createPubsubPacket(to, type, ext, ns));
+	}
+
+	static Packet sendPubsubPacket(Connection con, String to, Type type, PubSub packet)
+		throws XMPPException
+	{
+		return sendPubsubPacket(con, to, type, packet, null);
+	}
+
+	static Packet sendPubsubPacket(Connection con, String to, Type type, PubSub packet, PubSubNamespace ns)
+		throws XMPPException
+	{
+		return SyncPacketSend.getReply(con, packet);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishItem.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishItem.java
index ffbd705..15282ea 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishItem.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishItem.java
@@ -1,70 +1,70 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.ArrayList;

-import java.util.Collection;

-

-/**

- * Represents a request to publish an item(s) to a specific node.

- * 

- * @author Robin Collier

- */

-public class PublishItem <T extends Item> extends NodeExtension

-{

-	protected Collection<T> items;

-	

-	/**

-	 * Construct a request to publish an item to a node.

-	 * 

-	 * @param nodeId The node to publish to

-	 * @param toPublish The {@link Item} to publish

-	 */

-	public PublishItem(String nodeId, T toPublish)

-	{

-		super(PubSubElementType.PUBLISH, nodeId);

-		items = new ArrayList<T>(1);

-		items.add(toPublish);

-	}

-

-	/**

-	 * Construct a request to publish multiple items to a node.

-	 * 

-	 * @param nodeId The node to publish to

-	 * @param toPublish The list of {@link Item} to publish

-	 */

-	public PublishItem(String nodeId, Collection<T> toPublish)

-	{

-		super(PubSubElementType.PUBLISH, nodeId);

-		items = toPublish;

-	}

-

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		builder.append(" node='");

-		builder.append(getNode());

-		builder.append("'>");

-		

-		for (Item item : items)

-		{

-			builder.append(item.toXML());

-		}

-		builder.append("</publish>");

-		

-		return builder.toString();

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Represents a request to publish an item(s) to a specific node.
+ * 
+ * @author Robin Collier
+ */
+public class PublishItem <T extends Item> extends NodeExtension
+{
+	protected Collection<T> items;
+	
+	/**
+	 * Construct a request to publish an item to a node.
+	 * 
+	 * @param nodeId The node to publish to
+	 * @param toPublish The {@link Item} to publish
+	 */
+	public PublishItem(String nodeId, T toPublish)
+	{
+		super(PubSubElementType.PUBLISH, nodeId);
+		items = new ArrayList<T>(1);
+		items.add(toPublish);
+	}
+
+	/**
+	 * Construct a request to publish multiple items to a node.
+	 * 
+	 * @param nodeId The node to publish to
+	 * @param toPublish The list of {@link Item} to publish
+	 */
+	public PublishItem(String nodeId, Collection<T> toPublish)
+	{
+		super(PubSubElementType.PUBLISH, nodeId);
+		items = toPublish;
+	}
+
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		builder.append(" node='");
+		builder.append(getNode());
+		builder.append("'>");
+		
+		for (Item item : items)
+		{
+			builder.append(item.toXML());
+		}
+		builder.append("</publish>");
+		
+		return builder.toString();
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishModel.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishModel.java
index 4b5a851..b94d50f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishModel.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/PublishModel.java
@@ -1,32 +1,32 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * Determines who may publish to a node.  Denotes possible values 

- * for {@link ConfigureForm#setPublishModel(PublishModel)}.

- * 

- * @author Robin Collier

- */

-public enum PublishModel

-{

-	/** Only publishers may publish */

-	publishers,

-	

-	/** Only subscribers may publish */

-	subscribers,

-	

-	/** Anyone may publish */

-	open;

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * Determines who may publish to a node.  Denotes possible values 
+ * for {@link ConfigureForm#setPublishModel(PublishModel)}.
+ * 
+ * @author Robin Collier
+ */
+public enum PublishModel
+{
+	/** Only publishers may publish */
+	publishers,
+	
+	/** Only subscribers may publish */
+	subscribers,
+	
+	/** Anyone may publish */
+	open;
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/RetractItem.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/RetractItem.java
index 97db5cc..1e6cc49 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/RetractItem.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/RetractItem.java
@@ -1,59 +1,59 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-

-/**

- * Represents and item that has been deleted from a node.

- * 

- * @author Robin Collier

- */

-public class RetractItem implements PacketExtension

-{

-	private String id;

-

-	/**

-	 * Construct a <tt>RetractItem</tt> with the specified id.

-	 * 

-	 * @param itemId The id if the item deleted

-	 */

-	public RetractItem(String itemId)

-	{

-		if (itemId == null)

-			throw new IllegalArgumentException("itemId must not be 'null'");

-		id = itemId;

-	}

-	

-	public String getId()

-	{

-		return id;

-	}

-

-	public String getElementName()

-	{

-		return "retract";

-	}

-

-	public String getNamespace()

-	{

-		return PubSubNamespace.EVENT.getXmlns();

-	}

-

-	public String toXML()

-	{

-		return "<retract id='" + id + "'/>";

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+
+/**
+ * Represents and item that has been deleted from a node.
+ * 
+ * @author Robin Collier
+ */
+public class RetractItem implements PacketExtension
+{
+	private String id;
+
+	/**
+	 * Construct a <tt>RetractItem</tt> with the specified id.
+	 * 
+	 * @param itemId The id if the item deleted
+	 */
+	public RetractItem(String itemId)
+	{
+		if (itemId == null)
+			throw new IllegalArgumentException("itemId must not be 'null'");
+		id = itemId;
+	}
+	
+	public String getId()
+	{
+		return id;
+	}
+
+	public String getElementName()
+	{
+		return "retract";
+	}
+
+	public String getNamespace()
+	{
+		return PubSubNamespace.EVENT.getXmlns();
+	}
+
+	public String toXML()
+	{
+		return "<retract id='" + id + "'/>";
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SimplePayload.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SimplePayload.java
index 9d114b0..644c1f3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SimplePayload.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SimplePayload.java
@@ -1,65 +1,65 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * The default payload representation for {@link Item#getPayload()}.  It simply 

- * stores the XML payload as a string.

- *  

- * @author Robin Collier

- */

-public class SimplePayload implements PacketExtension

-{

-	private String elemName;

-	private String ns;

-	private String payload;

-	

-	/**

-	 * Construct a <tt>SimplePayload</tt> object with the specified element name, 

-	 * namespace and content.  The content must be well formed XML.

-	 * 

-	 * @param elementName The root element name (of the payload)

-	 * @param namespace The namespace of the payload, null if there is none

-	 * @param xmlPayload The payload data

-	 */

-	public SimplePayload(String elementName, String namespace, String xmlPayload)

-	{

-		elemName = elementName;

-		payload = xmlPayload;

-		ns = namespace;

-	}

-

-	public String getElementName()

-	{

-		return elemName;

-	}

-

-	public String getNamespace()

-	{

-		return ns;

-	}

-

-	public String toXML()

-	{

-		return payload;

-	}

-

-	@Override

-	public String toString()

-	{

-		return getClass().getName() + "payload [" + toXML() + "]";

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * The default payload representation for {@link Item#getPayload()}.  It simply 
+ * stores the XML payload as a string.
+ *  
+ * @author Robin Collier
+ */
+public class SimplePayload implements PacketExtension
+{
+	private String elemName;
+	private String ns;
+	private String payload;
+	
+	/**
+	 * Construct a <tt>SimplePayload</tt> object with the specified element name, 
+	 * namespace and content.  The content must be well formed XML.
+	 * 
+	 * @param elementName The root element name (of the payload)
+	 * @param namespace The namespace of the payload, null if there is none
+	 * @param xmlPayload The payload data
+	 */
+	public SimplePayload(String elementName, String namespace, String xmlPayload)
+	{
+		elemName = elementName;
+		payload = xmlPayload;
+		ns = namespace;
+	}
+
+	public String getElementName()
+	{
+		return elemName;
+	}
+
+	public String getNamespace()
+	{
+		return ns;
+	}
+
+	public String toXML()
+	{
+		return payload;
+	}
+
+	@Override
+	public String toString()
+	{
+		return getClass().getName() + "payload [" + toXML() + "]";
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeExtension.java
index daf8db7..5245ece 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeExtension.java
@@ -1,60 +1,60 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * Represents a request to subscribe to a node.

- * 

- * @author Robin Collier

- */

-public class SubscribeExtension extends NodeExtension

-{

-	protected String jid;

-	

-	public SubscribeExtension(String subscribeJid)

-	{

-		super(PubSubElementType.SUBSCRIBE);

-		jid = subscribeJid;

-	}

-	

-	public SubscribeExtension(String subscribeJid, String nodeId)

-	{

-		super(PubSubElementType.SUBSCRIBE, nodeId);

-		jid = subscribeJid;

-	}

-

-	public String getJid()

-	{

-		return jid;

-	}

-

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		

-		if (getNode() != null)

-		{

-			builder.append(" node='");

-			builder.append(getNode());

-			builder.append("'");

-		}

-		builder.append(" jid='");

-		builder.append(getJid());

-		builder.append("'/>");

-		

-		return builder.toString();

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * Represents a request to subscribe to a node.
+ * 
+ * @author Robin Collier
+ */
+public class SubscribeExtension extends NodeExtension
+{
+	protected String jid;
+	
+	public SubscribeExtension(String subscribeJid)
+	{
+		super(PubSubElementType.SUBSCRIBE);
+		jid = subscribeJid;
+	}
+	
+	public SubscribeExtension(String subscribeJid, String nodeId)
+	{
+		super(PubSubElementType.SUBSCRIBE, nodeId);
+		jid = subscribeJid;
+	}
+
+	public String getJid()
+	{
+		return jid;
+	}
+
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		
+		if (getNode() != null)
+		{
+			builder.append(" node='");
+			builder.append(getNode());
+			builder.append("'");
+		}
+		builder.append(" jid='");
+		builder.append(getJid());
+		builder.append("'/>");
+		
+		return builder.toString();
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeForm.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeForm.java
index 53f2606..71cad14 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeForm.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeForm.java
@@ -1,241 +1,241 @@
-/**

- * 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.smackx.pubsub;

-

-import java.text.ParseException;

-import java.text.SimpleDateFormat;

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Date;

-import java.util.Iterator;

-import java.util.UnknownFormatConversionException;

-

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.packet.DataForm;

-

-/**

- * A decorator for a {@link Form} to easily enable reading and updating

- * of subscription options.  All operations read or update the underlying {@link DataForm}.

- * 

- * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not

- * exist, all <b>SubscribeForm.setXXX</b> methods will create the field in the wrapped form

- * if it does not already exist.

- * 

- * @author Robin Collier

- */

-public class SubscribeForm extends Form

-{	

-	public SubscribeForm(DataForm configDataForm)

-	{

-		super(configDataForm);

-	}

-	

-	public SubscribeForm(Form subscribeOptionsForm)

-	{

-		super(subscribeOptionsForm.getDataFormToSend());

-	}

-	

-	public SubscribeForm(FormType formType)

-	{

-		super(formType.toString());

-	}

-	

-	/**

-	 * Determines if an entity wants to receive notifications.

-	 * 

-	 * @return true if want to receive, false otherwise

-	 */

-	public boolean isDeliverOn()

-	{

-		return parseBoolean(getFieldValue(SubscribeOptionFields.deliver));

-	}

-	

-	/**

-	 * Sets whether an entity wants to receive notifications.

-	 *

-	 * @param deliverNotifications

-	 */

-	public void setDeliverOn(boolean deliverNotifications)

-	{

-		addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);

-		setAnswer(SubscribeOptionFields.deliver.getFieldName(), deliverNotifications);

-	}

-

-	/**

-	 * Determines if notifications should be delivered as aggregations or not.

-	 * 

-	 * @return true to aggregate, false otherwise

-	 */

-	public boolean isDigestOn()

-	{

-		return parseBoolean(getFieldValue(SubscribeOptionFields.digest));

-	}

-	

-	/**

-	 * Sets whether notifications should be delivered as aggregations or not.

-	 * 

-	 * @param digestOn true to aggregate, false otherwise 

-	 */

-	public void setDigestOn(boolean digestOn)

-	{

-		addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);

-		setAnswer(SubscribeOptionFields.deliver.getFieldName(), digestOn);

-	}

-

-	/**

-	 * Gets the minimum number of milliseconds between sending notification digests

-	 * 

-	 * @return The frequency in milliseconds

-	 */

-	public int getDigestFrequency()

-	{

-		return Integer.parseInt(getFieldValue(SubscribeOptionFields.digest_frequency));

-	}

-

-	/**

-	 * Sets the minimum number of milliseconds between sending notification digests

-	 * 

-	 * @param frequency The frequency in milliseconds

-	 */

-	public void setDigestFrequency(int frequency)

-	{

-		addField(SubscribeOptionFields.digest_frequency, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(SubscribeOptionFields.digest_frequency.getFieldName(), frequency);

-	}

-

-	/**

-	 * Get the time at which the leased subscription will expire, or has expired.

-	 * 

-	 * @return The expiry date

-	 */

-	public Date getExpiry()

-	{

-		String dateTime = getFieldValue(SubscribeOptionFields.expire);

-		try

-		{

-			return StringUtils.parseDate(dateTime);

-		}

-		catch (ParseException e)

-		{

-			UnknownFormatConversionException exc = new UnknownFormatConversionException(dateTime);

-			exc.initCause(e);

-			throw exc;

-		}

-	}

-	

-	/**

-	 * Sets the time at which the leased subscription will expire, or has expired.

-	 * 

-	 * @param expire The expiry date

-	 */

-	public void setExpiry(Date expire)

-	{

-		addField(SubscribeOptionFields.expire, FormField.TYPE_TEXT_SINGLE);

-		setAnswer(SubscribeOptionFields.expire.getFieldName(), StringUtils.formatXEP0082Date(expire));

-	}

-	

-	/**

-	 * Determines whether the entity wants to receive an XMPP message body in 

-	 * addition to the payload format.

-	 * 

-	 * @return true to receive the message body, false otherwise

-	 */

-	public boolean isIncludeBody()

-	{

-		return parseBoolean(getFieldValue(SubscribeOptionFields.include_body));

-	}

-	

-	/**

-	 * Sets whether the entity wants to receive an XMPP message body in 

-	 * addition to the payload format.

-	 * 

-	 * @param include true to receive the message body, false otherwise

-	 */

-	public void setIncludeBody(boolean include)

-	{

-		addField(SubscribeOptionFields.include_body, FormField.TYPE_BOOLEAN);

-		setAnswer(SubscribeOptionFields.include_body.getFieldName(), include);

-	}

-

-	/**

-	 * Gets the {@link PresenceState} for which an entity wants to receive 

-	 * notifications.

-	 * 

-	 * @return iterator over the list of states

-	 */

-	public Iterator<PresenceState> getShowValues()

-	{

-		ArrayList<PresenceState> result = new ArrayList<PresenceState>(5);

-		Iterator<String > it = getFieldValues(SubscribeOptionFields.show_values);

-		

-		while (it.hasNext())

-		{

-			String state = it.next();

-			result.add(PresenceState.valueOf(state));

-		}

-		return result.iterator();

-	}

-	

-	/**

-	 * Sets the list of {@link PresenceState} for which an entity wants

-	 * to receive notifications.

-	 * 

-	 * @param stateValues The list of states

-	 */

-	public void setShowValues(Collection<PresenceState> stateValues)

-	{

-		ArrayList<String> values = new ArrayList<String>(stateValues.size());

-		

-		for (PresenceState state : stateValues)

-		{

-			values.add(state.toString());

-		}

-		addField(SubscribeOptionFields.show_values, FormField.TYPE_LIST_MULTI);

-		setAnswer(SubscribeOptionFields.show_values.getFieldName(), values);

-	}

-	

-	

-	static private boolean parseBoolean(String fieldValue)

-	{

-		return ("1".equals(fieldValue) || "true".equals(fieldValue));

-	}

-

-	private String getFieldValue(SubscribeOptionFields field)

-	{

-		FormField formField = getField(field.getFieldName());

-		

-		return formField.getValues().next();

-	}

-

-	private Iterator<String> getFieldValues(SubscribeOptionFields field)

-	{

-		FormField formField = getField(field.getFieldName());

-		

-		return formField.getValues();

-	}

-

-	private void addField(SubscribeOptionFields nodeField, String type)

-	{

-		String fieldName = nodeField.getFieldName();

-		

-		if (getField(fieldName) == null)

-		{

-			FormField field = new FormField(fieldName);

-			field.setType(type);

-			addField(field);

-		}

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.UnknownFormatConversionException;
+
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.packet.DataForm;
+
+/**
+ * A decorator for a {@link Form} to easily enable reading and updating
+ * of subscription options.  All operations read or update the underlying {@link DataForm}.
+ * 
+ * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
+ * exist, all <b>SubscribeForm.setXXX</b> methods will create the field in the wrapped form
+ * if it does not already exist.
+ * 
+ * @author Robin Collier
+ */
+public class SubscribeForm extends Form
+{	
+	public SubscribeForm(DataForm configDataForm)
+	{
+		super(configDataForm);
+	}
+	
+	public SubscribeForm(Form subscribeOptionsForm)
+	{
+		super(subscribeOptionsForm.getDataFormToSend());
+	}
+	
+	public SubscribeForm(FormType formType)
+	{
+		super(formType.toString());
+	}
+	
+	/**
+	 * Determines if an entity wants to receive notifications.
+	 * 
+	 * @return true if want to receive, false otherwise
+	 */
+	public boolean isDeliverOn()
+	{
+		return parseBoolean(getFieldValue(SubscribeOptionFields.deliver));
+	}
+	
+	/**
+	 * Sets whether an entity wants to receive notifications.
+	 *
+	 * @param deliverNotifications
+	 */
+	public void setDeliverOn(boolean deliverNotifications)
+	{
+		addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);
+		setAnswer(SubscribeOptionFields.deliver.getFieldName(), deliverNotifications);
+	}
+
+	/**
+	 * Determines if notifications should be delivered as aggregations or not.
+	 * 
+	 * @return true to aggregate, false otherwise
+	 */
+	public boolean isDigestOn()
+	{
+		return parseBoolean(getFieldValue(SubscribeOptionFields.digest));
+	}
+	
+	/**
+	 * Sets whether notifications should be delivered as aggregations or not.
+	 * 
+	 * @param digestOn true to aggregate, false otherwise 
+	 */
+	public void setDigestOn(boolean digestOn)
+	{
+		addField(SubscribeOptionFields.deliver, FormField.TYPE_BOOLEAN);
+		setAnswer(SubscribeOptionFields.deliver.getFieldName(), digestOn);
+	}
+
+	/**
+	 * Gets the minimum number of milliseconds between sending notification digests
+	 * 
+	 * @return The frequency in milliseconds
+	 */
+	public int getDigestFrequency()
+	{
+		return Integer.parseInt(getFieldValue(SubscribeOptionFields.digest_frequency));
+	}
+
+	/**
+	 * Sets the minimum number of milliseconds between sending notification digests
+	 * 
+	 * @param frequency The frequency in milliseconds
+	 */
+	public void setDigestFrequency(int frequency)
+	{
+		addField(SubscribeOptionFields.digest_frequency, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(SubscribeOptionFields.digest_frequency.getFieldName(), frequency);
+	}
+
+	/**
+	 * Get the time at which the leased subscription will expire, or has expired.
+	 * 
+	 * @return The expiry date
+	 */
+	public Date getExpiry()
+	{
+		String dateTime = getFieldValue(SubscribeOptionFields.expire);
+		try
+		{
+			return StringUtils.parseDate(dateTime);
+		}
+		catch (ParseException e)
+		{
+			UnknownFormatConversionException exc = new UnknownFormatConversionException(dateTime);
+			exc.initCause(e);
+			throw exc;
+		}
+	}
+	
+	/**
+	 * Sets the time at which the leased subscription will expire, or has expired.
+	 * 
+	 * @param expire The expiry date
+	 */
+	public void setExpiry(Date expire)
+	{
+		addField(SubscribeOptionFields.expire, FormField.TYPE_TEXT_SINGLE);
+		setAnswer(SubscribeOptionFields.expire.getFieldName(), StringUtils.formatXEP0082Date(expire));
+	}
+	
+	/**
+	 * Determines whether the entity wants to receive an XMPP message body in 
+	 * addition to the payload format.
+	 * 
+	 * @return true to receive the message body, false otherwise
+	 */
+	public boolean isIncludeBody()
+	{
+		return parseBoolean(getFieldValue(SubscribeOptionFields.include_body));
+	}
+	
+	/**
+	 * Sets whether the entity wants to receive an XMPP message body in 
+	 * addition to the payload format.
+	 * 
+	 * @param include true to receive the message body, false otherwise
+	 */
+	public void setIncludeBody(boolean include)
+	{
+		addField(SubscribeOptionFields.include_body, FormField.TYPE_BOOLEAN);
+		setAnswer(SubscribeOptionFields.include_body.getFieldName(), include);
+	}
+
+	/**
+	 * Gets the {@link PresenceState} for which an entity wants to receive 
+	 * notifications.
+	 * 
+	 * @return iterator over the list of states
+	 */
+	public Iterator<PresenceState> getShowValues()
+	{
+		ArrayList<PresenceState> result = new ArrayList<PresenceState>(5);
+		Iterator<String > it = getFieldValues(SubscribeOptionFields.show_values);
+		
+		while (it.hasNext())
+		{
+			String state = it.next();
+			result.add(PresenceState.valueOf(state));
+		}
+		return result.iterator();
+	}
+	
+	/**
+	 * Sets the list of {@link PresenceState} for which an entity wants
+	 * to receive notifications.
+	 * 
+	 * @param stateValues The list of states
+	 */
+	public void setShowValues(Collection<PresenceState> stateValues)
+	{
+		ArrayList<String> values = new ArrayList<String>(stateValues.size());
+		
+		for (PresenceState state : stateValues)
+		{
+			values.add(state.toString());
+		}
+		addField(SubscribeOptionFields.show_values, FormField.TYPE_LIST_MULTI);
+		setAnswer(SubscribeOptionFields.show_values.getFieldName(), values);
+	}
+	
+	
+	static private boolean parseBoolean(String fieldValue)
+	{
+		return ("1".equals(fieldValue) || "true".equals(fieldValue));
+	}
+
+	private String getFieldValue(SubscribeOptionFields field)
+	{
+		FormField formField = getField(field.getFieldName());
+		
+		return formField.getValues().next();
+	}
+
+	private Iterator<String> getFieldValues(SubscribeOptionFields field)
+	{
+		FormField formField = getField(field.getFieldName());
+		
+		return formField.getValues();
+	}
+
+	private void addField(SubscribeOptionFields nodeField, String type)
+	{
+		String fieldName = nodeField.getFieldName();
+		
+		if (getField(fieldName) == null)
+		{
+			FormField field = new FormField(fieldName);
+			field.setType(type);
+			addField(field);
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java
index dfca601..463a40f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java
@@ -1,99 +1,99 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Calendar;

-

-/**

- * Defines the possible field options for a subscribe options form as defined 

- * by <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-subscribe">Section 16.4.2</a>.

- * 

- * @author Robin Collier

- */

-public enum SubscribeOptionFields

-{

-	/**

-	 * Whether an entity wants to receive or disable notifications

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	deliver,

-

-	/**

-	 * Whether an entity wants to receive digests (aggregations) of 

-	 * notifications or all notifications individually.

-	 * 

-	 * <p><b>Value: boolean</b></p>

-	 */

-	digest,

-	

-	/**

-	 * The minimum number of seconds between sending any two notifications digests

-	 * 

-	 * <p><b>Value: int</b></p>

-	 */

-	digest_frequency,

-

-	/**

-	 * The DateTime at which a leased subsscription will end ro has ended.

-	 * 

-	 * <p><b>Value: {@link Calendar}</b></p>

-	 */

-	expire,

-

-	/**

-	 * Whether an entity wants to receive an XMPP message body in addition to 

-	 * the payload format.

-	 *

-	 * <p><b>Value: boolean</b></p>

-	 */

-	include_body,

-	

-	/**

-	 * The presence states for which an entity wants to receive notifications.

-	 *

-	 * <p><b>Value: {@link PresenceState}</b></p>

-	 */

-	show_values,

-	

-	/**

-	 * 

-	 * 

-	 * <p><b>Value: </b></p>

-	 */

-	subscription_type,

-	

-	/**

-	 * 

-	 * <p><b>Value: </b></p>

-	 */

-	subscription_depth;

-	

-	public String getFieldName()

-	{

-		if (this == show_values)

-			return "pubsub#" + toString().replace('_', '-');

-		return "pubsub#" + toString();

-	}

-	

-	static public SubscribeOptionFields valueOfFromElement(String elementName)

-	{

-		String portion = elementName.substring(elementName.lastIndexOf('#' + 1));

-		

-		if ("show-values".equals(portion))

-			return show_values;

-		else

-			return valueOf(portion);

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Calendar;
+
+/**
+ * Defines the possible field options for a subscribe options form as defined 
+ * by <a href="http://xmpp.org/extensions/xep-0060.html#registrar-formtypes-subscribe">Section 16.4.2</a>.
+ * 
+ * @author Robin Collier
+ */
+public enum SubscribeOptionFields
+{
+	/**
+	 * Whether an entity wants to receive or disable notifications
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	deliver,
+
+	/**
+	 * Whether an entity wants to receive digests (aggregations) of 
+	 * notifications or all notifications individually.
+	 * 
+	 * <p><b>Value: boolean</b></p>
+	 */
+	digest,
+	
+	/**
+	 * The minimum number of seconds between sending any two notifications digests
+	 * 
+	 * <p><b>Value: int</b></p>
+	 */
+	digest_frequency,
+
+	/**
+	 * The DateTime at which a leased subsscription will end ro has ended.
+	 * 
+	 * <p><b>Value: {@link Calendar}</b></p>
+	 */
+	expire,
+
+	/**
+	 * Whether an entity wants to receive an XMPP message body in addition to 
+	 * the payload format.
+	 *
+	 * <p><b>Value: boolean</b></p>
+	 */
+	include_body,
+	
+	/**
+	 * The presence states for which an entity wants to receive notifications.
+	 *
+	 * <p><b>Value: {@link PresenceState}</b></p>
+	 */
+	show_values,
+	
+	/**
+	 * 
+	 * 
+	 * <p><b>Value: </b></p>
+	 */
+	subscription_type,
+	
+	/**
+	 * 
+	 * <p><b>Value: </b></p>
+	 */
+	subscription_depth;
+	
+	public String getFieldName()
+	{
+		if (this == show_values)
+			return "pubsub#" + toString().replace('_', '-');
+		return "pubsub#" + toString();
+	}
+	
+	static public SubscribeOptionFields valueOfFromElement(String elementName)
+	{
+		String portion = elementName.substring(elementName.lastIndexOf('#' + 1));
+		
+		if ("show-values".equals(portion))
+			return show_values;
+		else
+			return valueOf(portion);
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Subscription.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Subscription.java
index 19ad8a8..49d9a4a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Subscription.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/Subscription.java
@@ -1,160 +1,160 @@
-/**

- * 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.smackx.pubsub;

-

-/**

- * Represents a subscription to node for both requests and replies.

- * 

- * @author Robin Collier

- */

-public class Subscription extends NodeExtension

-{

-	protected String jid;

-	protected String id;

-	protected State state;

-	protected boolean configRequired = false;

-	

-	public enum State

-	{

-		subscribed, unconfigured, pending, none 

-	}

-

-	/**

-	 * Used to constructs a subscription request to the root node with the specified

-	 * JID.

-	 * 

-	 * @param subscriptionJid The subscriber JID

-	 */

-	public Subscription(String subscriptionJid)

-	{

-		this(subscriptionJid, null, null, null);

-	}

-	

-	/**

-	 * Used to constructs a subscription request to the specified node with the specified

-	 * JID.

-	 * 

-	 * @param subscriptionJid The subscriber JID

-	 * @param nodeId The node id

-	 */

-	public Subscription(String subscriptionJid, String nodeId)

-	{

-		this(subscriptionJid, nodeId, null, null);

-	}

-	

-	/**

-	 * Constructs a representation of a subscription reply to the specified node 

-	 * and JID.  The server	will have supplied the subscription id and current state.

-	 * 

-	 * @param jid The JID the request was made under

-	 * @param nodeId The node subscribed to

-	 * @param subscriptionId The id of this subscription

-	 * @param state The current state of the subscription

-	 */

-	public Subscription(String jid, String nodeId, String subscriptionId, State state)

-	{

-		super(PubSubElementType.SUBSCRIPTION, nodeId);

-		this.jid = jid;

-		id = subscriptionId;

-		this.state = state;

-	}

-	

-	/**

-	 * Constructs a representation of a subscription reply to the specified node 

-	 * and JID.  The server	will have supplied the subscription id and current state

-	 * and whether the subscription need to be configured.

-	 * 

-	 * @param jid The JID the request was made under

-	 * @param nodeId The node subscribed to

-	 * @param subscriptionId The id of this subscription

-	 * @param state The current state of the subscription

-	 * @param configRequired Is configuration required to complete the subscription 

-	 */

-	public Subscription(String jid, String nodeId, String subscriptionId, State state, boolean configRequired)

-	{

-		super(PubSubElementType.SUBSCRIPTION, nodeId);

-		this.jid = jid;

-		id = subscriptionId;

-		this.state = state;

-		this.configRequired = configRequired;

-	}

-	

-	/**

-	 * Gets the JID the subscription is created for

-	 * 

-	 * @return The JID

-	 */

-	public String getJid()

-	{

-		return jid;

-	}

-	

-	/**

-	 * Gets the subscription id

-	 * 

-	 * @return The subscription id

-	 */

-	public String getId()

-	{

-		return id;

-	}

-	

-	/**

-	 * Gets the current subscription state.

-	 * 

-	 * @return Current subscription state

-	 */

-	public State getState()

-	{

-		return state;

-	}

-

-	/**

-	 * This value is only relevant when the {@link #getState()} is {@link State#unconfigured}

-	 * 

-	 * @return true if configuration is required, false otherwise

-	 */

-	public boolean isConfigRequired()

-	{

-		return configRequired;

-	}

-	

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<subscription");

-		appendAttribute(builder, "jid", jid);

-		

-		if (getNode() != null)

-			appendAttribute(builder, "node", getNode());

-		

-		if (id != null)

-			appendAttribute(builder, "subid", id);

-		

-		if (state != null)

-			appendAttribute(builder, "subscription", state.toString());

-		

-		builder.append("/>");

-		return builder.toString();

-	}

-

-	private void appendAttribute(StringBuilder builder, String att, String value)

-	{

-		builder.append(" ");

-		builder.append(att);

-		builder.append("='");

-		builder.append(value);

-		builder.append("'");

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+/**
+ * Represents a subscription to node for both requests and replies.
+ * 
+ * @author Robin Collier
+ */
+public class Subscription extends NodeExtension
+{
+	protected String jid;
+	protected String id;
+	protected State state;
+	protected boolean configRequired = false;
+	
+	public enum State
+	{
+		subscribed, unconfigured, pending, none 
+	}
+
+	/**
+	 * Used to constructs a subscription request to the root node with the specified
+	 * JID.
+	 * 
+	 * @param subscriptionJid The subscriber JID
+	 */
+	public Subscription(String subscriptionJid)
+	{
+		this(subscriptionJid, null, null, null);
+	}
+	
+	/**
+	 * Used to constructs a subscription request to the specified node with the specified
+	 * JID.
+	 * 
+	 * @param subscriptionJid The subscriber JID
+	 * @param nodeId The node id
+	 */
+	public Subscription(String subscriptionJid, String nodeId)
+	{
+		this(subscriptionJid, nodeId, null, null);
+	}
+	
+	/**
+	 * Constructs a representation of a subscription reply to the specified node 
+	 * and JID.  The server	will have supplied the subscription id and current state.
+	 * 
+	 * @param jid The JID the request was made under
+	 * @param nodeId The node subscribed to
+	 * @param subscriptionId The id of this subscription
+	 * @param state The current state of the subscription
+	 */
+	public Subscription(String jid, String nodeId, String subscriptionId, State state)
+	{
+		super(PubSubElementType.SUBSCRIPTION, nodeId);
+		this.jid = jid;
+		id = subscriptionId;
+		this.state = state;
+	}
+	
+	/**
+	 * Constructs a representation of a subscription reply to the specified node 
+	 * and JID.  The server	will have supplied the subscription id and current state
+	 * and whether the subscription need to be configured.
+	 * 
+	 * @param jid The JID the request was made under
+	 * @param nodeId The node subscribed to
+	 * @param subscriptionId The id of this subscription
+	 * @param state The current state of the subscription
+	 * @param configRequired Is configuration required to complete the subscription 
+	 */
+	public Subscription(String jid, String nodeId, String subscriptionId, State state, boolean configRequired)
+	{
+		super(PubSubElementType.SUBSCRIPTION, nodeId);
+		this.jid = jid;
+		id = subscriptionId;
+		this.state = state;
+		this.configRequired = configRequired;
+	}
+	
+	/**
+	 * Gets the JID the subscription is created for
+	 * 
+	 * @return The JID
+	 */
+	public String getJid()
+	{
+		return jid;
+	}
+	
+	/**
+	 * Gets the subscription id
+	 * 
+	 * @return The subscription id
+	 */
+	public String getId()
+	{
+		return id;
+	}
+	
+	/**
+	 * Gets the current subscription state.
+	 * 
+	 * @return Current subscription state
+	 */
+	public State getState()
+	{
+		return state;
+	}
+
+	/**
+	 * This value is only relevant when the {@link #getState()} is {@link State#unconfigured}
+	 * 
+	 * @return true if configuration is required, false otherwise
+	 */
+	public boolean isConfigRequired()
+	{
+		return configRequired;
+	}
+	
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<subscription");
+		appendAttribute(builder, "jid", jid);
+		
+		if (getNode() != null)
+			appendAttribute(builder, "node", getNode());
+		
+		if (id != null)
+			appendAttribute(builder, "subid", id);
+		
+		if (state != null)
+			appendAttribute(builder, "subscription", state.toString());
+		
+		builder.append("/>");
+		return builder.toString();
+	}
+
+	private void appendAttribute(StringBuilder builder, String att, String value)
+	{
+		builder.append(" ");
+		builder.append(att);
+		builder.append("='");
+		builder.append(value);
+		builder.append("'");
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java
index 99f18d5..a721452 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java
@@ -1,75 +1,75 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Collections;

-import java.util.List;

-

-/**

- * Base class to represents events that are associated to subscriptions.

- * 

- * @author Robin Collier

- */

-abstract public class SubscriptionEvent extends NodeEvent

-{

-	private List<String> subIds = Collections.EMPTY_LIST;

-

-	/**

-	 * Construct an event with no subscription id's.  This can 

-	 * occur when there is only one subscription to a node.  The

-	 * event may or may not report the subscription id along 

-	 * with the event.

-	 * 

-	 * @param nodeId The id of the node the event came from

-	 */

-	protected SubscriptionEvent(String nodeId)

-	{

-		super(nodeId);

-	}

-

-	/**

-	 * Construct an event with multiple subscriptions.

-	 * 

-	 * @param nodeId The id of the node the event came from

-	 * @param subscriptionIds The list of subscription id's

-	 */

-	protected SubscriptionEvent(String nodeId, List<String> subscriptionIds)

-	{

-		super(nodeId);

-		

-		if (subscriptionIds != null)

-			subIds = subscriptionIds;

-	}

-

-	/** 

-	 * Get the subscriptions this event is associated with.

-	 * 

-	 * @return List of subscription id's

-	 */

-	public List<String> getSubscriptions()

-	{

-		return Collections.unmodifiableList(subIds);

-	}

-	

-	/**

-	 * Set the list of subscription id's for this event.

-	 * 

-	 * @param subscriptionIds The list of subscription id's

-	 */

-	protected void setSubscriptions(List<String> subscriptionIds)

-	{

-		if (subscriptionIds != null)

-			subIds = subscriptionIds;

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Base class to represents events that are associated to subscriptions.
+ * 
+ * @author Robin Collier
+ */
+abstract public class SubscriptionEvent extends NodeEvent
+{
+	private List<String> subIds = Collections.EMPTY_LIST;
+
+	/**
+	 * Construct an event with no subscription id's.  This can 
+	 * occur when there is only one subscription to a node.  The
+	 * event may or may not report the subscription id along 
+	 * with the event.
+	 * 
+	 * @param nodeId The id of the node the event came from
+	 */
+	protected SubscriptionEvent(String nodeId)
+	{
+		super(nodeId);
+	}
+
+	/**
+	 * Construct an event with multiple subscriptions.
+	 * 
+	 * @param nodeId The id of the node the event came from
+	 * @param subscriptionIds The list of subscription id's
+	 */
+	protected SubscriptionEvent(String nodeId, List<String> subscriptionIds)
+	{
+		super(nodeId);
+		
+		if (subscriptionIds != null)
+			subIds = subscriptionIds;
+	}
+
+	/** 
+	 * Get the subscriptions this event is associated with.
+	 * 
+	 * @return List of subscription id's
+	 */
+	public List<String> getSubscriptions()
+	{
+		return Collections.unmodifiableList(subIds);
+	}
+	
+	/**
+	 * Set the list of subscription id's for this event.
+	 * 
+	 * @param subscriptionIds The list of subscription id's
+	 */
+	protected void setSubscriptions(List<String> subscriptionIds)
+	{
+		if (subscriptionIds != null)
+			subIds = subscriptionIds;
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java
index a28cbe2..396b36d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java
@@ -1,96 +1,96 @@
-/**

- * 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.smackx.pubsub;

-

-import java.util.Collections;

-import java.util.List;

-

-/**

- * Represents the element holding the list of subscription elements.

- * 

- * @author Robin Collier

- */

-public class SubscriptionsExtension extends NodeExtension

-{

-	protected List<Subscription> items = Collections.EMPTY_LIST;

-	

-	/**

-	 * Subscriptions to the root node

-	 * 

-	 * @param subList The list of subscriptions

-	 */

-	public SubscriptionsExtension(List<Subscription> subList)

-	{

-		super(PubSubElementType.SUBSCRIPTIONS);

-		

-		if (subList != null)

-			items = subList;

-	}

-

-	/**

-	 * Subscriptions to the specified node.

-	 * 

-	 * @param nodeId The node subscribed to

-	 * @param subList The list of subscriptions

-	 */

-	public SubscriptionsExtension(String nodeId, List<Subscription> subList)

-	{

-		super(PubSubElementType.SUBSCRIPTIONS, nodeId);

-

-		if (subList != null)

-			items = subList;

-	}

-

-	/**

-	 * Gets the list of subscriptions.

-	 * 

-	 * @return List of subscriptions

-	 */

-	public List<Subscription> getSubscriptions()

-	{

-		return items;

-	}

-

-	@Override

-	public String toXML()

-	{

-		if ((items == null) || (items.size() == 0))

-		{

-			return super.toXML();

-		}

-		else

-		{

-			StringBuilder builder = new StringBuilder("<");

-			builder.append(getElementName());

-			

-			if (getNode() != null)

-			{

-				builder.append(" node='");

-				builder.append(getNode());

-				builder.append("'");

-			}

-			builder.append(">");

-			

-			for (Subscription item : items)

-			{

-				builder.append(item.toXML());

-			}

-			

-			builder.append("</");

-			builder.append(getElementName());

-			builder.append(">");

-			return builder.toString();

-		}

-	}

-}

+/**
+ * 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.smackx.pubsub;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents the element holding the list of subscription elements.
+ * 
+ * @author Robin Collier
+ */
+public class SubscriptionsExtension extends NodeExtension
+{
+	protected List<Subscription> items = Collections.EMPTY_LIST;
+	
+	/**
+	 * Subscriptions to the root node
+	 * 
+	 * @param subList The list of subscriptions
+	 */
+	public SubscriptionsExtension(List<Subscription> subList)
+	{
+		super(PubSubElementType.SUBSCRIPTIONS);
+		
+		if (subList != null)
+			items = subList;
+	}
+
+	/**
+	 * Subscriptions to the specified node.
+	 * 
+	 * @param nodeId The node subscribed to
+	 * @param subList The list of subscriptions
+	 */
+	public SubscriptionsExtension(String nodeId, List<Subscription> subList)
+	{
+		super(PubSubElementType.SUBSCRIPTIONS, nodeId);
+
+		if (subList != null)
+			items = subList;
+	}
+
+	/**
+	 * Gets the list of subscriptions.
+	 * 
+	 * @return List of subscriptions
+	 */
+	public List<Subscription> getSubscriptions()
+	{
+		return items;
+	}
+
+	@Override
+	public String toXML()
+	{
+		if ((items == null) || (items.size() == 0))
+		{
+			return super.toXML();
+		}
+		else
+		{
+			StringBuilder builder = new StringBuilder("<");
+			builder.append(getElementName());
+			
+			if (getNode() != null)
+			{
+				builder.append(" node='");
+				builder.append(getNode());
+				builder.append("'");
+			}
+			builder.append(">");
+			
+			for (Subscription item : items)
+			{
+				builder.append(item.toXML());
+			}
+			
+			builder.append("</");
+			builder.append(getElementName());
+			builder.append(">");
+			return builder.toString();
+		}
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java
index ac14c60..dc815ca 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java
@@ -1,73 +1,73 @@
-/**

- * 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.smackx.pubsub;

-

-import org.jivesoftware.smackx.pubsub.util.XmlUtils;

-

-

-/**

- * Represents an unsubscribe element.

- * 

- * @author Robin Collier

- */

-public class UnsubscribeExtension extends NodeExtension

-{

-	protected String jid;

-	protected String id;

-	

-	public UnsubscribeExtension(String subscriptionJid)

-	{

-		this(subscriptionJid, null, null);

-	}

-	

-	public UnsubscribeExtension(String subscriptionJid, String nodeId)

-	{

-		this(subscriptionJid, nodeId, null);

-	}

-	

-	public UnsubscribeExtension(String jid, String nodeId, String subscriptionId)

-	{

-		super(PubSubElementType.UNSUBSCRIBE, nodeId);

-		this.jid = jid;

-		id = subscriptionId;

-	}

-	

-	public String getJid()

-	{

-		return jid;

-	}

-	

-	public String getId()

-	{

-		return id;

-	}

-	

-	@Override

-	public String toXML()

-	{

-		StringBuilder builder = new StringBuilder("<");

-		builder.append(getElementName());

-		XmlUtils.appendAttribute(builder, "jid", jid);

-		

-		if (getNode() != null)

-			XmlUtils.appendAttribute(builder, "node", getNode());

-		

-		if (id != null)

-			XmlUtils.appendAttribute(builder, "subid", id);

-		

-		builder.append("/>");

-		return builder.toString();

-	}

-

-}

+/**
+ * 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.smackx.pubsub;
+
+import org.jivesoftware.smackx.pubsub.util.XmlUtils;
+
+
+/**
+ * Represents an unsubscribe element.
+ * 
+ * @author Robin Collier
+ */
+public class UnsubscribeExtension extends NodeExtension
+{
+	protected String jid;
+	protected String id;
+	
+	public UnsubscribeExtension(String subscriptionJid)
+	{
+		this(subscriptionJid, null, null);
+	}
+	
+	public UnsubscribeExtension(String subscriptionJid, String nodeId)
+	{
+		this(subscriptionJid, nodeId, null);
+	}
+	
+	public UnsubscribeExtension(String jid, String nodeId, String subscriptionId)
+	{
+		super(PubSubElementType.UNSUBSCRIBE, nodeId);
+		this.jid = jid;
+		id = subscriptionId;
+	}
+	
+	public String getJid()
+	{
+		return jid;
+	}
+	
+	public String getId()
+	{
+		return id;
+	}
+	
+	@Override
+	public String toXML()
+	{
+		StringBuilder builder = new StringBuilder("<");
+		builder.append(getElementName());
+		XmlUtils.appendAttribute(builder, "jid", jid);
+		
+		if (getNode() != null)
+			XmlUtils.appendAttribute(builder, "node", getNode());
+		
+		if (id != null)
+			XmlUtils.appendAttribute(builder, "subid", id);
+		
+		builder.append("/>");
+		return builder.toString();
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java
index d228e8f..9b9c310 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java
@@ -1,41 +1,41 @@
-/**

- * 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.smackx.pubsub.listener;

-

-import org.jivesoftware.smackx.pubsub.ItemDeleteEvent;

-import org.jivesoftware.smackx.pubsub.LeafNode;

-

-/**

- * Defines the listener for item deletion events from a node.

- * 

- * @see LeafNode#addItemDeleteListener(ItemDeleteListener)

- * 

- * @author Robin Collier

- */

-public interface ItemDeleteListener

-{

-	/**

-	 * Called when items are deleted from a node the listener is 

-	 * registered with.

-	 * 

-	 * @param items The event with item deletion details

-	 */

-	void handleDeletedItems(ItemDeleteEvent items);

-	

-	/**

-	 * Called when <b>all</b> items are deleted from a node the listener is 

-	 * registered with. 

-	 */

-	void handlePurge();

-}

+/**
+ * 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.smackx.pubsub.listener;
+
+import org.jivesoftware.smackx.pubsub.ItemDeleteEvent;
+import org.jivesoftware.smackx.pubsub.LeafNode;
+
+/**
+ * Defines the listener for item deletion events from a node.
+ * 
+ * @see LeafNode#addItemDeleteListener(ItemDeleteListener)
+ * 
+ * @author Robin Collier
+ */
+public interface ItemDeleteListener
+{
+	/**
+	 * Called when items are deleted from a node the listener is 
+	 * registered with.
+	 * 
+	 * @param items The event with item deletion details
+	 */
+	void handleDeletedItems(ItemDeleteEvent items);
+	
+	/**
+	 * Called when <b>all</b> items are deleted from a node the listener is 
+	 * registered with. 
+	 */
+	void handlePurge();
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java
index 714b2c0..0556eea 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java
@@ -1,36 +1,36 @@
-/**

- * 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.smackx.pubsub.listener;

-

-import org.jivesoftware.smackx.pubsub.Item;

-import org.jivesoftware.smackx.pubsub.ItemPublishEvent;

-import org.jivesoftware.smackx.pubsub.LeafNode;

-

-/**

- * Defines the listener for items being published to a node.

- * 

- * @see LeafNode#addItemEventListener(ItemEventListener)

- *

- * @author Robin Collier

- */

-public interface ItemEventListener <T extends Item> 

-{

-	/**

-	 * Called whenever an item is published to the node the listener

-	 * is registered with.

-	 * 

-	 * @param items The publishing details.

-	 */

-	void handlePublishedItems(ItemPublishEvent<T> items);

-}

+/**
+ * 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.smackx.pubsub.listener;
+
+import org.jivesoftware.smackx.pubsub.Item;
+import org.jivesoftware.smackx.pubsub.ItemPublishEvent;
+import org.jivesoftware.smackx.pubsub.LeafNode;
+
+/**
+ * Defines the listener for items being published to a node.
+ * 
+ * @see LeafNode#addItemEventListener(ItemEventListener)
+ *
+ * @author Robin Collier
+ */
+public interface ItemEventListener <T extends Item> 
+{
+	/**
+	 * Called whenever an item is published to the node the listener
+	 * is registered with.
+	 * 
+	 * @param items The publishing details.
+	 */
+	void handlePublishedItems(ItemPublishEvent<T> items);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java
index 39db5a5..096ed68 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java
@@ -1,35 +1,35 @@
-/**

- * 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.smackx.pubsub.listener;

-

-import org.jivesoftware.smackx.pubsub.ConfigurationEvent;

-import org.jivesoftware.smackx.pubsub.LeafNode;

-

-/**

- * Defines the listener for a node being configured.

- * 

- * @see LeafNode#addConfigurationListener(NodeConfigListener)

- *

- * @author Robin Collier

- */

-public interface NodeConfigListener

-{

-	/**

-	 * Called whenever the node the listener

-	 * is registered with is configured.

-	 * 

-	 * @param config The configuration details.

-	 */

-	void handleNodeConfiguration(ConfigurationEvent config);

-}

+/**
+ * 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.smackx.pubsub.listener;
+
+import org.jivesoftware.smackx.pubsub.ConfigurationEvent;
+import org.jivesoftware.smackx.pubsub.LeafNode;
+
+/**
+ * Defines the listener for a node being configured.
+ * 
+ * @see LeafNode#addConfigurationListener(NodeConfigListener)
+ *
+ * @author Robin Collier
+ */
+public interface NodeConfigListener
+{
+	/**
+	 * Called whenever the node the listener
+	 * is registered with is configured.
+	 * 
+	 * @param config The configuration details.
+	 */
+	void handleNodeConfiguration(ConfigurationEvent config);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSub.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSub.java
index 5aa4865..fb7f643 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSub.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSub.java
@@ -1,106 +1,106 @@
-/**

- * 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.smackx.pubsub.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.pubsub.PubSubElementType;

-

-/**

- * The standard PubSub extension of an {@link IQ} packet.  This is the topmost

- * element of all pubsub requests and replies as defined in the <a href="http://xmpp.org/extensions/xep-0060">Publish-Subscribe</a> 

- * specification.

- * 

- * @author Robin Collier

- */

-public class PubSub extends IQ

-{

-	private PubSubNamespace ns = PubSubNamespace.BASIC;

-	

-	/**

-    * 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 "pubsub";

-    }

-

-    /** 

-     * Returns the XML namespace of the extension sub-packet root element.

-     * According the specification the namespace is 

-     * http://jabber.org/protocol/pubsub with a specific fragment depending

-     * on the request.  The namespace is defined at <a href="http://xmpp.org/registrar/namespaces.html">XMPP Registrar</a> at

-     * 

-     * The default value has no fragment.

-     * 

-     * @return the XML namespace of the packet extension.

-     */

-    public String getNamespace() 

-    {

-        return ns.getXmlns();

-    }

-

-    /**

-     * Set the namespace for the packet if it something other than the default

-     * case of {@link PubSubNamespace#BASIC}.  The {@link #getNamespace()} method will return 

-     * the result of calling {@link PubSubNamespace#getXmlns()} on the specified enum.

-     * 

-     * @param ns - The new value for the namespace.

-     */

-	public void setPubSubNamespace(PubSubNamespace ns)

-	{

-		this.ns = ns;

-	}

-

-	public PacketExtension getExtension(PubSubElementType elem)

-	{

-		return getExtension(elem.getElementName(), elem.getNamespace().getXmlns());

-	}

-

-	/**

-	 * Returns the current value of the namespace.  The {@link #getNamespace()} method will return 

-     * the result of calling {@link PubSubNamespace#getXmlns()} this value.

-	 * 

-	 * @return The current value of the namespace.

-	 */

-	public PubSubNamespace getPubSubNamespace()

-	{

-		return ns;

-	}

-    /**

-     * Returns the XML representation of a pubsub element according the specification.

-     * 

-     * The XML representation will be inside of an iq packet like

-     * in the following example:

-     * <pre>

-     * &lt;iq type='set' id="MlIpV-4" to="pubsub.gato.home" from="gato3@gato.home/Smack"&gt;

-     *     &lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt;

-     *                      :

-     *         Specific request extension

-     *                      :

-     *     &lt;/pubsub&gt;

-     * &lt;/iq&gt;

-     * </pre>

-     * 

-     */

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">");

-        buf.append(getExtensionsXML());

-        buf.append("</").append(getElementName()).append(">");

-        return buf.toString();

-    }

-

-}

+/**
+ * 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.smackx.pubsub.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.pubsub.PubSubElementType;
+
+/**
+ * The standard PubSub extension of an {@link IQ} packet.  This is the topmost
+ * element of all pubsub requests and replies as defined in the <a href="http://xmpp.org/extensions/xep-0060">Publish-Subscribe</a> 
+ * specification.
+ * 
+ * @author Robin Collier
+ */
+public class PubSub extends IQ
+{
+	private PubSubNamespace ns = PubSubNamespace.BASIC;
+	
+	/**
+    * 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 "pubsub";
+    }
+
+    /** 
+     * Returns the XML namespace of the extension sub-packet root element.
+     * According the specification the namespace is 
+     * http://jabber.org/protocol/pubsub with a specific fragment depending
+     * on the request.  The namespace is defined at <a href="http://xmpp.org/registrar/namespaces.html">XMPP Registrar</a> at
+     * 
+     * The default value has no fragment.
+     * 
+     * @return the XML namespace of the packet extension.
+     */
+    public String getNamespace() 
+    {
+        return ns.getXmlns();
+    }
+
+    /**
+     * Set the namespace for the packet if it something other than the default
+     * case of {@link PubSubNamespace#BASIC}.  The {@link #getNamespace()} method will return 
+     * the result of calling {@link PubSubNamespace#getXmlns()} on the specified enum.
+     * 
+     * @param ns - The new value for the namespace.
+     */
+	public void setPubSubNamespace(PubSubNamespace ns)
+	{
+		this.ns = ns;
+	}
+
+	public PacketExtension getExtension(PubSubElementType elem)
+	{
+		return getExtension(elem.getElementName(), elem.getNamespace().getXmlns());
+	}
+
+	/**
+	 * Returns the current value of the namespace.  The {@link #getNamespace()} method will return 
+     * the result of calling {@link PubSubNamespace#getXmlns()} this value.
+	 * 
+	 * @return The current value of the namespace.
+	 */
+	public PubSubNamespace getPubSubNamespace()
+	{
+		return ns;
+	}
+    /**
+     * Returns the XML representation of a pubsub element according the specification.
+     * 
+     * The XML representation will be inside of an iq packet like
+     * in the following example:
+     * <pre>
+     * &lt;iq type='set' id="MlIpV-4" to="pubsub.gato.home" from="gato3@gato.home/Smack"&gt;
+     *     &lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt;
+     *                      :
+     *         Specific request extension
+     *                      :
+     *     &lt;/pubsub&gt;
+     * &lt;/iq&gt;
+     * </pre>
+     * 
+     */
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">");
+        buf.append(getExtensionsXML());
+        buf.append("</").append(getElementName()).append(">");
+        return buf.toString();
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java
index eecf959..4fa8ad2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java
@@ -1,63 +1,63 @@
-/**

- * 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.smackx.pubsub.packet;

-

-/**

- * Defines all the valid namespaces that are used with the {@link PubSub} packet

- * as defined by the specification.

- * 

- * @author Robin Collier

- */

-public enum PubSubNamespace

-{

-	BASIC(null),

-	ERROR("errors"),

-	EVENT("event"),

-	OWNER("owner");

-	

-	private String fragment;

-	

-	private PubSubNamespace(String fragment)

-	{

-		this.fragment = fragment;

-	}

-	

-	public String getXmlns()

-	{

-		String ns = "http://jabber.org/protocol/pubsub";

-		

-		if (fragment != null)

-			ns += '#' + fragment;

-		

-		return ns;

-	}

-	

-	public String getFragment()

-	{

-		return fragment;

-	}

-

-	public static PubSubNamespace valueOfFromXmlns(String ns)

-	{

-		int index = ns.lastIndexOf('#');

-		

-		if (index != -1)

-		{

-			String suffix = ns.substring(ns.lastIndexOf('#')+1);

-			return valueOf(suffix.toUpperCase());

-		}

-		else

-			return BASIC;

-	}

-}

+/**
+ * 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.smackx.pubsub.packet;
+
+/**
+ * Defines all the valid namespaces that are used with the {@link PubSub} packet
+ * as defined by the specification.
+ * 
+ * @author Robin Collier
+ */
+public enum PubSubNamespace
+{
+	BASIC(null),
+	ERROR("errors"),
+	EVENT("event"),
+	OWNER("owner");
+	
+	private String fragment;
+	
+	private PubSubNamespace(String fragment)
+	{
+		this.fragment = fragment;
+	}
+	
+	public String getXmlns()
+	{
+		String ns = "http://jabber.org/protocol/pubsub";
+		
+		if (fragment != null)
+			ns += '#' + fragment;
+		
+		return ns;
+	}
+	
+	public String getFragment()
+	{
+		return fragment;
+	}
+
+	public static PubSubNamespace valueOfFromXmlns(String ns)
+	{
+		int index = ns.lastIndexOf('#');
+		
+		if (index != -1)
+		{
+			String suffix = ns.substring(ns.lastIndexOf('#')+1);
+			return valueOf(suffix.toUpperCase());
+		}
+		else
+			return BASIC;
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java
index 080129b..ff730c4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java
@@ -1,63 +1,63 @@
-/**

- * 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.smackx.pubsub.packet;

-

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.Packet;

-

-/**

- * Utility class for doing synchronous calls to the server.  Provides several

- * methods for sending a packet to the server and waiting for the reply.

- * 

- * @author Robin Collier

- */

-final public class SyncPacketSend

-{

-	private SyncPacketSend()

-	{	}

-	

-	static public Packet getReply(Connection connection, Packet packet, long timeout)

-		throws XMPPException

-	{

-        PacketFilter responseFilter = new PacketIDFilter(packet.getPacketID());

-        PacketCollector response = connection.createPacketCollector(responseFilter);

-        

-        connection.sendPacket(packet);

-

-        // Wait up to a certain number of seconds for a reply.

-        Packet result = response.nextResult(timeout);

-

-        // Stop queuing results

-        response.cancel();

-

-        if (result == null) {

-            throw new XMPPException("No response from server.");

-        }

-        else if (result.getError() != null) {

-            throw new XMPPException(result.getError());

-        }

-        return result;

-	}

-

-	static public Packet getReply(Connection connection, Packet packet)

-		throws XMPPException

-	{

-		return getReply(connection, packet, SmackConfiguration.getPacketReplyTimeout());

-	}

-}

+/**
+ * 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.smackx.pubsub.packet;
+
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.Packet;
+
+/**
+ * Utility class for doing synchronous calls to the server.  Provides several
+ * methods for sending a packet to the server and waiting for the reply.
+ * 
+ * @author Robin Collier
+ */
+final public class SyncPacketSend
+{
+	private SyncPacketSend()
+	{	}
+	
+	static public Packet getReply(Connection connection, Packet packet, long timeout)
+		throws XMPPException
+	{
+        PacketFilter responseFilter = new PacketIDFilter(packet.getPacketID());
+        PacketCollector response = connection.createPacketCollector(responseFilter);
+        
+        connection.sendPacket(packet);
+
+        // Wait up to a certain number of seconds for a reply.
+        Packet result = response.nextResult(timeout);
+
+        // Stop queuing results
+        response.cancel();
+
+        if (result == null) {
+            throw new XMPPException("No response from server.");
+        }
+        else if (result.getError() != null) {
+            throw new XMPPException(result.getError());
+        }
+        return result;
+	}
+
+	static public Packet getReply(Connection connection, Packet packet)
+		throws XMPPException
+	{
+		return getReply(connection, packet, SmackConfiguration.getPacketReplyTimeout());
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java
index 4e27c50..771c5e9 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java
@@ -1,37 +1,37 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.Affiliation;

-

-/**

- * Parses the affiliation element out of the reply stanza from the server

- * as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.

- * 

- * @author Robin Collier

- */

-public class AffiliationProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-		return new Affiliation(attributeMap.get("node"), Affiliation.Type.valueOf(attributeMap.get("affiliation")));

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.Affiliation;
+
+/**
+ * Parses the affiliation element out of the reply stanza from the server
+ * as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class AffiliationProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+		return new Affiliation(attributeMap.get("node"), Affiliation.Type.valueOf(attributeMap.get("affiliation")));
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java
index 9bfeb81..0ccd596 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.Affiliation;

-import org.jivesoftware.smackx.pubsub.AffiliationsExtension;

-

-/**

- * Parses the affiliations element out of the reply stanza from the server

- * as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.

- * 

- * @author Robin Collier

- */public class AffiliationsProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-        return new AffiliationsExtension((List<Affiliation>)content);

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.Affiliation;
+import org.jivesoftware.smackx.pubsub.AffiliationsExtension;
+
+/**
+ * Parses the affiliations element out of the reply stanza from the server
+ * as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">affiliation schema</a>.
+ * 
+ * @author Robin Collier
+ */public class AffiliationsProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+        return new AffiliationsExtension((List<Affiliation>)content);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java
index 30e3017..5c1d88d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java
@@ -1,42 +1,42 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.ConfigurationEvent;

-import org.jivesoftware.smackx.pubsub.ConfigureForm;

-

-/**

- * Parses the node configuration element out of the message event stanza from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">configuration schema</a>.

- * 

- * @author Robin Collier

- */

-public class ConfigEventProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)

-	{

-		if (content.size() == 0)

-			return new ConfigurationEvent(attMap.get("node"));

-		else

-			return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm)content.iterator().next()));

-	}

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.ConfigurationEvent;
+import org.jivesoftware.smackx.pubsub.ConfigureForm;
+
+/**
+ * Parses the node configuration element out of the message event stanza from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">configuration schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class ConfigEventProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)
+	{
+		if (content.size() == 0)
+			return new ConfigurationEvent(attMap.get("node"));
+		else
+			return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm)content.iterator().next()));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/EventProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/EventProvider.java
index ef5671e..b216335 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/EventProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/EventProvider.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.EventElement;

-import org.jivesoftware.smackx.pubsub.EventElementType;

-import org.jivesoftware.smackx.pubsub.NodeExtension;

-

-/**

- * Parses the event element out of the message stanza from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">event schema</a>.

- * 

- * @author Robin Collier

- */

-public class EventProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)

-	{

-	   	return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0));

-	}

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.EventElement;
+import org.jivesoftware.smackx.pubsub.EventElementType;
+import org.jivesoftware.smackx.pubsub.NodeExtension;
+
+/**
+ * Parses the event element out of the message stanza from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">event schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class EventProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends PacketExtension> content)
+	{
+	   	return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java
index da75b24..5a11529 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java
@@ -1,39 +1,39 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.FormNode;

-import org.jivesoftware.smackx.pubsub.FormNodeType;

-

-/**

- * Parses one of several elements used in pubsub that contain a form of some kind as a child element.  The

- * elements and namespaces supported is defined in {@link FormNodeType}.

- * 

- * @author Robin Collier

- */

-public class FormNodeProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-        return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm)content.iterator().next()));

-	}

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.FormNode;
+import org.jivesoftware.smackx.pubsub.FormNodeType;
+
+/**
+ * Parses one of several elements used in pubsub that contain a form of some kind as a child element.  The
+ * elements and namespaces supported is defined in {@link FormNodeType}.
+ * 
+ * @author Robin Collier
+ */
+public class FormNodeProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+        return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm)content.iterator().next()));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java
index 7b06af3..b1cbd2b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java
@@ -1,109 +1,109 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smack.provider.ProviderManager;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.jivesoftware.smackx.pubsub.Item;

-import org.jivesoftware.smackx.pubsub.PayloadItem;

-import org.jivesoftware.smackx.pubsub.SimplePayload;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses an <b>item</b> element as is defined in both the {@link PubSubNamespace#BASIC} and

- * {@link PubSubNamespace#EVENT} namespaces. To parse the item contents, it will use whatever

- * {@link PacketExtensionProvider} is registered in <b>smack.providers</b> for its element name and namespace. If no

- * provider is registered, it will return a {@link SimplePayload}.

- * 

- * @author Robin Collier

- */

-public class ItemProvider implements PacketExtensionProvider 

-{

-    public PacketExtension parseExtension(XmlPullParser parser) throws Exception 

-    {

-        String id = parser.getAttributeValue(null, "id");

-        String node = parser.getAttributeValue(null, "node");

-        String elem = parser.getName();

-

-        int tag = parser.next();

-

-        if (tag == XmlPullParser.END_TAG) 

-        {

-            return new Item(id, node);

-        }

-        else 

-        {

-            String payloadElemName = parser.getName();

-            String payloadNS = parser.getNamespace();

-

-            if (ProviderManager.getInstance().getExtensionProvider(payloadElemName, payloadNS) == null) 

-            {

-                boolean done = false;

-                boolean isEmptyElement = false;

-                StringBuilder payloadText = new StringBuilder();

-

-                while (!done) 

-                {

-                    if (tag == XmlPullParser.END_TAG && parser.getName().equals(elem)) 

-                    {

-                        done = true;

-                    }

-                    else if (parser.getEventType() == XmlPullParser.START_TAG) 

-                    {

-                        payloadText.append("<").append(parser.getName());

-

-                        if (parser.getName().equals(payloadElemName) && (payloadNS.length() > 0))

-                            payloadText.append(" xmlns=\"").append(payloadNS).append("\"");

-                        int n = parser.getAttributeCount();

-

-                        for (int i = 0; i < n; i++) 

-                            payloadText.append(" ").append(parser.getAttributeName(i)).append("=\"")

-                                    .append(parser.getAttributeValue(i)).append("\"");

-

-                        if (parser.isEmptyElementTag()) 

-                        {

-                            payloadText.append("/>");

-                            isEmptyElement = true;

-                        }

-                        else 

-                        {

-                            payloadText.append(">");

-                        }

-                    }

-                    else if (parser.getEventType() == XmlPullParser.END_TAG) 

-                    {

-                        if (isEmptyElement) 

-                            isEmptyElement = false;

-                        else 

-                            payloadText.append("</").append(parser.getName()).append(">");

-                    }

-                    else if (parser.getEventType() == XmlPullParser.TEXT) 

-                    {

-                        payloadText.append(parser.getText());

-                    }

-                    tag = parser.next();

-                }

-                return new PayloadItem<SimplePayload>(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText.toString()));

-            }

-            else {

-                return new PayloadItem<PacketExtension>(id, node, PacketParserUtils.parsePacketExtension(

-                        payloadElemName, payloadNS, parser));

-            }

-        }

-    }

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.provider.ProviderManager;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.pubsub.Item;
+import org.jivesoftware.smackx.pubsub.PayloadItem;
+import org.jivesoftware.smackx.pubsub.SimplePayload;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses an <b>item</b> element as is defined in both the {@link PubSubNamespace#BASIC} and
+ * {@link PubSubNamespace#EVENT} namespaces. To parse the item contents, it will use whatever
+ * {@link PacketExtensionProvider} is registered in <b>smack.providers</b> for its element name and namespace. If no
+ * provider is registered, it will return a {@link SimplePayload}.
+ * 
+ * @author Robin Collier
+ */
+public class ItemProvider implements PacketExtensionProvider 
+{
+    public PacketExtension parseExtension(XmlPullParser parser) throws Exception 
+    {
+        String id = parser.getAttributeValue(null, "id");
+        String node = parser.getAttributeValue(null, "node");
+        String elem = parser.getName();
+
+        int tag = parser.next();
+
+        if (tag == XmlPullParser.END_TAG) 
+        {
+            return new Item(id, node);
+        }
+        else 
+        {
+            String payloadElemName = parser.getName();
+            String payloadNS = parser.getNamespace();
+
+            if (ProviderManager.getInstance().getExtensionProvider(payloadElemName, payloadNS) == null) 
+            {
+                boolean done = false;
+                boolean isEmptyElement = false;
+                StringBuilder payloadText = new StringBuilder();
+
+                while (!done) 
+                {
+                    if (tag == XmlPullParser.END_TAG && parser.getName().equals(elem)) 
+                    {
+                        done = true;
+                    }
+                    else if (parser.getEventType() == XmlPullParser.START_TAG) 
+                    {
+                        payloadText.append("<").append(parser.getName());
+
+                        if (parser.getName().equals(payloadElemName) && (payloadNS.length() > 0))
+                            payloadText.append(" xmlns=\"").append(payloadNS).append("\"");
+                        int n = parser.getAttributeCount();
+
+                        for (int i = 0; i < n; i++) 
+                            payloadText.append(" ").append(parser.getAttributeName(i)).append("=\"")
+                                    .append(parser.getAttributeValue(i)).append("\"");
+
+                        if (parser.isEmptyElementTag()) 
+                        {
+                            payloadText.append("/>");
+                            isEmptyElement = true;
+                        }
+                        else 
+                        {
+                            payloadText.append(">");
+                        }
+                    }
+                    else if (parser.getEventType() == XmlPullParser.END_TAG) 
+                    {
+                        if (isEmptyElement) 
+                            isEmptyElement = false;
+                        else 
+                            payloadText.append("</").append(parser.getName()).append(">");
+                    }
+                    else if (parser.getEventType() == XmlPullParser.TEXT) 
+                    {
+                        payloadText.append(parser.getText());
+                    }
+                    tag = parser.next();
+                }
+                return new PayloadItem<SimplePayload>(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText.toString()));
+            }
+            else {
+                return new PayloadItem<PacketExtension>(id, node, PacketParserUtils.parsePacketExtension(
+                        payloadElemName, payloadNS, parser));
+            }
+        }
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java
index 01cb9d4..5657dd3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.ItemsExtension;

-

-/**

- * Parses the <b>items</b> element out of the message event stanza from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">items schema</a>.

- * 

- * @author Robin Collier

- */

-public class ItemsProvider extends EmbeddedExtensionProvider

-{

-

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-        return new ItemsExtension(ItemsExtension.ItemsElementType.items, attributeMap.get("node"), content);

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.ItemsExtension;
+
+/**
+ * Parses the <b>items</b> element out of the message event stanza from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">items schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class ItemsProvider extends EmbeddedExtensionProvider
+{
+
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+        return new ItemsExtension(ItemsExtension.ItemsElementType.items, attributeMap.get("node"), content);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java
index 742f219..3fd34f5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java
@@ -1,62 +1,62 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.jivesoftware.smackx.pubsub.packet.PubSub;

-import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses the root pubsub packet extensions of the {@link IQ} packet and returns

- * a {@link PubSub} instance.

- * 

- * @author Robin Collier

- */

-public class PubSubProvider implements IQProvider

-{

-	public IQ parseIQ(XmlPullParser parser) throws Exception

-	{

-        PubSub pubsub = new PubSub();

-        String namespace = parser.getNamespace();

-        pubsub.setPubSubNamespace(PubSubNamespace.valueOfFromXmlns(namespace));

-        boolean done = false;

-

-        while (!done) 

-        {

-            int eventType = parser.next();

-            

-            if (eventType == XmlPullParser.START_TAG) 

-            {

-            	PacketExtension ext = PacketParserUtils.parsePacketExtension(parser.getName(), namespace, parser);

-            	

-            	if (ext != null)

-            	{

-            		pubsub.addExtension(ext);

-            	}

-            }

-            else if (eventType == XmlPullParser.END_TAG) 

-            {

-                if (parser.getName().equals("pubsub")) 

-                {

-                    done = true;

-                }

-            }

-        }

-        return pubsub;

-	}

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.pubsub.packet.PubSub;
+import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses the root pubsub packet extensions of the {@link IQ} packet and returns
+ * a {@link PubSub} instance.
+ * 
+ * @author Robin Collier
+ */
+public class PubSubProvider implements IQProvider
+{
+	public IQ parseIQ(XmlPullParser parser) throws Exception
+	{
+        PubSub pubsub = new PubSub();
+        String namespace = parser.getNamespace();
+        pubsub.setPubSubNamespace(PubSubNamespace.valueOfFromXmlns(namespace));
+        boolean done = false;
+
+        while (!done) 
+        {
+            int eventType = parser.next();
+            
+            if (eventType == XmlPullParser.START_TAG) 
+            {
+            	PacketExtension ext = PacketParserUtils.parsePacketExtension(parser.getName(), namespace, parser);
+            	
+            	if (ext != null)
+            	{
+            		pubsub.addExtension(ext);
+            	}
+            }
+            else if (eventType == XmlPullParser.END_TAG) 
+            {
+                if (parser.getName().equals("pubsub")) 
+                {
+                    done = true;
+                }
+            }
+        }
+        return pubsub;
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java
index 8fa3337..f182de8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.RetractItem;

-

-/**

- * Parses the <b>retract</b> element out of the message event stanza from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">retract schema</a>.

- * This element is a child of the <b>items</b> element.

- * 

- * @author Robin Collier

- */

-public class RetractEventProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-		return new RetractItem(attributeMap.get("id"));

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.RetractItem;
+
+/**
+ * Parses the <b>retract</b> element out of the message event stanza from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-event">retract schema</a>.
+ * This element is a child of the <b>items</b> element.
+ * 
+ * @author Robin Collier
+ */
+public class RetractEventProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+		return new RetractItem(attributeMap.get("id"));
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java
index d2b7d30..b57f04a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java
@@ -1,37 +1,37 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.NodeExtension;

-import org.jivesoftware.smackx.pubsub.PubSubElementType;

-

-/**

- * Parses simple elements that only contain a <b>node</b> attribute.  This is common amongst many of the 

- * elements defined in the pubsub specification.  For this common case a {@link NodeExtension} is returned. 

- * 

- * @author Robin Collier

- */

-public class SimpleNodeProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-        return new NodeExtension(PubSubElementType.valueOfFromElemName(currentElement, currentNamespace), attributeMap.get("node"));

-	}

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.NodeExtension;
+import org.jivesoftware.smackx.pubsub.PubSubElementType;
+
+/**
+ * Parses simple elements that only contain a <b>node</b> attribute.  This is common amongst many of the 
+ * elements defined in the pubsub specification.  For this common case a {@link NodeExtension} is returned. 
+ * 
+ * @author Robin Collier
+ */
+public class SimpleNodeProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+        return new NodeExtension(PubSubElementType.valueOfFromElemName(currentElement, currentNamespace), attributeMap.get("node"));
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java
index eccbe08..a35e6a1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java
@@ -1,52 +1,52 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.jivesoftware.smackx.pubsub.Subscription;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Parses the <b>subscription</b> element out of the pubsub IQ message from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscription schema</a>.

- * 

- * @author Robin Collier

- */

-public class SubscriptionProvider implements PacketExtensionProvider

-{

-	public PacketExtension parseExtension(XmlPullParser parser) throws Exception

-	{

-		String jid = parser.getAttributeValue(null, "jid");

-		String nodeId = parser.getAttributeValue(null, "node");

-		String subId = parser.getAttributeValue(null, "subid");

-		String state = parser.getAttributeValue(null, "subscription");

-		boolean isRequired = false;

-

-		int tag = parser.next();

-		

-		if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options"))

-		{

-			tag = parser.next();

-			

-			if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))

-				isRequired = true;

-			

-			while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");

-		}

-		while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();

-		return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state)), isRequired);

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smackx.pubsub.Subscription;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Parses the <b>subscription</b> element out of the pubsub IQ message from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscription schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class SubscriptionProvider implements PacketExtensionProvider
+{
+	public PacketExtension parseExtension(XmlPullParser parser) throws Exception
+	{
+		String jid = parser.getAttributeValue(null, "jid");
+		String nodeId = parser.getAttributeValue(null, "node");
+		String subId = parser.getAttributeValue(null, "subid");
+		String state = parser.getAttributeValue(null, "subscription");
+		boolean isRequired = false;
+
+		int tag = parser.next();
+		
+		if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options"))
+		{
+			tag = parser.next();
+			
+			if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
+				isRequired = true;
+			
+			while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
+		}
+		while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
+		return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state)), isRequired);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java
index 94dc61d..3b2aabb 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java
@@ -1,38 +1,38 @@
-/**

- * 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.smackx.pubsub.provider;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;

-import org.jivesoftware.smackx.pubsub.Subscription;

-import org.jivesoftware.smackx.pubsub.SubscriptionsExtension;

-

-/**

- * Parses the <b>subscriptions</b> element out of the pubsub IQ message from 

- * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscriptions schema</a>.

- * 

- * @author Robin Collier

- */

-public class SubscriptionsProvider extends EmbeddedExtensionProvider

-{

-	@Override

-	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)

-	{

-		return new SubscriptionsExtension(attributeMap.get("node"), (List<Subscription>)content);

-	}

-

-}

+/**
+ * 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.smackx.pubsub.provider;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.provider.EmbeddedExtensionProvider;
+import org.jivesoftware.smackx.pubsub.Subscription;
+import org.jivesoftware.smackx.pubsub.SubscriptionsExtension;
+
+/**
+ * Parses the <b>subscriptions</b> element out of the pubsub IQ message from 
+ * the server as specified in the <a href="http://xmpp.org/extensions/xep-0060.html#schemas-pubsub">subscriptions schema</a>.
+ * 
+ * @author Robin Collier
+ */
+public class SubscriptionsProvider extends EmbeddedExtensionProvider
+{
+	@Override
+	protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends PacketExtension> content)
+	{
+		return new SubscriptionsExtension(attributeMap.get("node"), (List<Subscription>)content);
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/NodeUtils.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/NodeUtils.java
index 414601f..48cafa7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/NodeUtils.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/NodeUtils.java
@@ -1,43 +1,43 @@
-/**

- * 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.smackx.pubsub.util;

-

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.pubsub.ConfigureForm;

-import org.jivesoftware.smackx.pubsub.FormNode;

-import org.jivesoftware.smackx.pubsub.PubSubElementType;

-

-/**

- * Utility for extracting information from packets.

- * 

- * @author Robin Collier

- */

-public class NodeUtils

-{

-	/** 

-	 * Get a {@link ConfigureForm} from a packet.

-	 * 

-	 * @param packet

-	 * @param elem

-	 * @return The configuration form

-	 */

-	public static ConfigureForm getFormFromPacket(Packet packet, PubSubElementType elem)

-	{

-		FormNode config = (FormNode)packet.getExtension(elem.getElementName(), elem.getNamespace().getXmlns());

-		Form formReply = config.getForm();

-		return new ConfigureForm(formReply);

-

-	}

-}

+/**
+ * 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.smackx.pubsub.util;
+
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.pubsub.ConfigureForm;
+import org.jivesoftware.smackx.pubsub.FormNode;
+import org.jivesoftware.smackx.pubsub.PubSubElementType;
+
+/**
+ * Utility for extracting information from packets.
+ * 
+ * @author Robin Collier
+ */
+public class NodeUtils
+{
+	/** 
+	 * Get a {@link ConfigureForm} from a packet.
+	 * 
+	 * @param packet
+	 * @param elem
+	 * @return The configuration form
+	 */
+	public static ConfigureForm getFormFromPacket(Packet packet, PubSubElementType elem)
+	{
+		FormNode config = (FormNode)packet.getExtension(elem.getElementName(), elem.getNamespace().getXmlns());
+		Form formReply = config.getForm();
+		return new ConfigureForm(formReply);
+
+	}
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/XmlUtils.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/XmlUtils.java
index f594871..c42e373 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/XmlUtils.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/pubsub/util/XmlUtils.java
@@ -1,67 +1,67 @@
-/**

- * 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.smackx.pubsub.util;

-

-import java.io.StringReader;

-

-import javax.xml.transform.OutputKeys;

-import javax.xml.transform.Transformer;

-import javax.xml.transform.TransformerFactory;

-import javax.xml.transform.stream.StreamResult;

-import javax.xml.transform.stream.StreamSource;

-

-/**

- * Simple utility for pretty printing xml.

- * 

- * @author Robin Collier

- */

-public class XmlUtils

-{

-	/**

-	 * 

-	 * @param header Just a title for the stanza for readability.  Single word no spaces since

-	 * it is inserted as the root element in the output.

-	 * @param xml The string to pretty print

-	 */

-	static public void prettyPrint(String header, String xml)

-	{

-		try

-		{

-			Transformer transformer = TransformerFactory.newInstance().newTransformer();

-			transformer.setOutputProperty(OutputKeys.INDENT, "yes");

-			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");

-

-			if (header != null)

-			{

-				xml = "\n<" + header + ">" + xml + "</" + header + '>';

-			}

-			transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out));

-		}

-		catch (Exception e)

-		{

-			System.out.println("Something wrong with xml in \n---------------\n" + xml + "\n---------------");

-			e.printStackTrace();

-		}

-	}

-

-	static public void appendAttribute(StringBuilder builder, String att, String value)

-	{

-		builder.append(" ");

-		builder.append(att);

-		builder.append("='");

-		builder.append(value);

-		builder.append("'");

-	}

-

-}

+/**
+ * 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.smackx.pubsub.util;
+
+import java.io.StringReader;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * Simple utility for pretty printing xml.
+ * 
+ * @author Robin Collier
+ */
+public class XmlUtils
+{
+	/**
+	 * 
+	 * @param header Just a title for the stanza for readability.  Single word no spaces since
+	 * it is inserted as the root element in the output.
+	 * @param xml The string to pretty print
+	 */
+	static public void prettyPrint(String header, String xml)
+	{
+		try
+		{
+			Transformer transformer = TransformerFactory.newInstance().newTransformer();
+			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
+
+			if (header != null)
+			{
+				xml = "\n<" + header + ">" + xml + "</" + header + '>';
+			}
+			transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out));
+		}
+		catch (Exception e)
+		{
+			System.out.println("Something wrong with xml in \n---------------\n" + xml + "\n---------------");
+			e.printStackTrace();
+		}
+	}
+
+	static public void appendAttribute(StringBuilder builder, String att, String value)
+	{
+		builder.append(" ");
+		builder.append(att);
+		builder.append("='");
+		builder.append(value);
+		builder.append("'");
+	}
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceipt.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
index 9020556..d58fab2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceipt.java
@@ -1,77 +1,77 @@
-/**

- * 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.smackx.receipts;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.EmbeddedExtensionProvider;

-

-/**

- * Represents a <b>message delivery receipt</b> entry as specified by

- * <a href="http://xmpp.org/extensions/xep-0184.html">Message Delivery Receipts</a>.

- *

- * @author Georg Lukas

- */

-public class DeliveryReceipt implements PacketExtension

-{

-    public static final String NAMESPACE = "urn:xmpp:receipts";

-    public static final String ELEMENT = "received";

-

-    private String id; /// original ID of the delivered message

-

-    public DeliveryReceipt(String id)

-    {

-        this.id = id;

-    }

-

-    public String getId()

-    {

-        return id;

-    }

-

-    @Override

-    public String getElementName()

-    {

-        return ELEMENT;

-    }

-

-    @Override

-    public String getNamespace()

-    {

-        return NAMESPACE;

-    }

-

-    @Override

-    public String toXML()

-    {

-        return "<received xmlns='" + NAMESPACE + "' id='" + id + "'/>";

-    }

-

-    /**

-     * This Provider parses and returns DeliveryReceipt packets.

-     */

-    public static class Provider extends EmbeddedExtensionProvider

-    {

-

-        @Override

-        protected PacketExtension createReturnExtension(String currentElement, String currentNamespace,

-                Map<String, String> attributeMap, List<? extends PacketExtension> content)

-        {

-            return new DeliveryReceipt(attributeMap.get("id"));

-        }

-

-    }

-}

+/**
+ * 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.smackx.receipts;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.EmbeddedExtensionProvider;
+
+/**
+ * Represents a <b>message delivery receipt</b> entry as specified by
+ * <a href="http://xmpp.org/extensions/xep-0184.html">Message Delivery Receipts</a>.
+ *
+ * @author Georg Lukas
+ */
+public class DeliveryReceipt implements PacketExtension
+{
+    public static final String NAMESPACE = "urn:xmpp:receipts";
+    public static final String ELEMENT = "received";
+
+    private String id; /// original ID of the delivered message
+
+    public DeliveryReceipt(String id)
+    {
+        this.id = id;
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    @Override
+    public String getElementName()
+    {
+        return ELEMENT;
+    }
+
+    @Override
+    public String getNamespace()
+    {
+        return NAMESPACE;
+    }
+
+    @Override
+    public String toXML()
+    {
+        return "<received xmlns='" + NAMESPACE + "' id='" + id + "'/>";
+    }
+
+    /**
+     * This Provider parses and returns DeliveryReceipt packets.
+     */
+    public static class Provider extends EmbeddedExtensionProvider
+    {
+
+        @Override
+        protected PacketExtension createReturnExtension(String currentElement, String currentNamespace,
+                Map<String, String> attributeMap, List<? extends PacketExtension> content)
+        {
+            return new DeliveryReceipt(attributeMap.get("id"));
+        }
+
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java
index 1b5ed3b..28bd0c8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java
@@ -1,54 +1,54 @@
-/*

- * 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.smackx.receipts;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Represents a <b>message delivery receipt request</b> entry as specified by

- * <a href="http://xmpp.org/extensions/xep-0184.html">Message Delivery Receipts</a>.

- *

- * @author Georg Lukas

- */

-public class DeliveryReceiptRequest implements PacketExtension

-{

-    public static final String ELEMENT = "request";

-

-    public String getElementName()

-    {

-        return ELEMENT;

-    }

-

-    public String getNamespace()

-    {

-        return DeliveryReceipt.NAMESPACE;

-    }

-

-    public String toXML()

-    {

-        return "<request xmlns='" + DeliveryReceipt.NAMESPACE + "'/>";

-    }

-

-    /**

-     * This Provider parses and returns DeliveryReceiptRequest packets.

-     */

-    public static class Provider implements PacketExtensionProvider {

-        @Override

-        public PacketExtension parseExtension(XmlPullParser parser) {

-            return new DeliveryReceiptRequest();

-        }

-    }

-}

+/*
+ * 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.smackx.receipts;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Represents a <b>message delivery receipt request</b> entry as specified by
+ * <a href="http://xmpp.org/extensions/xep-0184.html">Message Delivery Receipts</a>.
+ *
+ * @author Georg Lukas
+ */
+public class DeliveryReceiptRequest implements PacketExtension
+{
+    public static final String ELEMENT = "request";
+
+    public String getElementName()
+    {
+        return ELEMENT;
+    }
+
+    public String getNamespace()
+    {
+        return DeliveryReceipt.NAMESPACE;
+    }
+
+    public String toXML()
+    {
+        return "<request xmlns='" + DeliveryReceipt.NAMESPACE + "'/>";
+    }
+
+    /**
+     * This Provider parses and returns DeliveryReceiptRequest packets.
+     */
+    public static class Provider implements PacketExtensionProvider {
+        @Override
+        public PacketExtension parseExtension(XmlPullParser parser) {
+            return new DeliveryReceiptRequest();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java
index 3183113..4f613d3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java
@@ -1,26 +1,26 @@
-/**

- * Copyright 2013 Georg Lukas

- *

- * 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.smackx.receipts;

-

-/**

- * Interface for received receipt notifications.

- * 

- * Implement this and add a listener to get notified. 

- */

-public interface ReceiptReceivedListener {

-    void onReceiptReceived(String fromJid, String toJid, String receiptId);

+/**
+ * Copyright 2013 Georg Lukas
+ *
+ * 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.smackx.receipts;
+
+/**
+ * Interface for received receipt notifications.
+ * 
+ * Implement this and add a listener to get notified. 
+ */
+public interface ReceiptReceivedListener {
+    void onReceiptReceived(String fromJid, String toJid, String receiptId);
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/MetaData.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/MetaData.java
index 115a79c..707bcb5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/MetaData.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/MetaData.java
@@ -1,68 +1,68 @@
-/**

- * $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.smackx.workgroup;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-

-/**

- * MetaData packet extension.

- */

-public class MetaData implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "metadata";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    private Map<String, List<String>> metaData;

-

-    public MetaData(Map<String, List<String>> metaData) {

-        this.metaData = metaData;

-    }

-

-    /**

-     * @return the Map of metadata contained by this instance

-     */

-    public Map<String, List<String>> getMetaData() {

-        return metaData;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        return MetaDataUtils.serializeMetaData(this.getMetaData());

-    }

+/**
+ * $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.smackx.workgroup;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * MetaData packet extension.
+ */
+public class MetaData implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "metadata";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    private Map<String, List<String>> metaData;
+
+    public MetaData(Map<String, List<String>> metaData) {
+        this.metaData = metaData;
+    }
+
+    /**
+     * @return the Map of metadata contained by this instance
+     */
+    public Map<String, List<String>> getMetaData() {
+        return metaData;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        return MetaDataUtils.serializeMetaData(this.getMetaData());
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/QueueUser.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/QueueUser.java
index 89a1899..da83061 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/QueueUser.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/QueueUser.java
@@ -1,85 +1,85 @@
-/**

- * $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.smackx.workgroup;

-

-import java.util.Date;

-

-/**

- * An immutable class which wraps up customer-in-queue data return from the server; depending on

- * the type of information dispatched from the server, not all information will be available in

- * any given instance.

- *

- * @author loki der quaeler

- */

-public class QueueUser {

-

-    private String userID;

-

-    private int queuePosition;

-    private int estimatedTime;

-    private Date joinDate;

-

-    /**

-     * @param uid the user jid of the customer in the queue

-     * @param position the position customer sits in the queue

-     * @param time the estimate of how much longer the customer will be in the queue in seconds

-     * @param joinedAt the timestamp of when the customer entered the queue

-     */

-    public QueueUser (String uid, int position, int time, Date joinedAt) {

-        super();

-

-        this.userID = uid;

-        this.queuePosition = position;

-        this.estimatedTime = time;

-        this.joinDate = joinedAt;

-    }

-

-    /**

-     * @return the user jid of the customer in the queue

-     */

-    public String getUserID () {

-        return this.userID;

-    }

-

-    /**

-     * @return the position in the queue at which the customer sits, or -1 if the update which

-     *          this instance embodies is only a time update instead

-     */

-    public int getQueuePosition () {

-        return this.queuePosition;

-    }

-

-    /**

-     * @return the estimated time remaining of the customer in the queue in seconds, or -1 if

-     *          if the update which this instance embodies is only a position update instead

-     */

-    public int getEstimatedRemainingTime () {

-        return this.estimatedTime;

-    }

-

-    /**

-     * @return the timestamp of when this customer entered the queue, or null if the server did not

-     *          provide this information

-     */

-    public Date getQueueJoinTimestamp () {

-        return this.joinDate;

-    }

-

-}

+/**
+ * $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.smackx.workgroup;
+
+import java.util.Date;
+
+/**
+ * An immutable class which wraps up customer-in-queue data return from the server; depending on
+ * the type of information dispatched from the server, not all information will be available in
+ * any given instance.
+ *
+ * @author loki der quaeler
+ */
+public class QueueUser {
+
+    private String userID;
+
+    private int queuePosition;
+    private int estimatedTime;
+    private Date joinDate;
+
+    /**
+     * @param uid the user jid of the customer in the queue
+     * @param position the position customer sits in the queue
+     * @param time the estimate of how much longer the customer will be in the queue in seconds
+     * @param joinedAt the timestamp of when the customer entered the queue
+     */
+    public QueueUser (String uid, int position, int time, Date joinedAt) {
+        super();
+
+        this.userID = uid;
+        this.queuePosition = position;
+        this.estimatedTime = time;
+        this.joinDate = joinedAt;
+    }
+
+    /**
+     * @return the user jid of the customer in the queue
+     */
+    public String getUserID () {
+        return this.userID;
+    }
+
+    /**
+     * @return the position in the queue at which the customer sits, or -1 if the update which
+     *          this instance embodies is only a time update instead
+     */
+    public int getQueuePosition () {
+        return this.queuePosition;
+    }
+
+    /**
+     * @return the estimated time remaining of the customer in the queue in seconds, or -1 if
+     *          if the update which this instance embodies is only a position update instead
+     */
+    public int getEstimatedRemainingTime () {
+        return this.estimatedTime;
+    }
+
+    /**
+     * @return the timestamp of when this customer entered the queue, or null if the server did not
+     *          provide this information
+     */
+    public Date getQueueJoinTimestamp () {
+        return this.joinDate;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java
index ac3b5b6..10a84d5 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java
@@ -1,134 +1,134 @@
-/**

- * $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.smackx.workgroup;

-

-import java.util.List;

-import java.util.Map;

-

-/**

- * An immutable class wrapping up the basic information which comprises a group chat invitation.

- *

- * @author loki der quaeler

- */

-public class WorkgroupInvitation {

-

-    protected String uniqueID;

-

-    protected String sessionID;

-

-    protected String groupChatName;

-    protected String issuingWorkgroupName;

-    protected String messageBody;

-    protected String invitationSender;

-    protected Map<String, List<String>> metaData;

-

-    /**

-     * This calls the 5-argument constructor with a null MetaData argument value

-     *

-     * @param jid the jid string with which the issuing AgentSession or Workgroup instance

-     *                  was created

-     * @param group the jid of the room to which the person is invited

-     * @param workgroup the jid of the workgroup issuing the invitation

-     * @param sessID the session id associated with the pending chat

-     * @param msgBody the body of the message which contained the invitation

-     * @param from the user jid who issued the invitation, if known, null otherwise

-     */

-    public WorkgroupInvitation (String jid, String group, String workgroup,

-                       String sessID, String msgBody, String from) {

-        this(jid, group, workgroup, sessID, msgBody, from, null);

-    }

-

-    /**

-     * @param jid the jid string with which the issuing AgentSession or Workgroup instance

-     *                  was created

-     * @param group the jid of the room to which the person is invited

-     * @param workgroup the jid of the workgroup issuing the invitation

-     * @param sessID the session id associated with the pending chat

-     * @param msgBody the body of the message which contained the invitation

-     * @param from the user jid who issued the invitation, if known, null otherwise

-     * @param metaData the metadata sent with the invitation

-     */

-    public WorkgroupInvitation (String jid, String group, String workgroup, String sessID, String msgBody,

-                       String from, Map<String, List<String>> metaData) {

-        super();

-

-        this.uniqueID = jid;

-        this.sessionID = sessID;

-        this.groupChatName = group;

-        this.issuingWorkgroupName = workgroup;

-        this.messageBody = msgBody;

-        this.invitationSender = from;

-        this.metaData = metaData;

-    }

-

-    /**

-     * @return the jid string with which the issuing AgentSession or Workgroup instance

-     *  was created.

-     */

-    public String getUniqueID () {

-        return this.uniqueID;

-    }

-

-    /**

-     * @return the session id associated with the pending chat; working backwards temporally,

-     *              this session id should match the session id to the corresponding offer request

-     *              which resulted in this invitation.

-     */

-    public String getSessionID () {

-        return this.sessionID;

-    }

-

-    /**

-     * @return the jid of the room to which the person is invited.

-     */

-    public String getGroupChatName () {

-        return this.groupChatName;

-    }

-

-    /**

-     * @return the name of the workgroup from which the invitation was issued.

-     */

-    public String getWorkgroupName () {

-        return this.issuingWorkgroupName;

-    }

-

-    /**

-     * @return the contents of the body-block of the message that housed this invitation.

-     */

-    public String getMessageBody () {

-        return this.messageBody;

-    }

-

-    /**

-     * @return the user who issued the invitation, or null if it wasn't known.

-     */

-    public String getInvitationSender () {

-        return this.invitationSender;

-    }

-

-    /**

-     * @return the meta data associated with the invitation, or null if this instance was

-     *              constructed with none

-     */

-    public Map<String, List<String>> getMetaData () {

-        return this.metaData;

-    }

-

-}

+/**
+ * $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.smackx.workgroup;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An immutable class wrapping up the basic information which comprises a group chat invitation.
+ *
+ * @author loki der quaeler
+ */
+public class WorkgroupInvitation {
+
+    protected String uniqueID;
+
+    protected String sessionID;
+
+    protected String groupChatName;
+    protected String issuingWorkgroupName;
+    protected String messageBody;
+    protected String invitationSender;
+    protected Map<String, List<String>> metaData;
+
+    /**
+     * This calls the 5-argument constructor with a null MetaData argument value
+     *
+     * @param jid the jid string with which the issuing AgentSession or Workgroup instance
+     *                  was created
+     * @param group the jid of the room to which the person is invited
+     * @param workgroup the jid of the workgroup issuing the invitation
+     * @param sessID the session id associated with the pending chat
+     * @param msgBody the body of the message which contained the invitation
+     * @param from the user jid who issued the invitation, if known, null otherwise
+     */
+    public WorkgroupInvitation (String jid, String group, String workgroup,
+                       String sessID, String msgBody, String from) {
+        this(jid, group, workgroup, sessID, msgBody, from, null);
+    }
+
+    /**
+     * @param jid the jid string with which the issuing AgentSession or Workgroup instance
+     *                  was created
+     * @param group the jid of the room to which the person is invited
+     * @param workgroup the jid of the workgroup issuing the invitation
+     * @param sessID the session id associated with the pending chat
+     * @param msgBody the body of the message which contained the invitation
+     * @param from the user jid who issued the invitation, if known, null otherwise
+     * @param metaData the metadata sent with the invitation
+     */
+    public WorkgroupInvitation (String jid, String group, String workgroup, String sessID, String msgBody,
+                       String from, Map<String, List<String>> metaData) {
+        super();
+
+        this.uniqueID = jid;
+        this.sessionID = sessID;
+        this.groupChatName = group;
+        this.issuingWorkgroupName = workgroup;
+        this.messageBody = msgBody;
+        this.invitationSender = from;
+        this.metaData = metaData;
+    }
+
+    /**
+     * @return the jid string with which the issuing AgentSession or Workgroup instance
+     *  was created.
+     */
+    public String getUniqueID () {
+        return this.uniqueID;
+    }
+
+    /**
+     * @return the session id associated with the pending chat; working backwards temporally,
+     *              this session id should match the session id to the corresponding offer request
+     *              which resulted in this invitation.
+     */
+    public String getSessionID () {
+        return this.sessionID;
+    }
+
+    /**
+     * @return the jid of the room to which the person is invited.
+     */
+    public String getGroupChatName () {
+        return this.groupChatName;
+    }
+
+    /**
+     * @return the name of the workgroup from which the invitation was issued.
+     */
+    public String getWorkgroupName () {
+        return this.issuingWorkgroupName;
+    }
+
+    /**
+     * @return the contents of the body-block of the message that housed this invitation.
+     */
+    public String getMessageBody () {
+        return this.messageBody;
+    }
+
+    /**
+     * @return the user who issued the invitation, or null if it wasn't known.
+     */
+    public String getInvitationSender () {
+        return this.invitationSender;
+    }
+
+    /**
+     * @return the meta data associated with the invitation, or null if this instance was
+     *              constructed with none
+     */
+    public Map<String, List<String>> getMetaData () {
+        return this.metaData;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java
index bc73242..06634d7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java
@@ -1,39 +1,39 @@
-/**

- * $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.smackx.workgroup;

-

-/**

- * An interface which all classes interested in hearing about group chat invitations should

- *  implement.

- *

- * @author loki der quaeler

- */

-public interface WorkgroupInvitationListener {

-

-    /**

-     * The implementing class instance will be notified via this method when an invitation

-     *  to join a group chat has been received from the server.

-     *

-     * @param invitation an Invitation instance embodying the information pertaining to the

-     *                      invitation

-     */

-    public void invitationReceived(WorkgroupInvitation invitation);

-

-}

+/**
+ * $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.smackx.workgroup;
+
+/**
+ * An interface which all classes interested in hearing about group chat invitations should
+ *  implement.
+ *
+ * @author loki der quaeler
+ */
+public interface WorkgroupInvitationListener {
+
+    /**
+     * The implementing class instance will be notified via this method when an invitation
+     *  to join a group chat has been received from the server.
+     *
+     * @param invitation an Invitation instance embodying the information pertaining to the
+     *                      invitation
+     */
+    public void invitationReceived(WorkgroupInvitation invitation);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Agent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Agent.java
index bebac37..d7e4fa2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Agent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Agent.java
@@ -1,138 +1,138 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smackx.workgroup.packet.AgentInfo;

-import org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups;

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.IQ;

-

-import java.util.Collection;

-

-/**

- * The <code>Agent</code> class is used to represent one agent in a Workgroup Queue.

- *

- * @author Derek DeMoro

- */

-public class Agent {

-    private Connection connection;

-    private String workgroupJID;

-

-    public static Collection<String> getWorkgroups(String serviceJID, String agentJID, Connection connection) throws XMPPException {

-        AgentWorkgroups request = new AgentWorkgroups(agentJID);

-        request.setTo(serviceJID);

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        // Send the request

-        connection.sendPacket(request);

-

-        AgentWorkgroups response = (AgentWorkgroups)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response.getWorkgroups();

-    }

-

-    /**

-     * Constructs an Agent.

-     */

-    Agent(Connection connection, String workgroupJID) {

-        this.connection = connection;

-        this.workgroupJID = workgroupJID;

-    }

-

-    /**

-     * Return the agents JID

-     *

-     * @return - the agents JID.

-     */

-    public String getUser() {

-        return connection.getUser();

-    }

-

-    /**

-     * Return the agents name.

-     *

-     * @return - the agents name.

-     */

-    public String getName() throws XMPPException {

-        AgentInfo agentInfo = new AgentInfo();

-        agentInfo.setType(IQ.Type.GET);

-        agentInfo.setTo(workgroupJID);

-        agentInfo.setFrom(getUser());

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(agentInfo.getPacketID()));

-        // Send the request

-        connection.sendPacket(agentInfo);

-

-        AgentInfo response = (AgentInfo)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response.getName();

-    }

-

-    /**

-     * Changes the name of the agent in the server. The server may have this functionality

-     * disabled for all the agents or for this agent in particular. If the agent is not

-     * allowed to change his name then an exception will be thrown with a service_unavailable

-     * error code.

-     *

-     * @param newName the new name of the agent.

-     * @throws XMPPException if the agent is not allowed to change his name or no response was

-     *                       obtained from the server.

-     */

-    public void setName(String newName) throws XMPPException {

-        AgentInfo agentInfo = new AgentInfo();

-        agentInfo.setType(IQ.Type.SET);

-        agentInfo.setTo(workgroupJID);

-        agentInfo.setFrom(getUser());

-        agentInfo.setName(newName);

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(agentInfo.getPacketID()));

-        // Send the request

-        connection.sendPacket(agentInfo);

-

-        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return;

-    }

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smackx.workgroup.packet.AgentInfo;
+import org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.IQ;
+
+import java.util.Collection;
+
+/**
+ * The <code>Agent</code> class is used to represent one agent in a Workgroup Queue.
+ *
+ * @author Derek DeMoro
+ */
+public class Agent {
+    private Connection connection;
+    private String workgroupJID;
+
+    public static Collection<String> getWorkgroups(String serviceJID, String agentJID, Connection connection) throws XMPPException {
+        AgentWorkgroups request = new AgentWorkgroups(agentJID);
+        request.setTo(serviceJID);
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        // Send the request
+        connection.sendPacket(request);
+
+        AgentWorkgroups response = (AgentWorkgroups)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response.getWorkgroups();
+    }
+
+    /**
+     * Constructs an Agent.
+     */
+    Agent(Connection connection, String workgroupJID) {
+        this.connection = connection;
+        this.workgroupJID = workgroupJID;
+    }
+
+    /**
+     * Return the agents JID
+     *
+     * @return - the agents JID.
+     */
+    public String getUser() {
+        return connection.getUser();
+    }
+
+    /**
+     * Return the agents name.
+     *
+     * @return - the agents name.
+     */
+    public String getName() throws XMPPException {
+        AgentInfo agentInfo = new AgentInfo();
+        agentInfo.setType(IQ.Type.GET);
+        agentInfo.setTo(workgroupJID);
+        agentInfo.setFrom(getUser());
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(agentInfo.getPacketID()));
+        // Send the request
+        connection.sendPacket(agentInfo);
+
+        AgentInfo response = (AgentInfo)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response.getName();
+    }
+
+    /**
+     * Changes the name of the agent in the server. The server may have this functionality
+     * disabled for all the agents or for this agent in particular. If the agent is not
+     * allowed to change his name then an exception will be thrown with a service_unavailable
+     * error code.
+     *
+     * @param newName the new name of the agent.
+     * @throws XMPPException if the agent is not allowed to change his name or no response was
+     *                       obtained from the server.
+     */
+    public void setName(String newName) throws XMPPException {
+        AgentInfo agentInfo = new AgentInfo();
+        agentInfo.setType(IQ.Type.SET);
+        agentInfo.setTo(workgroupJID);
+        agentInfo.setFrom(getUser());
+        agentInfo.setName(newName);
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(agentInfo.getPacketID()));
+        // Send the request
+        connection.sendPacket(agentInfo);
+
+        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java
index 70c95ee..edeb195 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java
@@ -1,386 +1,386 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smackx.workgroup.packet.AgentStatus;

-import org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest;

-import org.jivesoftware.smack.PacketListener;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.filter.PacketFilter;

-import org.jivesoftware.smack.filter.PacketTypeFilter;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.packet.Presence;

-import org.jivesoftware.smack.util.StringUtils;

-

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.HashSet;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

-import java.util.Set;

-

-/**

- * Manges information about the agents in a workgroup and their presence.

- *

- * @author Matt Tucker

- * @see AgentSession#getAgentRoster()

- */

-public class AgentRoster {

-

-    private static final int EVENT_AGENT_ADDED = 0;

-    private static final int EVENT_AGENT_REMOVED = 1;

-    private static final int EVENT_PRESENCE_CHANGED = 2;

-

-    private Connection connection;

-    private String workgroupJID;

-    private List<String> entries;

-    private List<AgentRosterListener> listeners;

-    private Map<String, Map<String, Presence>> presenceMap;

-    // The roster is marked as initialized when at least a single roster packet

-    // has been recieved and processed.

-    boolean rosterInitialized = false;

-

-    /**

-     * Constructs a new AgentRoster.

-     *

-     * @param connection an XMPP connection.

-     */

-    AgentRoster(Connection connection, String workgroupJID) {

-        this.connection = connection;

-        this.workgroupJID = workgroupJID;

-        entries = new ArrayList<String>();

-        listeners = new ArrayList<AgentRosterListener>();

-        presenceMap = new HashMap<String, Map<String, Presence>>();

-        // Listen for any roster packets.

-        PacketFilter rosterFilter = new PacketTypeFilter(AgentStatusRequest.class);

-        connection.addPacketListener(new AgentStatusListener(), rosterFilter);

-        // Listen for any presence packets.

-        connection.addPacketListener(new PresencePacketListener(),

-                new PacketTypeFilter(Presence.class));

-

-        // Send request for roster.

-        AgentStatusRequest request = new AgentStatusRequest();

-        request.setTo(workgroupJID);

-        connection.sendPacket(request);

-    }

-

-    /**

-     * Reloads the entire roster from the server. This is an asynchronous operation,

-     * which means the method will return immediately, and the roster will be

-     * reloaded at a later point when the server responds to the reload request.

-     */

-    public void reload() {

-        AgentStatusRequest request = new AgentStatusRequest();

-        request.setTo(workgroupJID);

-        connection.sendPacket(request);

-    }

-

-    /**

-     * Adds a listener to this roster. The listener will be fired anytime one or more

-     * changes to the roster are pushed from the server.

-     *

-     * @param listener an agent roster listener.

-     */

-    public void addListener(AgentRosterListener listener) {

-        synchronized (listeners) {

-            if (!listeners.contains(listener)) {

-                listeners.add(listener);

-

-                // Fire events for the existing entries and presences in the roster

-                for (Iterator<String> it = getAgents().iterator(); it.hasNext();) {

-                    String jid = it.next();

-                    // Check again in case the agent is no longer in the roster (highly unlikely

-                    // but possible)

-                    if (entries.contains(jid)) {

-                        // Fire the agent added event

-                        listener.agentAdded(jid);

-                        Map<String,Presence> userPresences = presenceMap.get(jid);

-                        if (userPresences != null) {

-                            Iterator<Presence> presences = userPresences.values().iterator();

-                            while (presences.hasNext()) {

-                                // Fire the presence changed event

-                                listener.presenceChanged(presences.next());

-                            }

-                        }

-                    }

-                }

-            }

-        }

-    }

-

-    /**

-     * Removes a listener from this roster. The listener will be fired anytime one or more

-     * changes to the roster are pushed from the server.

-     *

-     * @param listener a roster listener.

-     */

-    public void removeListener(AgentRosterListener listener) {

-        synchronized (listeners) {

-            listeners.remove(listener);

-        }

-    }

-

-    /**

-     * Returns a count of all agents in the workgroup.

-     *

-     * @return the number of agents in the workgroup.

-     */

-    public int getAgentCount() {

-        return entries.size();

-    }

-

-    /**

-     * Returns all agents (String JID values) in the workgroup.

-     *

-     * @return all entries in the roster.

-     */

-    public Set<String> getAgents() {

-        Set<String> agents = new HashSet<String>();

-        synchronized (entries) {

-            for (Iterator<String> i = entries.iterator(); i.hasNext();) {

-                agents.add(i.next());

-            }

-        }

-        return Collections.unmodifiableSet(agents);

-    }

-

-    /**

-     * Returns true if the specified XMPP address is an agent in the workgroup.

-     *

-     * @param jid the XMPP address of the agent (eg "jsmith@example.com"). The

-     *            address can be in any valid format (e.g. "domain/resource", "user@domain"

-     *            or "user@domain/resource").

-     * @return true if the XMPP address is an agent in the workgroup.

-     */

-    public boolean contains(String jid) {

-        if (jid == null) {

-            return false;

-        }

-        synchronized (entries) {

-            for (Iterator<String> i = entries.iterator(); i.hasNext();) {

-                String entry = i.next();

-                if (entry.toLowerCase().equals(jid.toLowerCase())) {

-                    return true;

-                }

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Returns the presence info for a particular agent, or <tt>null</tt> if the agent

-     * is unavailable (offline) or if no presence information is available.<p>

-     *

-     * @param user a fully qualified xmpp JID. The address could be in any valid format (e.g.

-     *             "domain/resource", "user@domain" or "user@domain/resource").

-     * @return the agent's current presence, or <tt>null</tt> if the agent is unavailable

-     *         or if no presence information is available..

-     */

-    public Presence getPresence(String user) {

-        String key = getPresenceMapKey(user);

-        Map<String, Presence> userPresences = presenceMap.get(key);

-        if (userPresences == null) {

-            Presence presence = new Presence(Presence.Type.unavailable);

-            presence.setFrom(user);

-            return presence;

-        }

-        else {

-            // Find the resource with the highest priority

-            // Might be changed to use the resource with the highest availability instead.

-            Iterator<String> it = userPresences.keySet().iterator();

-            Presence p;

-            Presence presence = null;

-

-            while (it.hasNext()) {

-                p = (Presence)userPresences.get(it.next());

-                if (presence == null){

-                    presence = p;

-                }

-                else {

-                    if (p.getPriority() > presence.getPriority()) {

-                        presence = p;

-                    }

-                }

-            }

-            if (presence == null) {

-                presence = new Presence(Presence.Type.unavailable);

-                presence.setFrom(user);

-                return presence;

-            }

-            else {

-                return presence;

-            }

-        }

-    }

-

-    /**

-     * Returns the key to use in the presenceMap for a fully qualified xmpp ID. The roster

-     * can contain any valid address format such us "domain/resource", "user@domain" or

-     * "user@domain/resource". If the roster contains an entry associated with the fully qualified

-     * xmpp ID then use the fully qualified xmpp ID as the key in presenceMap, otherwise use the

-     * bare address. Note: When the key in presenceMap is a fully qualified xmpp ID, the

-     * userPresences is useless since it will always contain one entry for the user.

-     *

-     * @param user the fully qualified xmpp ID, e.g. jdoe@example.com/Work.

-     * @return the key to use in the presenceMap for the fully qualified xmpp ID.

-     */

-    private String getPresenceMapKey(String user) {

-        String key = user;

-        if (!contains(user)) {

-            key = StringUtils.parseBareAddress(user).toLowerCase();

-        }

-        return key;

-    }

-

-    /**

-     * Fires event to listeners.

-     */

-    private void fireEvent(int eventType, Object eventObject) {

-        AgentRosterListener[] listeners = null;

-        synchronized (this.listeners) {

-            listeners = new AgentRosterListener[this.listeners.size()];

-            this.listeners.toArray(listeners);

-        }

-        for (int i = 0; i < listeners.length; i++) {

-            switch (eventType) {

-                case EVENT_AGENT_ADDED:

-                    listeners[i].agentAdded((String)eventObject);

-                    break;

-                case EVENT_AGENT_REMOVED:

-                    listeners[i].agentRemoved((String)eventObject);

-                    break;

-                case EVENT_PRESENCE_CHANGED:

-                    listeners[i].presenceChanged((Presence)eventObject);

-                    break;

-            }

-        }

-    }

-

-    /**

-     * Listens for all presence packets and processes them.

-     */

-    private class PresencePacketListener implements PacketListener {

-        public void processPacket(Packet packet) {

-            Presence presence = (Presence)packet;

-            String from = presence.getFrom();

-            if (from == null) {

-                // TODO Check if we need to ignore these presences or this is a server bug?

-                System.out.println("Presence with no FROM: " + presence.toXML());

-                return;

-            }

-            String key = getPresenceMapKey(from);

-

-            // If an "available" packet, add it to the presence map. Each presence map will hold

-            // for a particular user a map with the presence packets saved for each resource.

-            if (presence.getType() == Presence.Type.available) {

-                // Ignore the presence packet unless it has an agent status extension.

-                AgentStatus agentStatus = (AgentStatus)presence.getExtension(

-                        AgentStatus.ELEMENT_NAME, AgentStatus.NAMESPACE);

-                if (agentStatus == null) {

-                    return;

-                }

-                // Ensure that this presence is coming from an Agent of the same workgroup

-                // of this Agent

-                else if (!workgroupJID.equals(agentStatus.getWorkgroupJID())) {

-                    return;

-                }

-                Map<String, Presence> userPresences;

-                // Get the user presence map

-                if (presenceMap.get(key) == null) {

-                    userPresences = new HashMap<String, Presence>();

-                    presenceMap.put(key, userPresences);

-                }

-                else {

-                    userPresences = presenceMap.get(key);

-                }

-                // Add the new presence, using the resources as a key.

-                synchronized (userPresences) {

-                    userPresences.put(StringUtils.parseResource(from), presence);

-                }

-                // Fire an event.

-                synchronized (entries) {

-                    for (Iterator<String> i = entries.iterator(); i.hasNext();) {

-                        String entry = i.next();

-                        if (entry.toLowerCase().equals(StringUtils.parseBareAddress(key).toLowerCase())) {

-                            fireEvent(EVENT_PRESENCE_CHANGED, packet);

-                        }

-                    }

-                }

-            }

-            // If an "unavailable" packet, remove any entries in the presence map.

-            else if (presence.getType() == Presence.Type.unavailable) {

-                if (presenceMap.get(key) != null) {

-                    Map<String,Presence> userPresences = presenceMap.get(key);

-                    synchronized (userPresences) {

-                        userPresences.remove(StringUtils.parseResource(from));

-                    }

-                    if (userPresences.isEmpty()) {

-                        presenceMap.remove(key);

-                    }

-                }

-                // Fire an event.

-                synchronized (entries) {

-                    for (Iterator<String> i = entries.iterator(); i.hasNext();) {

-                        String entry = (String)i.next();

-                        if (entry.toLowerCase().equals(StringUtils.parseBareAddress(key).toLowerCase())) {

-                            fireEvent(EVENT_PRESENCE_CHANGED, packet);

-                        }

-                    }

-                }

-            }

-        }

-    }

-

-    /**

-     * Listens for all roster packets and processes them.

-     */

-    private class AgentStatusListener implements PacketListener {

-

-        public void processPacket(Packet packet) {

-            if (packet instanceof AgentStatusRequest) {

-                AgentStatusRequest statusRequest = (AgentStatusRequest)packet;

-                for (Iterator<AgentStatusRequest.Item> i = statusRequest.getAgents().iterator(); i.hasNext();) {

-                    AgentStatusRequest.Item item = i.next();

-                    String agentJID = item.getJID();

-                    if ("remove".equals(item.getType())) {

-

-                        // Removing the user from the roster, so remove any presence information

-                        // about them.

-                        String key = StringUtils.parseName(StringUtils.parseName(agentJID) + "@" +

-                                StringUtils.parseServer(agentJID));

-                        presenceMap.remove(key);

-                        // Fire event for roster listeners.

-                        fireEvent(EVENT_AGENT_REMOVED, agentJID);

-                    }

-                    else {

-                        entries.add(agentJID);

-                        // Fire event for roster listeners.

-                        fireEvent(EVENT_AGENT_ADDED, agentJID);

-                    }

-                }

-

-                // Mark the roster as initialized.

-                rosterInitialized = true;

-            }

-        }

-    }

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smackx.workgroup.packet.AgentStatus;
+import org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest;
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manges information about the agents in a workgroup and their presence.
+ *
+ * @author Matt Tucker
+ * @see AgentSession#getAgentRoster()
+ */
+public class AgentRoster {
+
+    private static final int EVENT_AGENT_ADDED = 0;
+    private static final int EVENT_AGENT_REMOVED = 1;
+    private static final int EVENT_PRESENCE_CHANGED = 2;
+
+    private Connection connection;
+    private String workgroupJID;
+    private List<String> entries;
+    private List<AgentRosterListener> listeners;
+    private Map<String, Map<String, Presence>> presenceMap;
+    // The roster is marked as initialized when at least a single roster packet
+    // has been recieved and processed.
+    boolean rosterInitialized = false;
+
+    /**
+     * Constructs a new AgentRoster.
+     *
+     * @param connection an XMPP connection.
+     */
+    AgentRoster(Connection connection, String workgroupJID) {
+        this.connection = connection;
+        this.workgroupJID = workgroupJID;
+        entries = new ArrayList<String>();
+        listeners = new ArrayList<AgentRosterListener>();
+        presenceMap = new HashMap<String, Map<String, Presence>>();
+        // Listen for any roster packets.
+        PacketFilter rosterFilter = new PacketTypeFilter(AgentStatusRequest.class);
+        connection.addPacketListener(new AgentStatusListener(), rosterFilter);
+        // Listen for any presence packets.
+        connection.addPacketListener(new PresencePacketListener(),
+                new PacketTypeFilter(Presence.class));
+
+        // Send request for roster.
+        AgentStatusRequest request = new AgentStatusRequest();
+        request.setTo(workgroupJID);
+        connection.sendPacket(request);
+    }
+
+    /**
+     * Reloads the entire roster from the server. This is an asynchronous operation,
+     * which means the method will return immediately, and the roster will be
+     * reloaded at a later point when the server responds to the reload request.
+     */
+    public void reload() {
+        AgentStatusRequest request = new AgentStatusRequest();
+        request.setTo(workgroupJID);
+        connection.sendPacket(request);
+    }
+
+    /**
+     * Adds a listener to this roster. The listener will be fired anytime one or more
+     * changes to the roster are pushed from the server.
+     *
+     * @param listener an agent roster listener.
+     */
+    public void addListener(AgentRosterListener listener) {
+        synchronized (listeners) {
+            if (!listeners.contains(listener)) {
+                listeners.add(listener);
+
+                // Fire events for the existing entries and presences in the roster
+                for (Iterator<String> it = getAgents().iterator(); it.hasNext();) {
+                    String jid = it.next();
+                    // Check again in case the agent is no longer in the roster (highly unlikely
+                    // but possible)
+                    if (entries.contains(jid)) {
+                        // Fire the agent added event
+                        listener.agentAdded(jid);
+                        Map<String,Presence> userPresences = presenceMap.get(jid);
+                        if (userPresences != null) {
+                            Iterator<Presence> presences = userPresences.values().iterator();
+                            while (presences.hasNext()) {
+                                // Fire the presence changed event
+                                listener.presenceChanged(presences.next());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a listener from this roster. The listener will be fired anytime one or more
+     * changes to the roster are pushed from the server.
+     *
+     * @param listener a roster listener.
+     */
+    public void removeListener(AgentRosterListener listener) {
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+    /**
+     * Returns a count of all agents in the workgroup.
+     *
+     * @return the number of agents in the workgroup.
+     */
+    public int getAgentCount() {
+        return entries.size();
+    }
+
+    /**
+     * Returns all agents (String JID values) in the workgroup.
+     *
+     * @return all entries in the roster.
+     */
+    public Set<String> getAgents() {
+        Set<String> agents = new HashSet<String>();
+        synchronized (entries) {
+            for (Iterator<String> i = entries.iterator(); i.hasNext();) {
+                agents.add(i.next());
+            }
+        }
+        return Collections.unmodifiableSet(agents);
+    }
+
+    /**
+     * Returns true if the specified XMPP address is an agent in the workgroup.
+     *
+     * @param jid the XMPP address of the agent (eg "jsmith@example.com"). The
+     *            address can be in any valid format (e.g. "domain/resource", "user@domain"
+     *            or "user@domain/resource").
+     * @return true if the XMPP address is an agent in the workgroup.
+     */
+    public boolean contains(String jid) {
+        if (jid == null) {
+            return false;
+        }
+        synchronized (entries) {
+            for (Iterator<String> i = entries.iterator(); i.hasNext();) {
+                String entry = i.next();
+                if (entry.toLowerCase().equals(jid.toLowerCase())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the presence info for a particular agent, or <tt>null</tt> if the agent
+     * is unavailable (offline) or if no presence information is available.<p>
+     *
+     * @param user a fully qualified xmpp JID. The address could be in any valid format (e.g.
+     *             "domain/resource", "user@domain" or "user@domain/resource").
+     * @return the agent's current presence, or <tt>null</tt> if the agent is unavailable
+     *         or if no presence information is available..
+     */
+    public Presence getPresence(String user) {
+        String key = getPresenceMapKey(user);
+        Map<String, Presence> userPresences = presenceMap.get(key);
+        if (userPresences == null) {
+            Presence presence = new Presence(Presence.Type.unavailable);
+            presence.setFrom(user);
+            return presence;
+        }
+        else {
+            // Find the resource with the highest priority
+            // Might be changed to use the resource with the highest availability instead.
+            Iterator<String> it = userPresences.keySet().iterator();
+            Presence p;
+            Presence presence = null;
+
+            while (it.hasNext()) {
+                p = (Presence)userPresences.get(it.next());
+                if (presence == null){
+                    presence = p;
+                }
+                else {
+                    if (p.getPriority() > presence.getPriority()) {
+                        presence = p;
+                    }
+                }
+            }
+            if (presence == null) {
+                presence = new Presence(Presence.Type.unavailable);
+                presence.setFrom(user);
+                return presence;
+            }
+            else {
+                return presence;
+            }
+        }
+    }
+
+    /**
+     * Returns the key to use in the presenceMap for a fully qualified xmpp ID. The roster
+     * can contain any valid address format such us "domain/resource", "user@domain" or
+     * "user@domain/resource". If the roster contains an entry associated with the fully qualified
+     * xmpp ID then use the fully qualified xmpp ID as the key in presenceMap, otherwise use the
+     * bare address. Note: When the key in presenceMap is a fully qualified xmpp ID, the
+     * userPresences is useless since it will always contain one entry for the user.
+     *
+     * @param user the fully qualified xmpp ID, e.g. jdoe@example.com/Work.
+     * @return the key to use in the presenceMap for the fully qualified xmpp ID.
+     */
+    private String getPresenceMapKey(String user) {
+        String key = user;
+        if (!contains(user)) {
+            key = StringUtils.parseBareAddress(user).toLowerCase();
+        }
+        return key;
+    }
+
+    /**
+     * Fires event to listeners.
+     */
+    private void fireEvent(int eventType, Object eventObject) {
+        AgentRosterListener[] listeners = null;
+        synchronized (this.listeners) {
+            listeners = new AgentRosterListener[this.listeners.size()];
+            this.listeners.toArray(listeners);
+        }
+        for (int i = 0; i < listeners.length; i++) {
+            switch (eventType) {
+                case EVENT_AGENT_ADDED:
+                    listeners[i].agentAdded((String)eventObject);
+                    break;
+                case EVENT_AGENT_REMOVED:
+                    listeners[i].agentRemoved((String)eventObject);
+                    break;
+                case EVENT_PRESENCE_CHANGED:
+                    listeners[i].presenceChanged((Presence)eventObject);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Listens for all presence packets and processes them.
+     */
+    private class PresencePacketListener implements PacketListener {
+        public void processPacket(Packet packet) {
+            Presence presence = (Presence)packet;
+            String from = presence.getFrom();
+            if (from == null) {
+                // TODO Check if we need to ignore these presences or this is a server bug?
+                System.out.println("Presence with no FROM: " + presence.toXML());
+                return;
+            }
+            String key = getPresenceMapKey(from);
+
+            // If an "available" packet, add it to the presence map. Each presence map will hold
+            // for a particular user a map with the presence packets saved for each resource.
+            if (presence.getType() == Presence.Type.available) {
+                // Ignore the presence packet unless it has an agent status extension.
+                AgentStatus agentStatus = (AgentStatus)presence.getExtension(
+                        AgentStatus.ELEMENT_NAME, AgentStatus.NAMESPACE);
+                if (agentStatus == null) {
+                    return;
+                }
+                // Ensure that this presence is coming from an Agent of the same workgroup
+                // of this Agent
+                else if (!workgroupJID.equals(agentStatus.getWorkgroupJID())) {
+                    return;
+                }
+                Map<String, Presence> userPresences;
+                // Get the user presence map
+                if (presenceMap.get(key) == null) {
+                    userPresences = new HashMap<String, Presence>();
+                    presenceMap.put(key, userPresences);
+                }
+                else {
+                    userPresences = presenceMap.get(key);
+                }
+                // Add the new presence, using the resources as a key.
+                synchronized (userPresences) {
+                    userPresences.put(StringUtils.parseResource(from), presence);
+                }
+                // Fire an event.
+                synchronized (entries) {
+                    for (Iterator<String> i = entries.iterator(); i.hasNext();) {
+                        String entry = i.next();
+                        if (entry.toLowerCase().equals(StringUtils.parseBareAddress(key).toLowerCase())) {
+                            fireEvent(EVENT_PRESENCE_CHANGED, packet);
+                        }
+                    }
+                }
+            }
+            // If an "unavailable" packet, remove any entries in the presence map.
+            else if (presence.getType() == Presence.Type.unavailable) {
+                if (presenceMap.get(key) != null) {
+                    Map<String,Presence> userPresences = presenceMap.get(key);
+                    synchronized (userPresences) {
+                        userPresences.remove(StringUtils.parseResource(from));
+                    }
+                    if (userPresences.isEmpty()) {
+                        presenceMap.remove(key);
+                    }
+                }
+                // Fire an event.
+                synchronized (entries) {
+                    for (Iterator<String> i = entries.iterator(); i.hasNext();) {
+                        String entry = (String)i.next();
+                        if (entry.toLowerCase().equals(StringUtils.parseBareAddress(key).toLowerCase())) {
+                            fireEvent(EVENT_PRESENCE_CHANGED, packet);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Listens for all roster packets and processes them.
+     */
+    private class AgentStatusListener implements PacketListener {
+
+        public void processPacket(Packet packet) {
+            if (packet instanceof AgentStatusRequest) {
+                AgentStatusRequest statusRequest = (AgentStatusRequest)packet;
+                for (Iterator<AgentStatusRequest.Item> i = statusRequest.getAgents().iterator(); i.hasNext();) {
+                    AgentStatusRequest.Item item = i.next();
+                    String agentJID = item.getJID();
+                    if ("remove".equals(item.getType())) {
+
+                        // Removing the user from the roster, so remove any presence information
+                        // about them.
+                        String key = StringUtils.parseName(StringUtils.parseName(agentJID) + "@" +
+                                StringUtils.parseServer(agentJID));
+                        presenceMap.remove(key);
+                        // Fire event for roster listeners.
+                        fireEvent(EVENT_AGENT_REMOVED, agentJID);
+                    }
+                    else {
+                        entries.add(agentJID);
+                        // Fire event for roster listeners.
+                        fireEvent(EVENT_AGENT_ADDED, agentJID);
+                    }
+                }
+
+                // Mark the roster as initialized.
+                rosterInitialized = true;
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java
index 4db9203..afea9ff 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java
@@ -1,35 +1,35 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smack.packet.Presence;

-

-/**

- *

- * @author Matt Tucker

- */

-public interface AgentRosterListener {

-

-    public void agentAdded(String jid);

-

-    public void agentRemoved(String jid);

-

-    public void presenceChanged(Presence presence);

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smack.packet.Presence;
+
+/**
+ *
+ * @author Matt Tucker
+ */
+public interface AgentRosterListener {
+
+    public void agentAdded(String jid);
+
+    public void agentRemoved(String jid);
+
+    public void presenceChanged(Presence presence);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentSession.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentSession.java
index 46d19d0..0600644 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentSession.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/AgentSession.java
@@ -1,1185 +1,1185 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smackx.workgroup.MetaData;

-import org.jivesoftware.smackx.workgroup.QueueUser;

-import org.jivesoftware.smackx.workgroup.WorkgroupInvitation;

-import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener;

-import org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory;

-import org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata;

-import org.jivesoftware.smackx.workgroup.ext.macros.MacroGroup;

-import org.jivesoftware.smackx.workgroup.ext.macros.Macros;

-import org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes;

-import org.jivesoftware.smackx.workgroup.packet.*;

-import org.jivesoftware.smackx.workgroup.settings.GenericSettings;

-import org.jivesoftware.smackx.workgroup.settings.SearchSettings;

-import org.jivesoftware.smack.*;

-import org.jivesoftware.smack.filter.*;

-import org.jivesoftware.smack.packet.*;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.ReportedData;

-import org.jivesoftware.smackx.packet.MUCUser;

-

-import java.util.*;

-

-/**

- * This class embodies the agent's active presence within a given workgroup. The application

- * should have N instances of this class, where N is the number of workgroups to which the

- * owning agent of the application belongs. This class provides all functionality that a

- * session within a given workgroup is expected to have from an agent's perspective -- setting

- * the status, tracking the status of queues to which the agent belongs within the workgroup, and

- * dequeuing customers.

- *

- * @author Matt Tucker

- * @author Derek DeMoro

- */

-public class AgentSession {

-

-    private Connection connection;

-

-    private String workgroupJID;

-

-    private boolean online = false;

-    private Presence.Mode presenceMode;

-    private int maxChats;

-    private final Map<String, List<String>> metaData;

-

-    private Map<String, WorkgroupQueue> queues;

-

-    private final List<OfferListener> offerListeners;

-    private final List<WorkgroupInvitationListener> invitationListeners;

-    private final List<QueueUsersListener> queueUsersListeners;

-

-    private AgentRoster agentRoster = null;

-    private TranscriptManager transcriptManager;

-    private TranscriptSearchManager transcriptSearchManager;

-    private Agent agent;

-    private PacketListener packetListener;

-

-    /**

-     * Constructs a new agent session instance. Note, the {@link #setOnline(boolean)}

-     * method must be called with an argument of <tt>true</tt> to mark the agent

-     * as available to accept chat requests.

-     *

-     * @param connection   a connection instance which must have already gone through

-     *                     authentication.

-     * @param workgroupJID the fully qualified JID of the workgroup.

-     */

-    public AgentSession(String workgroupJID, Connection connection) {

-        // Login must have been done before passing in connection.

-        if (!connection.isAuthenticated()) {

-            throw new IllegalStateException("Must login to server before creating workgroup.");

-        }

-

-        this.workgroupJID = workgroupJID;

-        this.connection = connection;

-        this.transcriptManager = new TranscriptManager(connection);

-        this.transcriptSearchManager = new TranscriptSearchManager(connection);

-

-        this.maxChats = -1;

-

-        this.metaData = new HashMap<String, List<String>>();

-

-        this.queues = new HashMap<String, WorkgroupQueue>();

-

-        offerListeners = new ArrayList<OfferListener>();

-        invitationListeners = new ArrayList<WorkgroupInvitationListener>();

-        queueUsersListeners = new ArrayList<QueueUsersListener>();

-

-        // Create a filter to listen for packets we're interested in.

-        OrFilter filter = new OrFilter();

-        filter.addFilter(new PacketTypeFilter(OfferRequestProvider.OfferRequestPacket.class));

-        filter.addFilter(new PacketTypeFilter(OfferRevokeProvider.OfferRevokePacket.class));

-        filter.addFilter(new PacketTypeFilter(Presence.class));

-        filter.addFilter(new PacketTypeFilter(Message.class));

-

-        packetListener = new PacketListener() {

-            public void processPacket(Packet packet) {

-                try {

-                    handlePacket(packet);

-                }

-                catch (Exception e) {

-                    e.printStackTrace();

-                }

-            }

-        };

-        connection.addPacketListener(packetListener, filter);

-        // Create the agent associated to this session

-        agent = new Agent(connection, workgroupJID);

-    }

-

-    /**

-     * Close the agent session. The underlying connection will remain opened but the

-     * packet listeners that were added by this agent session will be removed.

-     */

-    public void close() {

-        connection.removePacketListener(packetListener);

-    }

-

-    /**

-     * Returns the agent roster for the workgroup, which contains

-     *

-     * @return the AgentRoster

-     */

-    public AgentRoster getAgentRoster() {

-        if (agentRoster == null) {

-            agentRoster = new AgentRoster(connection, workgroupJID);

-        }

-

-        // This might be the first time the user has asked for the roster. If so, we

-        // want to wait up to 2 seconds for the server to send back the list of agents.

-        // This behavior shields API users from having to worry about the fact that the

-        // operation is asynchronous, although they'll still have to listen for changes

-        // to the roster.

-        int elapsed = 0;

-        while (!agentRoster.rosterInitialized && elapsed <= 2000) {

-            try {

-                Thread.sleep(500);

-            }

-            catch (Exception e) {

-                // Ignore

-            }

-            elapsed += 500;

-        }

-        return agentRoster;

-    }

-

-    /**

-     * Returns the agent's current presence mode.

-     *

-     * @return the agent's current presence mode.

-     */

-    public Presence.Mode getPresenceMode() {

-        return presenceMode;

-    }

-

-    /**

-     * Returns the maximum number of chats the agent can participate in.

-     *

-     * @return the maximum number of chats the agent can participate in.

-     */

-    public int getMaxChats() {

-        return maxChats;

-    }

-

-    /**

-     * Returns true if the agent is online with the workgroup.

-     *

-     * @return true if the agent is online with the workgroup.

-     */

-    public boolean isOnline() {

-        return online;

-    }

-

-    /**

-     * Allows the addition of a new key-value pair to the agent's meta data, if the value is

-     * new data, the revised meta data will be rebroadcast in an agent's presence broadcast.

-     *

-     * @param key the meta data key

-     * @param val the non-null meta data value

-     * @throws XMPPException if an exception occurs.

-     */

-    public void setMetaData(String key, String val) throws XMPPException {

-        synchronized (this.metaData) {

-            List<String> oldVals = metaData.get(key);

-

-            if ((oldVals == null) || (!oldVals.get(0).equals(val))) {

-                oldVals.set(0, val);

-

-                setStatus(presenceMode, maxChats);

-            }

-        }

-    }

-

-    /**

-     * Allows the removal of data from the agent's meta data, if the key represents existing data,

-     * the revised meta data will be rebroadcast in an agent's presence broadcast.

-     *

-     * @param key the meta data key.

-     * @throws XMPPException if an exception occurs.

-     */

-    public void removeMetaData(String key) throws XMPPException {

-        synchronized (this.metaData) {

-            List<String> oldVal = metaData.remove(key);

-

-            if (oldVal != null) {

-                setStatus(presenceMode, maxChats);

-            }

-        }

-    }

-

-    /**

-     * Allows the retrieval of meta data for a specified key.

-     *

-     * @param key the meta data key

-     * @return the meta data value associated with the key or <tt>null</tt> if the meta-data

-     *         doesn't exist..

-     */

-    public List<String> getMetaData(String key) {

-        return metaData.get(key);

-    }

-

-    /**

-     * Sets whether the agent is online with the workgroup. If the user tries to go online with

-     * the workgroup but is not allowed to be an agent, an XMPPError with error code 401 will

-     * be thrown.

-     *

-     * @param online true to set the agent as online with the workgroup.

-     * @throws XMPPException if an error occurs setting the online status.

-     */

-    public void setOnline(boolean online) throws XMPPException {

-        // If the online status hasn't changed, do nothing.

-        if (this.online == online) {

-            return;

-        }

-

-        Presence presence;

-

-        // If the user is going online...

-        if (online) {

-            presence = new Presence(Presence.Type.available);

-            presence.setTo(workgroupJID);

-            presence.addExtension(new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,

-                    AgentStatus.NAMESPACE));

-

-            PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupJID)));

-

-            connection.sendPacket(presence);

-

-            presence = (Presence)collector.nextResult(5000);

-            collector.cancel();

-            if (!presence.isAvailable()) {

-                throw new XMPPException("No response from server on status set.");

-            }

-

-            if (presence.getError() != null) {

-                throw new XMPPException(presence.getError());

-            }

-

-            // We can safely update this iv since we didn't get any error

-            this.online = online;

-        }

-        // Otherwise the user is going offline...

-        else {

-            // Update this iv now since we don't care at this point of any error

-            this.online = online;

-

-            presence = new Presence(Presence.Type.unavailable);

-            presence.setTo(workgroupJID);

-            presence.addExtension(new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,

-                    AgentStatus.NAMESPACE));

-            connection.sendPacket(presence);

-        }

-    }

-

-    /**

-     * Sets the agent's current status with the workgroup. The presence mode affects

-     * how offers are routed to the agent. The possible presence modes with their

-     * meanings are as follows:<ul>

-     * <p/>

-     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats

-     * (equivalent to Presence.Mode.CHAT).

-     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.

-     * However, special case, or extreme urgency chats may still be offered to the agent.

-     * <li>Presence.Mode.AWAY -- the agent is not available and should not

-     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>

-     * <p/>

-     * The max chats value is the maximum number of chats the agent is willing to have

-     * routed to them at once. Some servers may be configured to only accept max chat

-     * values in a certain range; for example, between two and five. In that case, the

-     * maxChats value the agent sends may be adjusted by the server to a value within that

-     * range.

-     *

-     * @param presenceMode the presence mode of the agent.

-     * @param maxChats     the maximum number of chats the agent is willing to accept.

-     * @throws XMPPException         if an error occurs setting the agent status.

-     * @throws IllegalStateException if the agent is not online with the workgroup.

-     */

-    public void setStatus(Presence.Mode presenceMode, int maxChats) throws XMPPException {

-        setStatus(presenceMode, maxChats, null);

-    }

-

-    /**

-     * Sets the agent's current status with the workgroup. The presence mode affects how offers

-     * are routed to the agent. The possible presence modes with their meanings are as follows:<ul>

-     * <p/>

-     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats

-     * (equivalent to Presence.Mode.CHAT).

-     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.

-     * However, special case, or extreme urgency chats may still be offered to the agent.

-     * <li>Presence.Mode.AWAY -- the agent is not available and should not

-     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>

-     * <p/>

-     * The max chats value is the maximum number of chats the agent is willing to have routed to

-     * them at once. Some servers may be configured to only accept max chat values in a certain

-     * range; for example, between two and five. In that case, the maxChats value the agent sends

-     * may be adjusted by the server to a value within that range.

-     *

-     * @param presenceMode the presence mode of the agent.

-     * @param maxChats     the maximum number of chats the agent is willing to accept.

-     * @param status       sets the status message of the presence update.

-     * @throws XMPPException         if an error occurs setting the agent status.

-     * @throws IllegalStateException if the agent is not online with the workgroup.

-     */

-    public void setStatus(Presence.Mode presenceMode, int maxChats, String status)

-            throws XMPPException {

-        if (!online) {

-            throw new IllegalStateException("Cannot set status when the agent is not online.");

-        }

-

-        if (presenceMode == null) {

-            presenceMode = Presence.Mode.available;

-        }

-        this.presenceMode = presenceMode;

-        this.maxChats = maxChats;

-

-        Presence presence = new Presence(Presence.Type.available);

-        presence.setMode(presenceMode);

-        presence.setTo(this.getWorkgroupJID());

-

-        if (status != null) {

-            presence.setStatus(status);

-        }

-        // Send information about max chats and current chats as a packet extension.

-        DefaultPacketExtension agentStatus = new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,

-                AgentStatus.NAMESPACE);

-        agentStatus.setValue("max-chats", "" + maxChats);

-        presence.addExtension(agentStatus);

-        presence.addExtension(new MetaData(this.metaData));

-

-        PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupJID)));

-

-        this.connection.sendPacket(presence);

-

-        presence = (Presence)collector.nextResult(5000);

-        collector.cancel();

-        if (!presence.isAvailable()) {

-            throw new XMPPException("No response from server on status set.");

-        }

-

-        if (presence.getError() != null) {

-            throw new XMPPException(presence.getError());

-        }

-    }

-

-    /**

-     * Sets the agent's current status with the workgroup. The presence mode affects how offers

-     * are routed to the agent. The possible presence modes with their meanings are as follows:<ul>

-     * <p/>

-     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats

-     * (equivalent to Presence.Mode.CHAT).

-     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.

-     * However, special case, or extreme urgency chats may still be offered to the agent.

-     * <li>Presence.Mode.AWAY -- the agent is not available and should not

-     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>

-     *

-     * @param presenceMode the presence mode of the agent.

-     * @param status       sets the status message of the presence update.

-     * @throws XMPPException         if an error occurs setting the agent status.

-     * @throws IllegalStateException if the agent is not online with the workgroup.

-     */

-    public void setStatus(Presence.Mode presenceMode, String status) throws XMPPException {

-        if (!online) {

-            throw new IllegalStateException("Cannot set status when the agent is not online.");

-        }

-

-        if (presenceMode == null) {

-            presenceMode = Presence.Mode.available;

-        }

-        this.presenceMode = presenceMode;

-

-        Presence presence = new Presence(Presence.Type.available);

-        presence.setMode(presenceMode);

-        presence.setTo(this.getWorkgroupJID());

-

-        if (status != null) {

-            presence.setStatus(status);

-        }

-        presence.addExtension(new MetaData(this.metaData));

-

-        PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class),

-                new FromContainsFilter(workgroupJID)));

-

-        this.connection.sendPacket(presence);

-

-        presence = (Presence)collector.nextResult(5000);

-        collector.cancel();

-        if (!presence.isAvailable()) {

-            throw new XMPPException("No response from server on status set.");

-        }

-

-        if (presence.getError() != null) {

-            throw new XMPPException(presence.getError());

-        }

-    }

-

-    /**

-     * Removes a user from the workgroup queue. This is an administrative action that the

-     * <p/>

-     * The agent is not guaranteed of having privileges to perform this action; an exception

-     * denying the request may be thrown.

-     *

-     * @param userID the ID of the user to remove.

-     * @throws XMPPException if an exception occurs.

-     */

-    public void dequeueUser(String userID) throws XMPPException {

-        // todo: this method simply won't work right now.

-        DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID);

-

-        // PENDING

-        this.connection.sendPacket(departPacket);

-    }

-

-    /**

-     * Returns the transcripts of a given user. The answer will contain the complete history of

-     * conversations that a user had.

-     *

-     * @param userID the id of the user to get his conversations.

-     * @return the transcripts of a given user.

-     * @throws XMPPException if an error occurs while getting the information.

-     */

-    public Transcripts getTranscripts(String userID) throws XMPPException {

-        return transcriptManager.getTranscripts(workgroupJID, userID);

-    }

-

-    /**

-     * Returns the full conversation transcript of a given session.

-     *

-     * @param sessionID the id of the session to get the full transcript.

-     * @return the full conversation transcript of a given session.

-     * @throws XMPPException if an error occurs while getting the information.

-     */

-    public Transcript getTranscript(String sessionID) throws XMPPException {

-        return transcriptManager.getTranscript(workgroupJID, sessionID);

-    }

-

-    /**

-     * Returns the Form to use for searching transcripts. It is unlikely that the server

-     * will change the form (without a restart) so it is safe to keep the returned form

-     * for future submissions.

-     *

-     * @return the Form to use for searching transcripts.

-     * @throws XMPPException if an error occurs while sending the request to the server.

-     */

-    public Form getTranscriptSearchForm() throws XMPPException {

-        return transcriptSearchManager.getSearchForm(StringUtils.parseServer(workgroupJID));

-    }

-

-    /**

-     * Submits the completed form and returns the result of the transcript search. The result

-     * will include all the data returned from the server so be careful with the amount of

-     * data that the search may return.

-     *

-     * @param completedForm the filled out search form.

-     * @return the result of the transcript search.

-     * @throws XMPPException if an error occurs while submiting the search to the server.

-     */

-    public ReportedData searchTranscripts(Form completedForm) throws XMPPException {

-        return transcriptSearchManager.submitSearch(StringUtils.parseServer(workgroupJID),

-                completedForm);

-    }

-

-    /**

-     * Asks the workgroup for information about the occupants of the specified room. The returned

-     * information will include the real JID of the occupants, the nickname of the user in the

-     * room as well as the date when the user joined the room.

-     *

-     * @param roomID the room to get information about its occupants.

-     * @return information about the occupants of the specified room.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public OccupantsInfo getOccupantsInfo(String roomID) throws XMPPException {

-        OccupantsInfo request = new OccupantsInfo(roomID);

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-        OccupantsInfo response = (OccupantsInfo)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * @return the fully-qualified name of the workgroup for which this session exists

-     */

-    public String getWorkgroupJID() {

-        return workgroupJID;

-    }

-

-    /**

-     * Returns the Agent associated to this session.

-     *

-     * @return the Agent associated to this session.

-     */

-    public Agent getAgent() {

-        return agent;

-    }

-

-    /**

-     * @param queueName the name of the queue

-     * @return an instance of WorkgroupQueue for the argument queue name, or null if none exists

-     */

-    public WorkgroupQueue getQueue(String queueName) {

-        return queues.get(queueName);

-    }

-

-    public Iterator<WorkgroupQueue> getQueues() {

-        return Collections.unmodifiableMap((new HashMap<String, WorkgroupQueue>(queues))).values().iterator();

-    }

-

-    public void addQueueUsersListener(QueueUsersListener listener) {

-        synchronized (queueUsersListeners) {

-            if (!queueUsersListeners.contains(listener)) {

-                queueUsersListeners.add(listener);

-            }

-        }

-    }

-

-    public void removeQueueUsersListener(QueueUsersListener listener) {

-        synchronized (queueUsersListeners) {

-            queueUsersListeners.remove(listener);

-        }

-    }

-

-    /**

-     * Adds an offer listener.

-     *

-     * @param offerListener the offer listener.

-     */

-    public void addOfferListener(OfferListener offerListener) {

-        synchronized (offerListeners) {

-            if (!offerListeners.contains(offerListener)) {

-                offerListeners.add(offerListener);

-            }

-        }

-    }

-

-    /**

-     * Removes an offer listener.

-     *

-     * @param offerListener the offer listener.

-     */

-    public void removeOfferListener(OfferListener offerListener) {

-        synchronized (offerListeners) {

-            offerListeners.remove(offerListener);

-        }

-    }

-

-    /**

-     * Adds an invitation listener.

-     *

-     * @param invitationListener the invitation listener.

-     */

-    public void addInvitationListener(WorkgroupInvitationListener invitationListener) {

-        synchronized (invitationListeners) {

-            if (!invitationListeners.contains(invitationListener)) {

-                invitationListeners.add(invitationListener);

-            }

-        }

-    }

-

-    /**

-     * Removes an invitation listener.

-     *

-     * @param invitationListener the invitation listener.

-     */

-    public void removeInvitationListener(WorkgroupInvitationListener invitationListener) {

-        synchronized (invitationListeners) {

-            invitationListeners.remove(invitationListener);

-        }

-    }

-

-    private void fireOfferRequestEvent(OfferRequestProvider.OfferRequestPacket requestPacket) {

-        Offer offer = new Offer(this.connection, this, requestPacket.getUserID(),

-                requestPacket.getUserJID(), this.getWorkgroupJID(),

-                new Date((new Date()).getTime() + (requestPacket.getTimeout() * 1000)),

-                requestPacket.getSessionID(), requestPacket.getMetaData(), requestPacket.getContent());

-

-        synchronized (offerListeners) {

-            for (OfferListener listener : offerListeners) {

-                listener.offerReceived(offer);

-            }

-        }

-    }

-

-    private void fireOfferRevokeEvent(OfferRevokeProvider.OfferRevokePacket orp) {

-        RevokedOffer revokedOffer = new RevokedOffer(orp.getUserJID(), orp.getUserID(),

-                this.getWorkgroupJID(), orp.getSessionID(), orp.getReason(), new Date());

-

-        synchronized (offerListeners) {

-            for (OfferListener listener : offerListeners) {

-                listener.offerRevoked(revokedOffer);

-            }

-        }

-    }

-

-    private void fireInvitationEvent(String groupChatJID, String sessionID, String body,

-                                     String from, Map<String, List<String>> metaData) {

-        WorkgroupInvitation invitation = new WorkgroupInvitation(connection.getUser(), groupChatJID,

-                workgroupJID, sessionID, body, from, metaData);

-

-        synchronized (invitationListeners) {

-            for (WorkgroupInvitationListener listener : invitationListeners) {

-                listener.invitationReceived(invitation);

-            }

-        }

-    }

-

-    private void fireQueueUsersEvent(WorkgroupQueue queue, WorkgroupQueue.Status status,

-                                     int averageWaitTime, Date oldestEntry, Set<QueueUser> users) {

-        synchronized (queueUsersListeners) {

-            for (QueueUsersListener listener : queueUsersListeners) {

-                if (status != null) {

-                    listener.statusUpdated(queue, status);

-                }

-                if (averageWaitTime != -1) {

-                    listener.averageWaitTimeUpdated(queue, averageWaitTime);

-                }

-                if (oldestEntry != null) {

-                    listener.oldestEntryUpdated(queue, oldestEntry);

-                }

-                if (users != null) {

-                    listener.usersUpdated(queue, users);

-                }

-            }

-        }

-    }

-

-    // PacketListener Implementation.

-

-    private void handlePacket(Packet packet) {

-        if (packet instanceof OfferRequestProvider.OfferRequestPacket) {

-            // Acknowledge the IQ set.

-            IQ reply = new IQ() {

-                public String getChildElementXML() {

-                    return null;

-                }

-            };

-            reply.setPacketID(packet.getPacketID());

-            reply.setTo(packet.getFrom());

-            reply.setType(IQ.Type.RESULT);

-            connection.sendPacket(reply);

-

-            fireOfferRequestEvent((OfferRequestProvider.OfferRequestPacket)packet);

-        }

-        else if (packet instanceof Presence) {

-            Presence presence = (Presence)packet;

-

-            // The workgroup can send us a number of different presence packets. We

-            // check for different packet extensions to see what type of presence

-            // packet it is.

-

-            String queueName = StringUtils.parseResource(presence.getFrom());

-            WorkgroupQueue queue = queues.get(queueName);

-            // If there isn't already an entry for the queue, create a new one.

-            if (queue == null) {

-                queue = new WorkgroupQueue(queueName);

-                queues.put(queueName, queue);

-            }

-

-            // QueueOverview packet extensions contain basic information about a queue.

-            QueueOverview queueOverview = (QueueOverview)presence.getExtension(QueueOverview.ELEMENT_NAME, QueueOverview.NAMESPACE);

-            if (queueOverview != null) {

-                if (queueOverview.getStatus() == null) {

-                    queue.setStatus(WorkgroupQueue.Status.CLOSED);

-                }

-                else {

-                    queue.setStatus(queueOverview.getStatus());

-                }

-                queue.setAverageWaitTime(queueOverview.getAverageWaitTime());

-                queue.setOldestEntry(queueOverview.getOldestEntry());

-                // Fire event.

-                fireQueueUsersEvent(queue, queueOverview.getStatus(),

-                        queueOverview.getAverageWaitTime(), queueOverview.getOldestEntry(),

-                        null);

-                return;

-            }

-

-            // QueueDetails packet extensions contain information about the users in

-            // a queue.

-            QueueDetails queueDetails = (QueueDetails)packet.getExtension(QueueDetails.ELEMENT_NAME, QueueDetails.NAMESPACE);

-            if (queueDetails != null) {

-                queue.setUsers(queueDetails.getUsers());

-                // Fire event.

-                fireQueueUsersEvent(queue, null, -1, null, queueDetails.getUsers());

-                return;

-            }

-

-            // Notify agent packets gives an overview of agent activity in a queue.

-            DefaultPacketExtension notifyAgents = (DefaultPacketExtension)presence.getExtension("notify-agents", "http://jabber.org/protocol/workgroup");

-            if (notifyAgents != null) {

-                int currentChats = Integer.parseInt(notifyAgents.getValue("current-chats"));

-                int maxChats = Integer.parseInt(notifyAgents.getValue("max-chats"));

-                queue.setCurrentChats(currentChats);

-                queue.setMaxChats(maxChats);

-                // Fire event.

-                // TODO: might need another event for current chats and max chats of queue

-                return;

-            }

-        }

-        else if (packet instanceof Message) {

-            Message message = (Message)packet;

-

-            // Check if a room invitation was sent and if the sender is the workgroup

-            MUCUser mucUser = (MUCUser)message.getExtension("x",

-                    "http://jabber.org/protocol/muc#user");

-            MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null;

-            if (invite != null && workgroupJID.equals(invite.getFrom())) {

-                String sessionID = null;

-                Map<String, List<String>> metaData = null;

-

-                SessionID sessionIDExt = (SessionID)message.getExtension(SessionID.ELEMENT_NAME,

-                        SessionID.NAMESPACE);

-                if (sessionIDExt != null) {

-                    sessionID = sessionIDExt.getSessionID();

-                }

-

-                MetaData metaDataExt = (MetaData)message.getExtension(MetaData.ELEMENT_NAME,

-                        MetaData.NAMESPACE);

-                if (metaDataExt != null) {

-                    metaData = metaDataExt.getMetaData();

-                }

-

-                this.fireInvitationEvent(message.getFrom(), sessionID, message.getBody(),

-                        message.getFrom(), metaData);

-            }

-        }

-        else if (packet instanceof OfferRevokeProvider.OfferRevokePacket) {

-            // Acknowledge the IQ set.

-            IQ reply = new IQ() {

-                public String getChildElementXML() {

-                    return null;

-                }

-            };

-            reply.setPacketID(packet.getPacketID());

-            reply.setType(IQ.Type.RESULT);

-            connection.sendPacket(reply);

-

-            fireOfferRevokeEvent((OfferRevokeProvider.OfferRevokePacket)packet);

-        }

-    }

-

-    /**

-     * Creates a ChatNote that will be mapped to the given chat session.

-     *

-     * @param sessionID the session id of a Chat Session.

-     * @param note      the chat note to add.

-     * @throws XMPPException is thrown if an error occurs while adding the note.

-     */

-    public void setNote(String sessionID, String note) throws XMPPException {

-        note = ChatNotes.replace(note, "\n", "\\n");

-        note = StringUtils.escapeForXML(note);

-

-        ChatNotes notes = new ChatNotes();

-        notes.setType(IQ.Type.SET);

-        notes.setTo(workgroupJID);

-        notes.setSessionID(sessionID);

-        notes.setNotes(note);

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(notes.getPacketID()));

-        // Send the request

-        connection.sendPacket(notes);

-

-        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-    }

-

-    /**

-     * Retrieves the ChatNote associated with a given chat session.

-     *

-     * @param sessionID the sessionID of the chat session.

-     * @return the <code>ChatNote</code> associated with a given chat session.

-     * @throws XMPPException if an error occurs while retrieving the ChatNote.

-     */

-    public ChatNotes getNote(String sessionID) throws XMPPException {

-        ChatNotes request = new ChatNotes();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-        request.setSessionID(sessionID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-        ChatNotes response = (ChatNotes)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-

-    }

-

-    /**

-     * Retrieves the AgentChatHistory associated with a particular agent jid.

-     *

-     * @param jid the jid of the agent.

-     * @param maxSessions the max number of sessions to retrieve.

-     * @param startDate the starting date of sessions to retrieve.

-     * @return the chat history associated with a given jid.

-     * @throws XMPPException if an error occurs while retrieving the AgentChatHistory.

-     */

-    public AgentChatHistory getAgentHistory(String jid, int maxSessions, Date startDate) throws XMPPException {

-        AgentChatHistory request;

-        if (startDate != null) {

-            request = new AgentChatHistory(jid, maxSessions, startDate);

-        }

-        else {

-            request = new AgentChatHistory(jid, maxSessions);

-        }

-

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-        AgentChatHistory response = (AgentChatHistory)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Asks the workgroup for it's Search Settings.

-     *

-     * @return SearchSettings the search settings for this workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public SearchSettings getSearchSettings() throws XMPPException {

-        SearchSettings request = new SearchSettings();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        SearchSettings response = (SearchSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Asks the workgroup for it's Global Macros.

-     *

-     * @param global true to retrieve global macros, otherwise false for personal macros.

-     * @return MacroGroup the root macro group.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public MacroGroup getMacros(boolean global) throws XMPPException {

-        Macros request = new Macros();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-        request.setPersonal(!global);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        Macros response = (Macros)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response.getRootGroup();

-    }

-

-    /**

-     * Persists the Personal Macro for an agent.

-     *

-     * @param group the macro group to save. 

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public void saveMacros(MacroGroup group) throws XMPPException {

-        Macros request = new Macros();

-        request.setType(IQ.Type.SET);

-        request.setTo(workgroupJID);

-        request.setPersonal(true);

-        request.setPersonalMacroGroup(group);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-    }

-

-    /**

-     * Query for metadata associated with a session id.

-     *

-     * @param sessionID the sessionID to query for.

-     * @return Map a map of all metadata associated with the sessionID.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public Map<String, List<String>> getChatMetadata(String sessionID) throws XMPPException {

-        ChatMetadata request = new ChatMetadata();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-        request.setSessionID(sessionID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        ChatMetadata response = (ChatMetadata)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response.getMetadata();

-    }

-

-    /**

-     * Invites a user or agent to an existing session support. The provided invitee's JID can be of

-     * a user, an agent, a queue or a workgroup. In the case of a queue or a workgroup the workgroup service

-     * will decide the best agent to receive the invitation.<p>

-     *

-     * This method will return either when the service returned an ACK of the request or if an error occured

-     * while requesting the invitation. After sending the ACK the service will send the invitation to the target

-     * entity. When dealing with agents the common sequence of offer-response will be followed. However, when

-     * sending an invitation to a user a standard MUC invitation will be sent.<p>

-     *

-     * The agent or user that accepted the offer <b>MUST</b> join the room. Failing to do so will make

-     * the invitation to fail. The inviter will eventually receive a message error indicating that the invitee

-     * accepted the offer but failed to join the room.

-     *

-     * Different situations may lead to a failed invitation. Possible cases are: 1) all agents rejected the

-     * offer and ther are no agents available, 2) the agent that accepted the offer failed to join the room or

-     * 2) the user that received the MUC invitation never replied or joined the room. In any of these cases

-     * (or other failing cases) the inviter will get an error message with the failed notification.

-     *

-     * @param type type of entity that will get the invitation.

-     * @param invitee JID of entity that will get the invitation.

-     * @param sessionID ID of the support session that the invitee is being invited.

-     * @param reason the reason of the invitation.

-     * @throws XMPPException if the sender of the invitation is not an agent or the service failed to process

-     *         the request.

-     */

-    public void sendRoomInvitation(RoomInvitation.Type type, String invitee, String sessionID, String reason)

-            throws XMPPException {

-        final RoomInvitation invitation = new RoomInvitation(type, invitee, sessionID, reason);

-        IQ iq = new IQ() {

-

-            public String getChildElementXML() {

-                return invitation.toXML();

-            }

-        };

-        iq.setType(IQ.Type.SET);

-        iq.setTo(workgroupJID);

-        iq.setFrom(connection.getUser());

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(iq.getPacketID()));

-        connection.sendPacket(iq);

-

-        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-    }

-

-    /**

-     * Transfer an existing session support to another user or agent. The provided invitee's JID can be of

-     * a user, an agent, a queue or a workgroup. In the case of a queue or a workgroup the workgroup service

-     * will decide the best agent to receive the invitation.<p>

-     *

-     * This method will return either when the service returned an ACK of the request or if an error occured

-     * while requesting the transfer. After sending the ACK the service will send the invitation to the target

-     * entity. When dealing with agents the common sequence of offer-response will be followed. However, when

-     * sending an invitation to a user a standard MUC invitation will be sent.<p>

-     *

-     * Once the invitee joins the support room the workgroup service will kick the inviter from the room.<p>

-     *

-     * Different situations may lead to a failed transfers. Possible cases are: 1) all agents rejected the

-     * offer and there are no agents available, 2) the agent that accepted the offer failed to join the room

-     * or 2) the user that received the MUC invitation never replied or joined the room. In any of these cases

-     * (or other failing cases) the inviter will get an error message with the failed notification.

-     *

-     * @param type type of entity that will get the invitation.

-     * @param invitee JID of entity that will get the invitation.

-     * @param sessionID ID of the support session that the invitee is being invited.

-     * @param reason the reason of the invitation.

-     * @throws XMPPException if the sender of the invitation is not an agent or the service failed to process

-     *         the request.

-     */

-    public void sendRoomTransfer(RoomTransfer.Type type, String invitee, String sessionID, String reason)

-            throws XMPPException {

-        final RoomTransfer transfer = new RoomTransfer(type, invitee, sessionID, reason);

-        IQ iq = new IQ() {

-

-            public String getChildElementXML() {

-                return transfer.toXML();

-            }

-        };

-        iq.setType(IQ.Type.SET);

-        iq.setTo(workgroupJID);

-        iq.setFrom(connection.getUser());

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(iq.getPacketID()));

-        connection.sendPacket(iq);

-

-        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-    }

-

-    /**

-     * Returns the generic metadata of the workgroup the agent belongs to.

-     *

-     * @param con   the Connection to use.

-     * @param query an optional query object used to tell the server what metadata to retrieve. This can be null.

-     * @throws XMPPException if an error occurs while sending the request to the server.

-     * @return the settings for the workgroup.

-     */

-    public GenericSettings getGenericSettings(Connection con, String query) throws XMPPException {

-        GenericSettings setting = new GenericSettings();

-        setting.setType(IQ.Type.GET);

-        setting.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(setting.getPacketID()));

-        connection.sendPacket(setting);

-

-        GenericSettings response = (GenericSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    public boolean hasMonitorPrivileges(Connection con) throws XMPPException {

-        MonitorPacket request = new MonitorPacket();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-        MonitorPacket response = (MonitorPacket)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response.isMonitor();

-

-    }

-

-    public void makeRoomOwner(Connection con, String sessionID) throws XMPPException {

-        MonitorPacket request = new MonitorPacket();

-        request.setType(IQ.Type.SET);

-        request.setTo(workgroupJID);

-        request.setSessionID(sessionID);

-

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-        Packet response = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-    }

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smackx.workgroup.MetaData;
+import org.jivesoftware.smackx.workgroup.QueueUser;
+import org.jivesoftware.smackx.workgroup.WorkgroupInvitation;
+import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener;
+import org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory;
+import org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata;
+import org.jivesoftware.smackx.workgroup.ext.macros.MacroGroup;
+import org.jivesoftware.smackx.workgroup.ext.macros.Macros;
+import org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes;
+import org.jivesoftware.smackx.workgroup.packet.*;
+import org.jivesoftware.smackx.workgroup.settings.GenericSettings;
+import org.jivesoftware.smackx.workgroup.settings.SearchSettings;
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.ReportedData;
+import org.jivesoftware.smackx.packet.MUCUser;
+
+import java.util.*;
+
+/**
+ * This class embodies the agent's active presence within a given workgroup. The application
+ * should have N instances of this class, where N is the number of workgroups to which the
+ * owning agent of the application belongs. This class provides all functionality that a
+ * session within a given workgroup is expected to have from an agent's perspective -- setting
+ * the status, tracking the status of queues to which the agent belongs within the workgroup, and
+ * dequeuing customers.
+ *
+ * @author Matt Tucker
+ * @author Derek DeMoro
+ */
+public class AgentSession {
+
+    private Connection connection;
+
+    private String workgroupJID;
+
+    private boolean online = false;
+    private Presence.Mode presenceMode;
+    private int maxChats;
+    private final Map<String, List<String>> metaData;
+
+    private Map<String, WorkgroupQueue> queues;
+
+    private final List<OfferListener> offerListeners;
+    private final List<WorkgroupInvitationListener> invitationListeners;
+    private final List<QueueUsersListener> queueUsersListeners;
+
+    private AgentRoster agentRoster = null;
+    private TranscriptManager transcriptManager;
+    private TranscriptSearchManager transcriptSearchManager;
+    private Agent agent;
+    private PacketListener packetListener;
+
+    /**
+     * Constructs a new agent session instance. Note, the {@link #setOnline(boolean)}
+     * method must be called with an argument of <tt>true</tt> to mark the agent
+     * as available to accept chat requests.
+     *
+     * @param connection   a connection instance which must have already gone through
+     *                     authentication.
+     * @param workgroupJID the fully qualified JID of the workgroup.
+     */
+    public AgentSession(String workgroupJID, Connection connection) {
+        // Login must have been done before passing in connection.
+        if (!connection.isAuthenticated()) {
+            throw new IllegalStateException("Must login to server before creating workgroup.");
+        }
+
+        this.workgroupJID = workgroupJID;
+        this.connection = connection;
+        this.transcriptManager = new TranscriptManager(connection);
+        this.transcriptSearchManager = new TranscriptSearchManager(connection);
+
+        this.maxChats = -1;
+
+        this.metaData = new HashMap<String, List<String>>();
+
+        this.queues = new HashMap<String, WorkgroupQueue>();
+
+        offerListeners = new ArrayList<OfferListener>();
+        invitationListeners = new ArrayList<WorkgroupInvitationListener>();
+        queueUsersListeners = new ArrayList<QueueUsersListener>();
+
+        // Create a filter to listen for packets we're interested in.
+        OrFilter filter = new OrFilter();
+        filter.addFilter(new PacketTypeFilter(OfferRequestProvider.OfferRequestPacket.class));
+        filter.addFilter(new PacketTypeFilter(OfferRevokeProvider.OfferRevokePacket.class));
+        filter.addFilter(new PacketTypeFilter(Presence.class));
+        filter.addFilter(new PacketTypeFilter(Message.class));
+
+        packetListener = new PacketListener() {
+            public void processPacket(Packet packet) {
+                try {
+                    handlePacket(packet);
+                }
+                catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+        connection.addPacketListener(packetListener, filter);
+        // Create the agent associated to this session
+        agent = new Agent(connection, workgroupJID);
+    }
+
+    /**
+     * Close the agent session. The underlying connection will remain opened but the
+     * packet listeners that were added by this agent session will be removed.
+     */
+    public void close() {
+        connection.removePacketListener(packetListener);
+    }
+
+    /**
+     * Returns the agent roster for the workgroup, which contains
+     *
+     * @return the AgentRoster
+     */
+    public AgentRoster getAgentRoster() {
+        if (agentRoster == null) {
+            agentRoster = new AgentRoster(connection, workgroupJID);
+        }
+
+        // This might be the first time the user has asked for the roster. If so, we
+        // want to wait up to 2 seconds for the server to send back the list of agents.
+        // This behavior shields API users from having to worry about the fact that the
+        // operation is asynchronous, although they'll still have to listen for changes
+        // to the roster.
+        int elapsed = 0;
+        while (!agentRoster.rosterInitialized && elapsed <= 2000) {
+            try {
+                Thread.sleep(500);
+            }
+            catch (Exception e) {
+                // Ignore
+            }
+            elapsed += 500;
+        }
+        return agentRoster;
+    }
+
+    /**
+     * Returns the agent's current presence mode.
+     *
+     * @return the agent's current presence mode.
+     */
+    public Presence.Mode getPresenceMode() {
+        return presenceMode;
+    }
+
+    /**
+     * Returns the maximum number of chats the agent can participate in.
+     *
+     * @return the maximum number of chats the agent can participate in.
+     */
+    public int getMaxChats() {
+        return maxChats;
+    }
+
+    /**
+     * Returns true if the agent is online with the workgroup.
+     *
+     * @return true if the agent is online with the workgroup.
+     */
+    public boolean isOnline() {
+        return online;
+    }
+
+    /**
+     * Allows the addition of a new key-value pair to the agent's meta data, if the value is
+     * new data, the revised meta data will be rebroadcast in an agent's presence broadcast.
+     *
+     * @param key the meta data key
+     * @param val the non-null meta data value
+     * @throws XMPPException if an exception occurs.
+     */
+    public void setMetaData(String key, String val) throws XMPPException {
+        synchronized (this.metaData) {
+            List<String> oldVals = metaData.get(key);
+
+            if ((oldVals == null) || (!oldVals.get(0).equals(val))) {
+                oldVals.set(0, val);
+
+                setStatus(presenceMode, maxChats);
+            }
+        }
+    }
+
+    /**
+     * Allows the removal of data from the agent's meta data, if the key represents existing data,
+     * the revised meta data will be rebroadcast in an agent's presence broadcast.
+     *
+     * @param key the meta data key.
+     * @throws XMPPException if an exception occurs.
+     */
+    public void removeMetaData(String key) throws XMPPException {
+        synchronized (this.metaData) {
+            List<String> oldVal = metaData.remove(key);
+
+            if (oldVal != null) {
+                setStatus(presenceMode, maxChats);
+            }
+        }
+    }
+
+    /**
+     * Allows the retrieval of meta data for a specified key.
+     *
+     * @param key the meta data key
+     * @return the meta data value associated with the key or <tt>null</tt> if the meta-data
+     *         doesn't exist..
+     */
+    public List<String> getMetaData(String key) {
+        return metaData.get(key);
+    }
+
+    /**
+     * Sets whether the agent is online with the workgroup. If the user tries to go online with
+     * the workgroup but is not allowed to be an agent, an XMPPError with error code 401 will
+     * be thrown.
+     *
+     * @param online true to set the agent as online with the workgroup.
+     * @throws XMPPException if an error occurs setting the online status.
+     */
+    public void setOnline(boolean online) throws XMPPException {
+        // If the online status hasn't changed, do nothing.
+        if (this.online == online) {
+            return;
+        }
+
+        Presence presence;
+
+        // If the user is going online...
+        if (online) {
+            presence = new Presence(Presence.Type.available);
+            presence.setTo(workgroupJID);
+            presence.addExtension(new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,
+                    AgentStatus.NAMESPACE));
+
+            PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupJID)));
+
+            connection.sendPacket(presence);
+
+            presence = (Presence)collector.nextResult(5000);
+            collector.cancel();
+            if (!presence.isAvailable()) {
+                throw new XMPPException("No response from server on status set.");
+            }
+
+            if (presence.getError() != null) {
+                throw new XMPPException(presence.getError());
+            }
+
+            // We can safely update this iv since we didn't get any error
+            this.online = online;
+        }
+        // Otherwise the user is going offline...
+        else {
+            // Update this iv now since we don't care at this point of any error
+            this.online = online;
+
+            presence = new Presence(Presence.Type.unavailable);
+            presence.setTo(workgroupJID);
+            presence.addExtension(new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,
+                    AgentStatus.NAMESPACE));
+            connection.sendPacket(presence);
+        }
+    }
+
+    /**
+     * Sets the agent's current status with the workgroup. The presence mode affects
+     * how offers are routed to the agent. The possible presence modes with their
+     * meanings are as follows:<ul>
+     * <p/>
+     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats
+     * (equivalent to Presence.Mode.CHAT).
+     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.
+     * However, special case, or extreme urgency chats may still be offered to the agent.
+     * <li>Presence.Mode.AWAY -- the agent is not available and should not
+     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>
+     * <p/>
+     * The max chats value is the maximum number of chats the agent is willing to have
+     * routed to them at once. Some servers may be configured to only accept max chat
+     * values in a certain range; for example, between two and five. In that case, the
+     * maxChats value the agent sends may be adjusted by the server to a value within that
+     * range.
+     *
+     * @param presenceMode the presence mode of the agent.
+     * @param maxChats     the maximum number of chats the agent is willing to accept.
+     * @throws XMPPException         if an error occurs setting the agent status.
+     * @throws IllegalStateException if the agent is not online with the workgroup.
+     */
+    public void setStatus(Presence.Mode presenceMode, int maxChats) throws XMPPException {
+        setStatus(presenceMode, maxChats, null);
+    }
+
+    /**
+     * Sets the agent's current status with the workgroup. The presence mode affects how offers
+     * are routed to the agent. The possible presence modes with their meanings are as follows:<ul>
+     * <p/>
+     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats
+     * (equivalent to Presence.Mode.CHAT).
+     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.
+     * However, special case, or extreme urgency chats may still be offered to the agent.
+     * <li>Presence.Mode.AWAY -- the agent is not available and should not
+     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>
+     * <p/>
+     * The max chats value is the maximum number of chats the agent is willing to have routed to
+     * them at once. Some servers may be configured to only accept max chat values in a certain
+     * range; for example, between two and five. In that case, the maxChats value the agent sends
+     * may be adjusted by the server to a value within that range.
+     *
+     * @param presenceMode the presence mode of the agent.
+     * @param maxChats     the maximum number of chats the agent is willing to accept.
+     * @param status       sets the status message of the presence update.
+     * @throws XMPPException         if an error occurs setting the agent status.
+     * @throws IllegalStateException if the agent is not online with the workgroup.
+     */
+    public void setStatus(Presence.Mode presenceMode, int maxChats, String status)
+            throws XMPPException {
+        if (!online) {
+            throw new IllegalStateException("Cannot set status when the agent is not online.");
+        }
+
+        if (presenceMode == null) {
+            presenceMode = Presence.Mode.available;
+        }
+        this.presenceMode = presenceMode;
+        this.maxChats = maxChats;
+
+        Presence presence = new Presence(Presence.Type.available);
+        presence.setMode(presenceMode);
+        presence.setTo(this.getWorkgroupJID());
+
+        if (status != null) {
+            presence.setStatus(status);
+        }
+        // Send information about max chats and current chats as a packet extension.
+        DefaultPacketExtension agentStatus = new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,
+                AgentStatus.NAMESPACE);
+        agentStatus.setValue("max-chats", "" + maxChats);
+        presence.addExtension(agentStatus);
+        presence.addExtension(new MetaData(this.metaData));
+
+        PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupJID)));
+
+        this.connection.sendPacket(presence);
+
+        presence = (Presence)collector.nextResult(5000);
+        collector.cancel();
+        if (!presence.isAvailable()) {
+            throw new XMPPException("No response from server on status set.");
+        }
+
+        if (presence.getError() != null) {
+            throw new XMPPException(presence.getError());
+        }
+    }
+
+    /**
+     * Sets the agent's current status with the workgroup. The presence mode affects how offers
+     * are routed to the agent. The possible presence modes with their meanings are as follows:<ul>
+     * <p/>
+     * <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats
+     * (equivalent to Presence.Mode.CHAT).
+     * <li>Presence.Mode.DO_NOT_DISTURB -- the agent is busy and should not be disturbed.
+     * However, special case, or extreme urgency chats may still be offered to the agent.
+     * <li>Presence.Mode.AWAY -- the agent is not available and should not
+     * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>
+     *
+     * @param presenceMode the presence mode of the agent.
+     * @param status       sets the status message of the presence update.
+     * @throws XMPPException         if an error occurs setting the agent status.
+     * @throws IllegalStateException if the agent is not online with the workgroup.
+     */
+    public void setStatus(Presence.Mode presenceMode, String status) throws XMPPException {
+        if (!online) {
+            throw new IllegalStateException("Cannot set status when the agent is not online.");
+        }
+
+        if (presenceMode == null) {
+            presenceMode = Presence.Mode.available;
+        }
+        this.presenceMode = presenceMode;
+
+        Presence presence = new Presence(Presence.Type.available);
+        presence.setMode(presenceMode);
+        presence.setTo(this.getWorkgroupJID());
+
+        if (status != null) {
+            presence.setStatus(status);
+        }
+        presence.addExtension(new MetaData(this.metaData));
+
+        PacketCollector collector = this.connection.createPacketCollector(new AndFilter(new PacketTypeFilter(Presence.class),
+                new FromContainsFilter(workgroupJID)));
+
+        this.connection.sendPacket(presence);
+
+        presence = (Presence)collector.nextResult(5000);
+        collector.cancel();
+        if (!presence.isAvailable()) {
+            throw new XMPPException("No response from server on status set.");
+        }
+
+        if (presence.getError() != null) {
+            throw new XMPPException(presence.getError());
+        }
+    }
+
+    /**
+     * Removes a user from the workgroup queue. This is an administrative action that the
+     * <p/>
+     * The agent is not guaranteed of having privileges to perform this action; an exception
+     * denying the request may be thrown.
+     *
+     * @param userID the ID of the user to remove.
+     * @throws XMPPException if an exception occurs.
+     */
+    public void dequeueUser(String userID) throws XMPPException {
+        // todo: this method simply won't work right now.
+        DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID);
+
+        // PENDING
+        this.connection.sendPacket(departPacket);
+    }
+
+    /**
+     * Returns the transcripts of a given user. The answer will contain the complete history of
+     * conversations that a user had.
+     *
+     * @param userID the id of the user to get his conversations.
+     * @return the transcripts of a given user.
+     * @throws XMPPException if an error occurs while getting the information.
+     */
+    public Transcripts getTranscripts(String userID) throws XMPPException {
+        return transcriptManager.getTranscripts(workgroupJID, userID);
+    }
+
+    /**
+     * Returns the full conversation transcript of a given session.
+     *
+     * @param sessionID the id of the session to get the full transcript.
+     * @return the full conversation transcript of a given session.
+     * @throws XMPPException if an error occurs while getting the information.
+     */
+    public Transcript getTranscript(String sessionID) throws XMPPException {
+        return transcriptManager.getTranscript(workgroupJID, sessionID);
+    }
+
+    /**
+     * Returns the Form to use for searching transcripts. It is unlikely that the server
+     * will change the form (without a restart) so it is safe to keep the returned form
+     * for future submissions.
+     *
+     * @return the Form to use for searching transcripts.
+     * @throws XMPPException if an error occurs while sending the request to the server.
+     */
+    public Form getTranscriptSearchForm() throws XMPPException {
+        return transcriptSearchManager.getSearchForm(StringUtils.parseServer(workgroupJID));
+    }
+
+    /**
+     * Submits the completed form and returns the result of the transcript search. The result
+     * will include all the data returned from the server so be careful with the amount of
+     * data that the search may return.
+     *
+     * @param completedForm the filled out search form.
+     * @return the result of the transcript search.
+     * @throws XMPPException if an error occurs while submiting the search to the server.
+     */
+    public ReportedData searchTranscripts(Form completedForm) throws XMPPException {
+        return transcriptSearchManager.submitSearch(StringUtils.parseServer(workgroupJID),
+                completedForm);
+    }
+
+    /**
+     * Asks the workgroup for information about the occupants of the specified room. The returned
+     * information will include the real JID of the occupants, the nickname of the user in the
+     * room as well as the date when the user joined the room.
+     *
+     * @param roomID the room to get information about its occupants.
+     * @return information about the occupants of the specified room.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public OccupantsInfo getOccupantsInfo(String roomID) throws XMPPException {
+        OccupantsInfo request = new OccupantsInfo(roomID);
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+        OccupantsInfo response = (OccupantsInfo)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * @return the fully-qualified name of the workgroup for which this session exists
+     */
+    public String getWorkgroupJID() {
+        return workgroupJID;
+    }
+
+    /**
+     * Returns the Agent associated to this session.
+     *
+     * @return the Agent associated to this session.
+     */
+    public Agent getAgent() {
+        return agent;
+    }
+
+    /**
+     * @param queueName the name of the queue
+     * @return an instance of WorkgroupQueue for the argument queue name, or null if none exists
+     */
+    public WorkgroupQueue getQueue(String queueName) {
+        return queues.get(queueName);
+    }
+
+    public Iterator<WorkgroupQueue> getQueues() {
+        return Collections.unmodifiableMap((new HashMap<String, WorkgroupQueue>(queues))).values().iterator();
+    }
+
+    public void addQueueUsersListener(QueueUsersListener listener) {
+        synchronized (queueUsersListeners) {
+            if (!queueUsersListeners.contains(listener)) {
+                queueUsersListeners.add(listener);
+            }
+        }
+    }
+
+    public void removeQueueUsersListener(QueueUsersListener listener) {
+        synchronized (queueUsersListeners) {
+            queueUsersListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Adds an offer listener.
+     *
+     * @param offerListener the offer listener.
+     */
+    public void addOfferListener(OfferListener offerListener) {
+        synchronized (offerListeners) {
+            if (!offerListeners.contains(offerListener)) {
+                offerListeners.add(offerListener);
+            }
+        }
+    }
+
+    /**
+     * Removes an offer listener.
+     *
+     * @param offerListener the offer listener.
+     */
+    public void removeOfferListener(OfferListener offerListener) {
+        synchronized (offerListeners) {
+            offerListeners.remove(offerListener);
+        }
+    }
+
+    /**
+     * Adds an invitation listener.
+     *
+     * @param invitationListener the invitation listener.
+     */
+    public void addInvitationListener(WorkgroupInvitationListener invitationListener) {
+        synchronized (invitationListeners) {
+            if (!invitationListeners.contains(invitationListener)) {
+                invitationListeners.add(invitationListener);
+            }
+        }
+    }
+
+    /**
+     * Removes an invitation listener.
+     *
+     * @param invitationListener the invitation listener.
+     */
+    public void removeInvitationListener(WorkgroupInvitationListener invitationListener) {
+        synchronized (invitationListeners) {
+            invitationListeners.remove(invitationListener);
+        }
+    }
+
+    private void fireOfferRequestEvent(OfferRequestProvider.OfferRequestPacket requestPacket) {
+        Offer offer = new Offer(this.connection, this, requestPacket.getUserID(),
+                requestPacket.getUserJID(), this.getWorkgroupJID(),
+                new Date((new Date()).getTime() + (requestPacket.getTimeout() * 1000)),
+                requestPacket.getSessionID(), requestPacket.getMetaData(), requestPacket.getContent());
+
+        synchronized (offerListeners) {
+            for (OfferListener listener : offerListeners) {
+                listener.offerReceived(offer);
+            }
+        }
+    }
+
+    private void fireOfferRevokeEvent(OfferRevokeProvider.OfferRevokePacket orp) {
+        RevokedOffer revokedOffer = new RevokedOffer(orp.getUserJID(), orp.getUserID(),
+                this.getWorkgroupJID(), orp.getSessionID(), orp.getReason(), new Date());
+
+        synchronized (offerListeners) {
+            for (OfferListener listener : offerListeners) {
+                listener.offerRevoked(revokedOffer);
+            }
+        }
+    }
+
+    private void fireInvitationEvent(String groupChatJID, String sessionID, String body,
+                                     String from, Map<String, List<String>> metaData) {
+        WorkgroupInvitation invitation = new WorkgroupInvitation(connection.getUser(), groupChatJID,
+                workgroupJID, sessionID, body, from, metaData);
+
+        synchronized (invitationListeners) {
+            for (WorkgroupInvitationListener listener : invitationListeners) {
+                listener.invitationReceived(invitation);
+            }
+        }
+    }
+
+    private void fireQueueUsersEvent(WorkgroupQueue queue, WorkgroupQueue.Status status,
+                                     int averageWaitTime, Date oldestEntry, Set<QueueUser> users) {
+        synchronized (queueUsersListeners) {
+            for (QueueUsersListener listener : queueUsersListeners) {
+                if (status != null) {
+                    listener.statusUpdated(queue, status);
+                }
+                if (averageWaitTime != -1) {
+                    listener.averageWaitTimeUpdated(queue, averageWaitTime);
+                }
+                if (oldestEntry != null) {
+                    listener.oldestEntryUpdated(queue, oldestEntry);
+                }
+                if (users != null) {
+                    listener.usersUpdated(queue, users);
+                }
+            }
+        }
+    }
+
+    // PacketListener Implementation.
+
+    private void handlePacket(Packet packet) {
+        if (packet instanceof OfferRequestProvider.OfferRequestPacket) {
+            // Acknowledge the IQ set.
+            IQ reply = new IQ() {
+                public String getChildElementXML() {
+                    return null;
+                }
+            };
+            reply.setPacketID(packet.getPacketID());
+            reply.setTo(packet.getFrom());
+            reply.setType(IQ.Type.RESULT);
+            connection.sendPacket(reply);
+
+            fireOfferRequestEvent((OfferRequestProvider.OfferRequestPacket)packet);
+        }
+        else if (packet instanceof Presence) {
+            Presence presence = (Presence)packet;
+
+            // The workgroup can send us a number of different presence packets. We
+            // check for different packet extensions to see what type of presence
+            // packet it is.
+
+            String queueName = StringUtils.parseResource(presence.getFrom());
+            WorkgroupQueue queue = queues.get(queueName);
+            // If there isn't already an entry for the queue, create a new one.
+            if (queue == null) {
+                queue = new WorkgroupQueue(queueName);
+                queues.put(queueName, queue);
+            }
+
+            // QueueOverview packet extensions contain basic information about a queue.
+            QueueOverview queueOverview = (QueueOverview)presence.getExtension(QueueOverview.ELEMENT_NAME, QueueOverview.NAMESPACE);
+            if (queueOverview != null) {
+                if (queueOverview.getStatus() == null) {
+                    queue.setStatus(WorkgroupQueue.Status.CLOSED);
+                }
+                else {
+                    queue.setStatus(queueOverview.getStatus());
+                }
+                queue.setAverageWaitTime(queueOverview.getAverageWaitTime());
+                queue.setOldestEntry(queueOverview.getOldestEntry());
+                // Fire event.
+                fireQueueUsersEvent(queue, queueOverview.getStatus(),
+                        queueOverview.getAverageWaitTime(), queueOverview.getOldestEntry(),
+                        null);
+                return;
+            }
+
+            // QueueDetails packet extensions contain information about the users in
+            // a queue.
+            QueueDetails queueDetails = (QueueDetails)packet.getExtension(QueueDetails.ELEMENT_NAME, QueueDetails.NAMESPACE);
+            if (queueDetails != null) {
+                queue.setUsers(queueDetails.getUsers());
+                // Fire event.
+                fireQueueUsersEvent(queue, null, -1, null, queueDetails.getUsers());
+                return;
+            }
+
+            // Notify agent packets gives an overview of agent activity in a queue.
+            DefaultPacketExtension notifyAgents = (DefaultPacketExtension)presence.getExtension("notify-agents", "http://jabber.org/protocol/workgroup");
+            if (notifyAgents != null) {
+                int currentChats = Integer.parseInt(notifyAgents.getValue("current-chats"));
+                int maxChats = Integer.parseInt(notifyAgents.getValue("max-chats"));
+                queue.setCurrentChats(currentChats);
+                queue.setMaxChats(maxChats);
+                // Fire event.
+                // TODO: might need another event for current chats and max chats of queue
+                return;
+            }
+        }
+        else if (packet instanceof Message) {
+            Message message = (Message)packet;
+
+            // Check if a room invitation was sent and if the sender is the workgroup
+            MUCUser mucUser = (MUCUser)message.getExtension("x",
+                    "http://jabber.org/protocol/muc#user");
+            MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null;
+            if (invite != null && workgroupJID.equals(invite.getFrom())) {
+                String sessionID = null;
+                Map<String, List<String>> metaData = null;
+
+                SessionID sessionIDExt = (SessionID)message.getExtension(SessionID.ELEMENT_NAME,
+                        SessionID.NAMESPACE);
+                if (sessionIDExt != null) {
+                    sessionID = sessionIDExt.getSessionID();
+                }
+
+                MetaData metaDataExt = (MetaData)message.getExtension(MetaData.ELEMENT_NAME,
+                        MetaData.NAMESPACE);
+                if (metaDataExt != null) {
+                    metaData = metaDataExt.getMetaData();
+                }
+
+                this.fireInvitationEvent(message.getFrom(), sessionID, message.getBody(),
+                        message.getFrom(), metaData);
+            }
+        }
+        else if (packet instanceof OfferRevokeProvider.OfferRevokePacket) {
+            // Acknowledge the IQ set.
+            IQ reply = new IQ() {
+                public String getChildElementXML() {
+                    return null;
+                }
+            };
+            reply.setPacketID(packet.getPacketID());
+            reply.setType(IQ.Type.RESULT);
+            connection.sendPacket(reply);
+
+            fireOfferRevokeEvent((OfferRevokeProvider.OfferRevokePacket)packet);
+        }
+    }
+
+    /**
+     * Creates a ChatNote that will be mapped to the given chat session.
+     *
+     * @param sessionID the session id of a Chat Session.
+     * @param note      the chat note to add.
+     * @throws XMPPException is thrown if an error occurs while adding the note.
+     */
+    public void setNote(String sessionID, String note) throws XMPPException {
+        note = ChatNotes.replace(note, "\n", "\\n");
+        note = StringUtils.escapeForXML(note);
+
+        ChatNotes notes = new ChatNotes();
+        notes.setType(IQ.Type.SET);
+        notes.setTo(workgroupJID);
+        notes.setSessionID(sessionID);
+        notes.setNotes(note);
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(notes.getPacketID()));
+        // Send the request
+        connection.sendPacket(notes);
+
+        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+    }
+
+    /**
+     * Retrieves the ChatNote associated with a given chat session.
+     *
+     * @param sessionID the sessionID of the chat session.
+     * @return the <code>ChatNote</code> associated with a given chat session.
+     * @throws XMPPException if an error occurs while retrieving the ChatNote.
+     */
+    public ChatNotes getNote(String sessionID) throws XMPPException {
+        ChatNotes request = new ChatNotes();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+        request.setSessionID(sessionID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+        ChatNotes response = (ChatNotes)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+
+    }
+
+    /**
+     * Retrieves the AgentChatHistory associated with a particular agent jid.
+     *
+     * @param jid the jid of the agent.
+     * @param maxSessions the max number of sessions to retrieve.
+     * @param startDate the starting date of sessions to retrieve.
+     * @return the chat history associated with a given jid.
+     * @throws XMPPException if an error occurs while retrieving the AgentChatHistory.
+     */
+    public AgentChatHistory getAgentHistory(String jid, int maxSessions, Date startDate) throws XMPPException {
+        AgentChatHistory request;
+        if (startDate != null) {
+            request = new AgentChatHistory(jid, maxSessions, startDate);
+        }
+        else {
+            request = new AgentChatHistory(jid, maxSessions);
+        }
+
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+        AgentChatHistory response = (AgentChatHistory)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Asks the workgroup for it's Search Settings.
+     *
+     * @return SearchSettings the search settings for this workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public SearchSettings getSearchSettings() throws XMPPException {
+        SearchSettings request = new SearchSettings();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        SearchSettings response = (SearchSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Asks the workgroup for it's Global Macros.
+     *
+     * @param global true to retrieve global macros, otherwise false for personal macros.
+     * @return MacroGroup the root macro group.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public MacroGroup getMacros(boolean global) throws XMPPException {
+        Macros request = new Macros();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+        request.setPersonal(!global);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        Macros response = (Macros)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response.getRootGroup();
+    }
+
+    /**
+     * Persists the Personal Macro for an agent.
+     *
+     * @param group the macro group to save. 
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public void saveMacros(MacroGroup group) throws XMPPException {
+        Macros request = new Macros();
+        request.setType(IQ.Type.SET);
+        request.setTo(workgroupJID);
+        request.setPersonal(true);
+        request.setPersonalMacroGroup(group);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+    }
+
+    /**
+     * Query for metadata associated with a session id.
+     *
+     * @param sessionID the sessionID to query for.
+     * @return Map a map of all metadata associated with the sessionID.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public Map<String, List<String>> getChatMetadata(String sessionID) throws XMPPException {
+        ChatMetadata request = new ChatMetadata();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+        request.setSessionID(sessionID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        ChatMetadata response = (ChatMetadata)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response.getMetadata();
+    }
+
+    /**
+     * Invites a user or agent to an existing session support. The provided invitee's JID can be of
+     * a user, an agent, a queue or a workgroup. In the case of a queue or a workgroup the workgroup service
+     * will decide the best agent to receive the invitation.<p>
+     *
+     * This method will return either when the service returned an ACK of the request or if an error occured
+     * while requesting the invitation. After sending the ACK the service will send the invitation to the target
+     * entity. When dealing with agents the common sequence of offer-response will be followed. However, when
+     * sending an invitation to a user a standard MUC invitation will be sent.<p>
+     *
+     * The agent or user that accepted the offer <b>MUST</b> join the room. Failing to do so will make
+     * the invitation to fail. The inviter will eventually receive a message error indicating that the invitee
+     * accepted the offer but failed to join the room.
+     *
+     * Different situations may lead to a failed invitation. Possible cases are: 1) all agents rejected the
+     * offer and ther are no agents available, 2) the agent that accepted the offer failed to join the room or
+     * 2) the user that received the MUC invitation never replied or joined the room. In any of these cases
+     * (or other failing cases) the inviter will get an error message with the failed notification.
+     *
+     * @param type type of entity that will get the invitation.
+     * @param invitee JID of entity that will get the invitation.
+     * @param sessionID ID of the support session that the invitee is being invited.
+     * @param reason the reason of the invitation.
+     * @throws XMPPException if the sender of the invitation is not an agent or the service failed to process
+     *         the request.
+     */
+    public void sendRoomInvitation(RoomInvitation.Type type, String invitee, String sessionID, String reason)
+            throws XMPPException {
+        final RoomInvitation invitation = new RoomInvitation(type, invitee, sessionID, reason);
+        IQ iq = new IQ() {
+
+            public String getChildElementXML() {
+                return invitation.toXML();
+            }
+        };
+        iq.setType(IQ.Type.SET);
+        iq.setTo(workgroupJID);
+        iq.setFrom(connection.getUser());
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(iq.getPacketID()));
+        connection.sendPacket(iq);
+
+        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+    }
+
+    /**
+     * Transfer an existing session support to another user or agent. The provided invitee's JID can be of
+     * a user, an agent, a queue or a workgroup. In the case of a queue or a workgroup the workgroup service
+     * will decide the best agent to receive the invitation.<p>
+     *
+     * This method will return either when the service returned an ACK of the request or if an error occured
+     * while requesting the transfer. After sending the ACK the service will send the invitation to the target
+     * entity. When dealing with agents the common sequence of offer-response will be followed. However, when
+     * sending an invitation to a user a standard MUC invitation will be sent.<p>
+     *
+     * Once the invitee joins the support room the workgroup service will kick the inviter from the room.<p>
+     *
+     * Different situations may lead to a failed transfers. Possible cases are: 1) all agents rejected the
+     * offer and there are no agents available, 2) the agent that accepted the offer failed to join the room
+     * or 2) the user that received the MUC invitation never replied or joined the room. In any of these cases
+     * (or other failing cases) the inviter will get an error message with the failed notification.
+     *
+     * @param type type of entity that will get the invitation.
+     * @param invitee JID of entity that will get the invitation.
+     * @param sessionID ID of the support session that the invitee is being invited.
+     * @param reason the reason of the invitation.
+     * @throws XMPPException if the sender of the invitation is not an agent or the service failed to process
+     *         the request.
+     */
+    public void sendRoomTransfer(RoomTransfer.Type type, String invitee, String sessionID, String reason)
+            throws XMPPException {
+        final RoomTransfer transfer = new RoomTransfer(type, invitee, sessionID, reason);
+        IQ iq = new IQ() {
+
+            public String getChildElementXML() {
+                return transfer.toXML();
+            }
+        };
+        iq.setType(IQ.Type.SET);
+        iq.setTo(workgroupJID);
+        iq.setFrom(connection.getUser());
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(iq.getPacketID()));
+        connection.sendPacket(iq);
+
+        IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+    }
+
+    /**
+     * Returns the generic metadata of the workgroup the agent belongs to.
+     *
+     * @param con   the Connection to use.
+     * @param query an optional query object used to tell the server what metadata to retrieve. This can be null.
+     * @throws XMPPException if an error occurs while sending the request to the server.
+     * @return the settings for the workgroup.
+     */
+    public GenericSettings getGenericSettings(Connection con, String query) throws XMPPException {
+        GenericSettings setting = new GenericSettings();
+        setting.setType(IQ.Type.GET);
+        setting.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(setting.getPacketID()));
+        connection.sendPacket(setting);
+
+        GenericSettings response = (GenericSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    public boolean hasMonitorPrivileges(Connection con) throws XMPPException {
+        MonitorPacket request = new MonitorPacket();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+        MonitorPacket response = (MonitorPacket)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response.isMonitor();
+
+    }
+
+    public void makeRoomOwner(Connection con, String sessionID) throws XMPPException {
+        MonitorPacket request = new MonitorPacket();
+        request.setType(IQ.Type.SET);
+        request.setTo(workgroupJID);
+        request.setSessionID(sessionID);
+
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+        Packet response = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java
index 16b324a..33629be 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java
@@ -1,62 +1,62 @@
-/**

- * $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.smackx.workgroup.agent;

-

-/**

- * Request sent by an agent to invite another agent or user.

- *

- * @author Gaston Dombiak

- */

-public class InvitationRequest extends OfferContent {

-

-    private String inviter;

-    private String room;

-    private String reason;

-

-    public InvitationRequest(String inviter, String room, String reason) {

-        this.inviter = inviter;

-        this.room = room;

-        this.reason = reason;

-    }

-

-    public String getInviter() {

-        return inviter;

-    }

-

-    public String getRoom() {

-        return room;

-    }

-

-    public String getReason() {

-        return reason;

-    }

-

-    boolean isUserRequest() {

-        return false;

-    }

-

-    boolean isInvitation() {

-        return true;

-    }

-

-    boolean isTransfer() {

-        return false;

-    }

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+/**
+ * Request sent by an agent to invite another agent or user.
+ *
+ * @author Gaston Dombiak
+ */
+public class InvitationRequest extends OfferContent {
+
+    private String inviter;
+    private String room;
+    private String reason;
+
+    public InvitationRequest(String inviter, String room, String reason) {
+        this.inviter = inviter;
+        this.room = room;
+        this.reason = reason;
+    }
+
+    public String getInviter() {
+        return inviter;
+    }
+
+    public String getRoom() {
+        return room;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    boolean isUserRequest() {
+        return false;
+    }
+
+    boolean isInvitation() {
+        return true;
+    }
+
+    boolean isTransfer() {
+        return false;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Offer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Offer.java
index ece8c6f..98070c6 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Offer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/Offer.java
@@ -1,223 +1,223 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-

-import java.util.Date;

-import java.util.List;

-import java.util.Map;

-

-/**

- * A class embodying the semantic agent chat offer; specific instances allow the acceptance or

- * rejecting of the offer.<br>

- *

- * @author Matt Tucker

- * @author loki der quaeler

- * @author Derek DeMoro

- */

-public class Offer {

-

-    private Connection connection;

-    private AgentSession session;

-

-    private String sessionID;

-    private String userJID;

-    private String userID;

-    private String workgroupName;

-    private Date expiresDate;

-    private Map<String, List<String>> metaData;

-    private OfferContent content;

-

-    private boolean accepted = false;

-    private boolean rejected = false;

-

-    /**

-     * Creates a new offer.

-     *

-     * @param conn the XMPP connection with which the issuing session was created.

-     * @param agentSession the agent session instance through which this offer was issued.

-     * @param userID  the userID of the user from which the offer originates.

-     * @param userJID the XMPP address of the user from which the offer originates.

-     * @param workgroupName the fully qualified name of the workgroup.

-     * @param expiresDate the date at which this offer expires.

-     * @param sessionID the session id associated with the offer.

-     * @param metaData the metadata associated with the offer.

-     * @param content content of the offer. The content explains the reason for the offer

-     *        (e.g. user request, transfer)

-     */

-    Offer(Connection conn, AgentSession agentSession, String userID,

-            String userJID, String workgroupName, Date expiresDate,

-            String sessionID, Map<String, List<String>> metaData, OfferContent content)

-    {

-        this.connection = conn;

-        this.session = agentSession;

-        this.userID = userID;

-        this.userJID = userJID;

-        this.workgroupName = workgroupName;

-        this.expiresDate = expiresDate;

-        this.sessionID = sessionID;

-        this.metaData = metaData;

-        this.content = content;

-    }

-

-    /**

-     * Accepts the offer.

-     */

-    public void accept() {

-        Packet acceptPacket = new AcceptPacket(this.session.getWorkgroupJID());

-        connection.sendPacket(acceptPacket);

-        // TODO: listen for a reply.

-        accepted = true;

-    }

-

-    /**

-     * Rejects the offer.

-     */

-    public void reject() {

-        RejectPacket rejectPacket = new RejectPacket(this.session.getWorkgroupJID());

-        connection.sendPacket(rejectPacket);

-        // TODO: listen for a reply.

-        rejected = true;

-    }

-

-    /**

-     * Returns the userID that the offer originates from. In most cases, the

-     * userID will simply be the JID of the requesting user. However, users can

-     * also manually specify a userID for their request. In that case, that value will

-     * be returned.

-     *

-     * @return the userID of the user from which the offer originates.

-     */

-    public String getUserID() {

-        return userID;

-    }

-

-    /**

-     * Returns the JID of the user that made the offer request.

-     *

-     * @return the user's JID.

-     */

-    public String getUserJID() {

-        return userJID;

-    }

-

-    /**

-     * The fully qualified name of the workgroup (eg support@example.com).

-     *

-     * @return the name of the workgroup.

-     */

-    public String getWorkgroupName() {

-        return this.workgroupName;

-    }

-

-    /**

-     * The date when the offer will expire. The agent must {@link #accept()}

-     * the offer before the expiration date or the offer will lapse and be

-     * routed to another agent. Alternatively, the agent can {@link #reject()}

-     * the offer at any time if they don't wish to accept it..

-     *

-     * @return the date at which this offer expires.

-     */

-    public Date getExpiresDate() {

-        return this.expiresDate;

-    }

-

-    /**

-     * The session ID associated with the offer.

-     *

-     * @return the session id associated with the offer.

-     */

-    public String getSessionID() {

-        return this.sessionID;

-    }

-

-    /**

-     * The meta-data associated with the offer.

-     *

-     * @return the offer meta-data.

-     */

-    public Map<String, List<String>> getMetaData() {

-        return this.metaData;

-    }

-

-    /**

-     * Returns the content of the offer. The content explains the reason for the offer

-     * (e.g. user request, transfer)

-     *

-     * @return the content of the offer.

-     */

-    public OfferContent getContent() {

-        return content;

-    }

-

-    /**

-     * Returns true if the agent accepted this offer.

-     *

-     * @return true if the agent accepted this offer.

-     */

-    public boolean isAccepted() {

-        return accepted;

-    }

-

-    /**

-     * Return true if the agent rejected this offer.

-     *

-     * @return true if the agent rejected this offer.

-     */

-    public boolean isRejected() {

-        return rejected;

-    }

-

-    /**

-     * Packet for rejecting offers.

-     */

-    private class RejectPacket extends IQ {

-

-        RejectPacket(String workgroup) {

-            this.setTo(workgroup);

-            this.setType(IQ.Type.SET);

-        }

-

-        public String getChildElementXML() {

-            return "<offer-reject id=\"" + Offer.this.getSessionID() +

-                    "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";

-        }

-    }

-

-    /**

-     * Packet for accepting an offer.

-     */

-    private class AcceptPacket extends IQ {

-

-        AcceptPacket(String workgroup) {

-            this.setTo(workgroup);

-            this.setType(IQ.Type.SET);

-        }

-

-        public String getChildElementXML() {

-            return "<offer-accept id=\"" + Offer.this.getSessionID() +

-                    "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";

-        }

-    }

-

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class embodying the semantic agent chat offer; specific instances allow the acceptance or
+ * rejecting of the offer.<br>
+ *
+ * @author Matt Tucker
+ * @author loki der quaeler
+ * @author Derek DeMoro
+ */
+public class Offer {
+
+    private Connection connection;
+    private AgentSession session;
+
+    private String sessionID;
+    private String userJID;
+    private String userID;
+    private String workgroupName;
+    private Date expiresDate;
+    private Map<String, List<String>> metaData;
+    private OfferContent content;
+
+    private boolean accepted = false;
+    private boolean rejected = false;
+
+    /**
+     * Creates a new offer.
+     *
+     * @param conn the XMPP connection with which the issuing session was created.
+     * @param agentSession the agent session instance through which this offer was issued.
+     * @param userID  the userID of the user from which the offer originates.
+     * @param userJID the XMPP address of the user from which the offer originates.
+     * @param workgroupName the fully qualified name of the workgroup.
+     * @param expiresDate the date at which this offer expires.
+     * @param sessionID the session id associated with the offer.
+     * @param metaData the metadata associated with the offer.
+     * @param content content of the offer. The content explains the reason for the offer
+     *        (e.g. user request, transfer)
+     */
+    Offer(Connection conn, AgentSession agentSession, String userID,
+            String userJID, String workgroupName, Date expiresDate,
+            String sessionID, Map<String, List<String>> metaData, OfferContent content)
+    {
+        this.connection = conn;
+        this.session = agentSession;
+        this.userID = userID;
+        this.userJID = userJID;
+        this.workgroupName = workgroupName;
+        this.expiresDate = expiresDate;
+        this.sessionID = sessionID;
+        this.metaData = metaData;
+        this.content = content;
+    }
+
+    /**
+     * Accepts the offer.
+     */
+    public void accept() {
+        Packet acceptPacket = new AcceptPacket(this.session.getWorkgroupJID());
+        connection.sendPacket(acceptPacket);
+        // TODO: listen for a reply.
+        accepted = true;
+    }
+
+    /**
+     * Rejects the offer.
+     */
+    public void reject() {
+        RejectPacket rejectPacket = new RejectPacket(this.session.getWorkgroupJID());
+        connection.sendPacket(rejectPacket);
+        // TODO: listen for a reply.
+        rejected = true;
+    }
+
+    /**
+     * Returns the userID that the offer originates from. In most cases, the
+     * userID will simply be the JID of the requesting user. However, users can
+     * also manually specify a userID for their request. In that case, that value will
+     * be returned.
+     *
+     * @return the userID of the user from which the offer originates.
+     */
+    public String getUserID() {
+        return userID;
+    }
+
+    /**
+     * Returns the JID of the user that made the offer request.
+     *
+     * @return the user's JID.
+     */
+    public String getUserJID() {
+        return userJID;
+    }
+
+    /**
+     * The fully qualified name of the workgroup (eg support@example.com).
+     *
+     * @return the name of the workgroup.
+     */
+    public String getWorkgroupName() {
+        return this.workgroupName;
+    }
+
+    /**
+     * The date when the offer will expire. The agent must {@link #accept()}
+     * the offer before the expiration date or the offer will lapse and be
+     * routed to another agent. Alternatively, the agent can {@link #reject()}
+     * the offer at any time if they don't wish to accept it..
+     *
+     * @return the date at which this offer expires.
+     */
+    public Date getExpiresDate() {
+        return this.expiresDate;
+    }
+
+    /**
+     * The session ID associated with the offer.
+     *
+     * @return the session id associated with the offer.
+     */
+    public String getSessionID() {
+        return this.sessionID;
+    }
+
+    /**
+     * The meta-data associated with the offer.
+     *
+     * @return the offer meta-data.
+     */
+    public Map<String, List<String>> getMetaData() {
+        return this.metaData;
+    }
+
+    /**
+     * Returns the content of the offer. The content explains the reason for the offer
+     * (e.g. user request, transfer)
+     *
+     * @return the content of the offer.
+     */
+    public OfferContent getContent() {
+        return content;
+    }
+
+    /**
+     * Returns true if the agent accepted this offer.
+     *
+     * @return true if the agent accepted this offer.
+     */
+    public boolean isAccepted() {
+        return accepted;
+    }
+
+    /**
+     * Return true if the agent rejected this offer.
+     *
+     * @return true if the agent rejected this offer.
+     */
+    public boolean isRejected() {
+        return rejected;
+    }
+
+    /**
+     * Packet for rejecting offers.
+     */
+    private class RejectPacket extends IQ {
+
+        RejectPacket(String workgroup) {
+            this.setTo(workgroup);
+            this.setType(IQ.Type.SET);
+        }
+
+        public String getChildElementXML() {
+            return "<offer-reject id=\"" + Offer.this.getSessionID() +
+                    "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";
+        }
+    }
+
+    /**
+     * Packet for accepting an offer.
+     */
+    private class AcceptPacket extends IQ {
+
+        AcceptPacket(String workgroup) {
+            this.setTo(workgroup);
+            this.setType(IQ.Type.SET);
+        }
+
+        public String getChildElementXML() {
+            return "<offer-accept id=\"" + Offer.this.getSessionID() +
+                    "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java
index f55d588..ea1eba7 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java
@@ -1,114 +1,114 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-

-public class OfferConfirmation extends IQ {

-    private String userJID;

-    private long sessionID;

-

-    public String getUserJID() {

-        return userJID;

-    }

-

-    public void setUserJID(String userJID) {

-        this.userJID = userJID;

-    }

-

-    public long getSessionID() {

-        return sessionID;

-    }

-

-    public void setSessionID(long sessionID) {

-        this.sessionID = sessionID;

-    }

-

-

-    public void notifyService(Connection con, String workgroup, String createdRoomName) {

-        NotifyServicePacket packet = new NotifyServicePacket(workgroup, createdRoomName);

-        con.sendPacket(packet);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<offer-confirmation xmlns=\"http://jabber.org/protocol/workgroup\">");

-        buf.append("</offer-confirmation>");

-        return buf.toString();

-    }

-

-    public static class Provider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            final OfferConfirmation confirmation = new OfferConfirmation();

-

-            boolean done = false;

-            while (!done) {

-                parser.next();

-                String elementName = parser.getName();

-                if (parser.getEventType() == XmlPullParser.START_TAG && "user-jid".equals(elementName)) {

-                    try {

-                        confirmation.setUserJID(parser.nextText());

-                    }

-                    catch (NumberFormatException nfe) {

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.START_TAG && "session-id".equals(elementName)) {

-                    try {

-                        confirmation.setSessionID(Long.valueOf(parser.nextText()));

-                    }

-                    catch (NumberFormatException nfe) {

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.END_TAG && "offer-confirmation".equals(elementName)) {

-                    done = true;

-                }

-            }

-

-

-            return confirmation;

-        }

-    }

-

-

-    /**

-     * Packet for notifying server of RoomName

-     */

-    private class NotifyServicePacket extends IQ {

-        String roomName;

-

-        NotifyServicePacket(String workgroup, String roomName) {

-            this.setTo(workgroup);

-            this.setType(IQ.Type.RESULT);

-

-            this.roomName = roomName;

-        }

-

-        public String getChildElementXML() {

-            return "<offer-confirmation  roomname=\"" + roomName + "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";

-        }

-    }

-

-

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+
+public class OfferConfirmation extends IQ {
+    private String userJID;
+    private long sessionID;
+
+    public String getUserJID() {
+        return userJID;
+    }
+
+    public void setUserJID(String userJID) {
+        this.userJID = userJID;
+    }
+
+    public long getSessionID() {
+        return sessionID;
+    }
+
+    public void setSessionID(long sessionID) {
+        this.sessionID = sessionID;
+    }
+
+
+    public void notifyService(Connection con, String workgroup, String createdRoomName) {
+        NotifyServicePacket packet = new NotifyServicePacket(workgroup, createdRoomName);
+        con.sendPacket(packet);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<offer-confirmation xmlns=\"http://jabber.org/protocol/workgroup\">");
+        buf.append("</offer-confirmation>");
+        return buf.toString();
+    }
+
+    public static class Provider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            final OfferConfirmation confirmation = new OfferConfirmation();
+
+            boolean done = false;
+            while (!done) {
+                parser.next();
+                String elementName = parser.getName();
+                if (parser.getEventType() == XmlPullParser.START_TAG && "user-jid".equals(elementName)) {
+                    try {
+                        confirmation.setUserJID(parser.nextText());
+                    }
+                    catch (NumberFormatException nfe) {
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.START_TAG && "session-id".equals(elementName)) {
+                    try {
+                        confirmation.setSessionID(Long.valueOf(parser.nextText()));
+                    }
+                    catch (NumberFormatException nfe) {
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.END_TAG && "offer-confirmation".equals(elementName)) {
+                    done = true;
+                }
+            }
+
+
+            return confirmation;
+        }
+    }
+
+
+    /**
+     * Packet for notifying server of RoomName
+     */
+    private class NotifyServicePacket extends IQ {
+        String roomName;
+
+        NotifyServicePacket(String workgroup, String roomName) {
+            this.setTo(workgroup);
+            this.setType(IQ.Type.RESULT);
+
+            this.roomName = roomName;
+        }
+
+        public String getChildElementXML() {
+            return "<offer-confirmation  roomname=\"" + roomName + "\" xmlns=\"http://jabber.org/protocol/workgroup" + "\"/>";
+        }
+    }
+
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java
index fb10550..ec079e4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java
@@ -1,32 +1,32 @@
-/**

- * $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.smackx.workgroup.agent;

-

-public interface OfferConfirmationListener {

-

-

-    /**

-     * The implementing class instance will be notified via this when the AgentSession has confirmed

-     * the acceptance of the <code>Offer</code>. The instance will then have the ability to create the room and

-     * send the service the room name created for tracking.

-     *

-     * @param confirmedOffer the ConfirmedOffer

-     */

-    void offerConfirmed(OfferConfirmation confirmedOffer);

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+public interface OfferConfirmationListener {
+
+
+    /**
+     * The implementing class instance will be notified via this when the AgentSession has confirmed
+     * the acceptance of the <code>Offer</code>. The instance will then have the ability to create the room and
+     * send the service the room name created for tracking.
+     *
+     * @param confirmedOffer the ConfirmedOffer
+     */
+    void offerConfirmed(OfferConfirmation confirmedOffer);
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferContent.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferContent.java
index a11ddc3..f6d60ae 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferContent.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferContent.java
@@ -1,55 +1,55 @@
-/**

- * $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.smackx.workgroup.agent;

-

-/**

- * Type of content being included in the offer. The content actually explains the reason

- * the agent is getting an offer.

- *

- * @author Gaston Dombiak

- */

-public abstract class OfferContent {

-

-    /**

-     * Returns true if the content of the offer is related to a user request. This is the

-     * most common type of offers an agent should receive.

-     *

-     * @return true if the content of the offer is related to a user request.

-     */

-    abstract boolean isUserRequest();

-

-    /**

-     * Returns true if the content of the offer is related to a room invitation made by another

-     * agent. This type of offer include the room to join, metadata sent by the user while joining

-     * the queue and the reason why the agent is being invited.

-     *

-     * @return true if the content of the offer is related to a room invitation made by another agent.

-     */

-    abstract boolean isInvitation();

-

-    /**

-     * Returns true if the content of the offer is related to a service transfer made by another

-     * agent. This type of offers include the room to join, metadata sent by the user while joining the

-     * queue and the reason why the agent is receiving the transfer offer.

-     *

-     * @return true if the content of the offer is related to a service transfer made by another agent.

-     */

-    abstract boolean isTransfer();

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+/**
+ * Type of content being included in the offer. The content actually explains the reason
+ * the agent is getting an offer.
+ *
+ * @author Gaston Dombiak
+ */
+public abstract class OfferContent {
+
+    /**
+     * Returns true if the content of the offer is related to a user request. This is the
+     * most common type of offers an agent should receive.
+     *
+     * @return true if the content of the offer is related to a user request.
+     */
+    abstract boolean isUserRequest();
+
+    /**
+     * Returns true if the content of the offer is related to a room invitation made by another
+     * agent. This type of offer include the room to join, metadata sent by the user while joining
+     * the queue and the reason why the agent is being invited.
+     *
+     * @return true if the content of the offer is related to a room invitation made by another agent.
+     */
+    abstract boolean isInvitation();
+
+    /**
+     * Returns true if the content of the offer is related to a service transfer made by another
+     * agent. This type of offers include the room to join, metadata sent by the user while joining the
+     * queue and the reason why the agent is receiving the transfer offer.
+     *
+     * @return true if the content of the offer is related to a service transfer made by another agent.
+     */
+    abstract boolean isTransfer();
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferListener.java
index 5efde99..72e50e3 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/OfferListener.java
@@ -1,49 +1,49 @@
-/**

- * $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.smackx.workgroup.agent;

-

-/**

- * An interface which all classes interested in hearing about chat offers associated to a particular

- *  AgentSession instance should implement.<br>

- *

- * @author Matt Tucker

- * @author loki der quaeler

- * @see org.jivesoftware.smackx.workgroup.agent.AgentSession

- */

-public interface OfferListener {

-

-    /**

-     * The implementing class instance will be notified via this when the AgentSession has received

-     *  an offer for a chat. The instance will then have the ability to accept, reject, or ignore

-     *  the request (resulting in a revocation-by-timeout).

-     *

-     * @param request the Offer instance embodying the details of the offer

-     */

-    public void offerReceived (Offer request);

-

-    /**

-     * The implementing class instance will be notified via this when the AgentSessino has received

-     *  a revocation of a previously extended offer.

-     *

-     * @param revokedOffer the RevokedOffer instance embodying the details of the revoked offer

-     */

-    public void offerRevoked (RevokedOffer revokedOffer);

-

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+/**
+ * An interface which all classes interested in hearing about chat offers associated to a particular
+ *  AgentSession instance should implement.<br>
+ *
+ * @author Matt Tucker
+ * @author loki der quaeler
+ * @see org.jivesoftware.smackx.workgroup.agent.AgentSession
+ */
+public interface OfferListener {
+
+    /**
+     * The implementing class instance will be notified via this when the AgentSession has received
+     *  an offer for a chat. The instance will then have the ability to accept, reject, or ignore
+     *  the request (resulting in a revocation-by-timeout).
+     *
+     * @param request the Offer instance embodying the details of the offer
+     */
+    public void offerReceived (Offer request);
+
+    /**
+     * The implementing class instance will be notified via this when the AgentSessino has received
+     *  a revocation of a previously extended offer.
+     *
+     * @param revokedOffer the RevokedOffer instance embodying the details of the revoked offer
+     */
+    public void offerRevoked (RevokedOffer revokedOffer);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java
index 9fcff9a..e7510d6 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java
@@ -1,60 +1,60 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import java.util.Date;

-import java.util.Set;

-

-import org.jivesoftware.smackx.workgroup.QueueUser;

-

-public interface QueueUsersListener {

-

-    /**

-     * The status of the queue was updated.

-     *

-     * @param queue the workgroup queue.

-     * @param status the status of queue.

-     */

-    public void statusUpdated(WorkgroupQueue queue, WorkgroupQueue.Status status);

-

-    /**

-     * The average wait time of the queue was updated.

-     *

-     * @param queue the workgroup queue.

-     * @param averageWaitTime the average wait time of the queue.

-     */

-    public void averageWaitTimeUpdated(WorkgroupQueue queue, int averageWaitTime);

-

-    /**

-     * The date of oldest entry waiting in the queue was updated.

-     *

-     * @param queue the workgroup queue.

-     * @param oldestEntry the date of the oldest entry waiting in the queue.

-     */

-    public void oldestEntryUpdated(WorkgroupQueue queue, Date oldestEntry);

-

-    /**

-     * The list of users waiting in the queue was updated.

-     *

-     * @param queue the workgroup queue.

-     * @param users the list of users waiting in the queue.

-     */

-    public void usersUpdated(WorkgroupQueue queue, Set<QueueUser> users);

+/**
+ * $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.smackx.workgroup.agent;
+
+import java.util.Date;
+import java.util.Set;
+
+import org.jivesoftware.smackx.workgroup.QueueUser;
+
+public interface QueueUsersListener {
+
+    /**
+     * The status of the queue was updated.
+     *
+     * @param queue the workgroup queue.
+     * @param status the status of queue.
+     */
+    public void statusUpdated(WorkgroupQueue queue, WorkgroupQueue.Status status);
+
+    /**
+     * The average wait time of the queue was updated.
+     *
+     * @param queue the workgroup queue.
+     * @param averageWaitTime the average wait time of the queue.
+     */
+    public void averageWaitTimeUpdated(WorkgroupQueue queue, int averageWaitTime);
+
+    /**
+     * The date of oldest entry waiting in the queue was updated.
+     *
+     * @param queue the workgroup queue.
+     * @param oldestEntry the date of the oldest entry waiting in the queue.
+     */
+    public void oldestEntryUpdated(WorkgroupQueue queue, Date oldestEntry);
+
+    /**
+     * The list of users waiting in the queue was updated.
+     *
+     * @param queue the workgroup queue.
+     * @param users the list of users waiting in the queue.
+     */
+    public void usersUpdated(WorkgroupQueue queue, Set<QueueUser> users);
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java
index dab4d91..88250b2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java
@@ -1,98 +1,98 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import java.util.Date;

-

-/**

- * An immutable simple class to embody the information concerning a revoked offer, this is namely

- *  the reason, the workgroup, the userJID, and the timestamp which the message was received.<br>

- *

- * @author loki der quaeler

- */

-public class RevokedOffer {

-

-    private String userJID;

-    private String userID;

-    private String workgroupName;

-    private String sessionID;

-    private String reason;

-    private Date timestamp;

-

-    /**

-     *

-     * @param userJID the JID of the user for which this revocation was issued.

-     * @param userID the user ID of the user for which this revocation was issued.

-     * @param workgroupName the fully qualified name of the workgroup

-     * @param sessionID the session id attributed to this chain of packets

-     * @param reason the server issued message as to why this revocation was issued.

-     * @param timestamp the timestamp at which the revocation was issued

-     */

-    RevokedOffer(String userJID, String userID, String workgroupName, String sessionID,

-            String reason, Date timestamp) {

-        super();

-

-        this.userJID = userJID;

-        this.userID = userID;

-        this.workgroupName = workgroupName;

-        this.sessionID = sessionID;

-        this.reason = reason;

-        this.timestamp = timestamp;

-    }

-

-    public String getUserJID() {

-        return userJID;

-    }

-

-    /**

-     * @return the jid of the user for which this revocation was issued

-     */

-    public String getUserID() {

-        return this.userID;

-    }

-

-    /**

-     * @return the fully qualified name of the workgroup

-     */

-    public String getWorkgroupName() {

-        return this.workgroupName;

-    }

-

-    /**

-     * @return the session id which will associate all packets for the pending chat

-     */

-    public String getSessionID() {

-        return this.sessionID;

-    }

-

-    /**

-     * @return the server issued message as to why this revocation was issued

-     */

-    public String getReason() {

-        return this.reason;

-    }

-

-    /**

-     * @return the timestamp at which the revocation was issued

-     */

-    public Date getTimestamp() {

-        return this.timestamp;

-    }

+/**
+ * $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.smackx.workgroup.agent;
+
+import java.util.Date;
+
+/**
+ * An immutable simple class to embody the information concerning a revoked offer, this is namely
+ *  the reason, the workgroup, the userJID, and the timestamp which the message was received.<br>
+ *
+ * @author loki der quaeler
+ */
+public class RevokedOffer {
+
+    private String userJID;
+    private String userID;
+    private String workgroupName;
+    private String sessionID;
+    private String reason;
+    private Date timestamp;
+
+    /**
+     *
+     * @param userJID the JID of the user for which this revocation was issued.
+     * @param userID the user ID of the user for which this revocation was issued.
+     * @param workgroupName the fully qualified name of the workgroup
+     * @param sessionID the session id attributed to this chain of packets
+     * @param reason the server issued message as to why this revocation was issued.
+     * @param timestamp the timestamp at which the revocation was issued
+     */
+    RevokedOffer(String userJID, String userID, String workgroupName, String sessionID,
+            String reason, Date timestamp) {
+        super();
+
+        this.userJID = userJID;
+        this.userID = userID;
+        this.workgroupName = workgroupName;
+        this.sessionID = sessionID;
+        this.reason = reason;
+        this.timestamp = timestamp;
+    }
+
+    public String getUserJID() {
+        return userJID;
+    }
+
+    /**
+     * @return the jid of the user for which this revocation was issued
+     */
+    public String getUserID() {
+        return this.userID;
+    }
+
+    /**
+     * @return the fully qualified name of the workgroup
+     */
+    public String getWorkgroupName() {
+        return this.workgroupName;
+    }
+
+    /**
+     * @return the session id which will associate all packets for the pending chat
+     */
+    public String getSessionID() {
+        return this.sessionID;
+    }
+
+    /**
+     * @return the server issued message as to why this revocation was issued
+     */
+    public String getReason() {
+        return this.reason;
+    }
+
+    /**
+     * @return the timestamp at which the revocation was issued
+     */
+    public Date getTimestamp() {
+        return this.timestamp;
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java
index 8a3801f..2a17b47 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java
@@ -1,100 +1,100 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smackx.workgroup.packet.Transcript;

-import org.jivesoftware.smackx.workgroup.packet.Transcripts;

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-

-/**

- * A TranscriptManager helps to retrieve the full conversation transcript of a given session

- * {@link #getTranscript(String, String)} or to retrieve a list with the summary of all the

- * conversations that a user had {@link #getTranscripts(String, String)}.

- *

- * @author Gaston Dombiak

- */

-public class TranscriptManager {

-    private Connection connection;

-

-    public TranscriptManager(Connection connection) {

-        this.connection = connection;

-    }

-

-    /**

-     * Returns the full conversation transcript of a given session.

-     *

-     * @param sessionID the id of the session to get the full transcript.

-     * @param workgroupJID the JID of the workgroup that will process the request.

-     * @return the full conversation transcript of a given session.

-     * @throws XMPPException if an error occurs while getting the information.

-     */

-    public Transcript getTranscript(String workgroupJID, String sessionID) throws XMPPException {

-        Transcript request = new Transcript(sessionID);

-        request.setTo(workgroupJID);

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        // Send the request

-        connection.sendPacket(request);

-

-        Transcript response = (Transcript) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Returns the transcripts of a given user. The answer will contain the complete history of

-     * conversations that a user had.

-     *

-     * @param userID the id of the user to get his conversations.

-     * @param workgroupJID the JID of the workgroup that will process the request.

-     * @return the transcripts of a given user.

-     * @throws XMPPException if an error occurs while getting the information.

-     */

-    public Transcripts getTranscripts(String workgroupJID, String userID) throws XMPPException {

-        Transcripts request = new Transcripts(userID);

-        request.setTo(workgroupJID);

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        // Send the request

-        connection.sendPacket(request);

-

-        Transcripts response = (Transcripts) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smackx.workgroup.packet.Transcript;
+import org.jivesoftware.smackx.workgroup.packet.Transcripts;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+
+/**
+ * A TranscriptManager helps to retrieve the full conversation transcript of a given session
+ * {@link #getTranscript(String, String)} or to retrieve a list with the summary of all the
+ * conversations that a user had {@link #getTranscripts(String, String)}.
+ *
+ * @author Gaston Dombiak
+ */
+public class TranscriptManager {
+    private Connection connection;
+
+    public TranscriptManager(Connection connection) {
+        this.connection = connection;
+    }
+
+    /**
+     * Returns the full conversation transcript of a given session.
+     *
+     * @param sessionID the id of the session to get the full transcript.
+     * @param workgroupJID the JID of the workgroup that will process the request.
+     * @return the full conversation transcript of a given session.
+     * @throws XMPPException if an error occurs while getting the information.
+     */
+    public Transcript getTranscript(String workgroupJID, String sessionID) throws XMPPException {
+        Transcript request = new Transcript(sessionID);
+        request.setTo(workgroupJID);
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        // Send the request
+        connection.sendPacket(request);
+
+        Transcript response = (Transcript) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Returns the transcripts of a given user. The answer will contain the complete history of
+     * conversations that a user had.
+     *
+     * @param userID the id of the user to get his conversations.
+     * @param workgroupJID the JID of the workgroup that will process the request.
+     * @return the transcripts of a given user.
+     * @throws XMPPException if an error occurs while getting the information.
+     */
+    public Transcripts getTranscripts(String workgroupJID, String userID) throws XMPPException {
+        Transcripts request = new Transcripts(userID);
+        request.setTo(workgroupJID);
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        // Send the request
+        connection.sendPacket(request);
+
+        Transcripts response = (Transcripts) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java
index 8260cd6..498cc8a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java
@@ -1,111 +1,111 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import org.jivesoftware.smackx.workgroup.packet.TranscriptSearch;

-import org.jivesoftware.smack.PacketCollector;

-import org.jivesoftware.smack.SmackConfiguration;

-import org.jivesoftware.smack.Connection;

-import org.jivesoftware.smack.XMPPException;

-import org.jivesoftware.smack.filter.PacketIDFilter;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.ReportedData;

-

-/**

- * A TranscriptSearchManager helps to retrieve the form to use for searching transcripts

- * {@link #getSearchForm(String)} or to submit a search form and return the results of

- * the search {@link #submitSearch(String, Form)}.

- *

- * @author Gaston Dombiak

- */

-public class TranscriptSearchManager {

-    private Connection connection;

-

-    public TranscriptSearchManager(Connection connection) {

-        this.connection = connection;

-    }

-

-    /**

-     * Returns the Form to use for searching transcripts. It is unlikely that the server

-     * will change the form (without a restart) so it is safe to keep the returned form

-     * for future submissions.

-     *

-     * @param serviceJID the address of the workgroup service.

-     * @return the Form to use for searching transcripts.

-     * @throws XMPPException if an error occurs while sending the request to the server.

-     */

-    public Form getSearchForm(String serviceJID) throws XMPPException {

-        TranscriptSearch search = new TranscriptSearch();

-        search.setType(IQ.Type.GET);

-        search.setTo(serviceJID);

-

-        PacketCollector collector = connection.createPacketCollector(

-                new PacketIDFilter(search.getPacketID()));

-        connection.sendPacket(search);

-

-        TranscriptSearch response = (TranscriptSearch) collector.nextResult(

-                SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return Form.getFormFrom(response);

-    }

-

-    /**

-     * Submits the completed form and returns the result of the transcript search. The result

-     * will include all the data returned from the server so be careful with the amount of

-     * data that the search may return.

-     *

-     * @param serviceJID    the address of the workgroup service.

-     * @param completedForm the filled out search form.

-     * @return the result of the transcript search.

-     * @throws XMPPException if an error occurs while submiting the search to the server.

-     */

-    public ReportedData submitSearch(String serviceJID, Form completedForm) throws XMPPException {

-        TranscriptSearch search = new TranscriptSearch();

-        search.setType(IQ.Type.GET);

-        search.setTo(serviceJID);

-        search.addExtension(completedForm.getDataFormToSend());

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(search.getPacketID()));

-        connection.sendPacket(search);

-

-        TranscriptSearch response = (TranscriptSearch) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return ReportedData.getReportedDataFrom(response);

-    }

-}

-

-

+/**
+ * $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.smackx.workgroup.agent;
+
+import org.jivesoftware.smackx.workgroup.packet.TranscriptSearch;
+import org.jivesoftware.smack.PacketCollector;
+import org.jivesoftware.smack.SmackConfiguration;
+import org.jivesoftware.smack.Connection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.ReportedData;
+
+/**
+ * A TranscriptSearchManager helps to retrieve the form to use for searching transcripts
+ * {@link #getSearchForm(String)} or to submit a search form and return the results of
+ * the search {@link #submitSearch(String, Form)}.
+ *
+ * @author Gaston Dombiak
+ */
+public class TranscriptSearchManager {
+    private Connection connection;
+
+    public TranscriptSearchManager(Connection connection) {
+        this.connection = connection;
+    }
+
+    /**
+     * Returns the Form to use for searching transcripts. It is unlikely that the server
+     * will change the form (without a restart) so it is safe to keep the returned form
+     * for future submissions.
+     *
+     * @param serviceJID the address of the workgroup service.
+     * @return the Form to use for searching transcripts.
+     * @throws XMPPException if an error occurs while sending the request to the server.
+     */
+    public Form getSearchForm(String serviceJID) throws XMPPException {
+        TranscriptSearch search = new TranscriptSearch();
+        search.setType(IQ.Type.GET);
+        search.setTo(serviceJID);
+
+        PacketCollector collector = connection.createPacketCollector(
+                new PacketIDFilter(search.getPacketID()));
+        connection.sendPacket(search);
+
+        TranscriptSearch response = (TranscriptSearch) collector.nextResult(
+                SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return Form.getFormFrom(response);
+    }
+
+    /**
+     * Submits the completed form and returns the result of the transcript search. The result
+     * will include all the data returned from the server so be careful with the amount of
+     * data that the search may return.
+     *
+     * @param serviceJID    the address of the workgroup service.
+     * @param completedForm the filled out search form.
+     * @return the result of the transcript search.
+     * @throws XMPPException if an error occurs while submiting the search to the server.
+     */
+    public ReportedData submitSearch(String serviceJID, Form completedForm) throws XMPPException {
+        TranscriptSearch search = new TranscriptSearch();
+        search.setType(IQ.Type.GET);
+        search.setTo(serviceJID);
+        search.addExtension(completedForm.getDataFormToSend());
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(search.getPacketID()));
+        connection.sendPacket(search);
+
+        TranscriptSearch response = (TranscriptSearch) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return ReportedData.getReportedDataFrom(response);
+    }
+}
+
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java
index a3abbaa..0d0f87e 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java
@@ -1,62 +1,62 @@
-/**

- * $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.smackx.workgroup.agent;

-

-/**

- * Request sent by an agent to transfer a support session to another agent or user.

- *

- * @author Gaston Dombiak

- */

-public class TransferRequest extends OfferContent {

-

-    private String inviter;

-    private String room;

-    private String reason;

-

-    public TransferRequest(String inviter, String room, String reason) {

-        this.inviter = inviter;

-        this.room = room;

-        this.reason = reason;

-    }

-

-    public String getInviter() {

-        return inviter;

-    }

-

-    public String getRoom() {

-        return room;

-    }

-

-    public String getReason() {

-        return reason;

-    }

-

-    boolean isUserRequest() {

-        return false;

-    }

-

-    boolean isInvitation() {

-        return false;

-    }

-

-    boolean isTransfer() {

-        return true;

-    }

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+/**
+ * Request sent by an agent to transfer a support session to another agent or user.
+ *
+ * @author Gaston Dombiak
+ */
+public class TransferRequest extends OfferContent {
+
+    private String inviter;
+    private String room;
+    private String reason;
+
+    public TransferRequest(String inviter, String room, String reason) {
+        this.inviter = inviter;
+        this.room = room;
+        this.reason = reason;
+    }
+
+    public String getInviter() {
+        return inviter;
+    }
+
+    public String getRoom() {
+        return room;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    boolean isUserRequest() {
+        return false;
+    }
+
+    boolean isInvitation() {
+        return false;
+    }
+
+    boolean isTransfer() {
+        return true;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/UserRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/UserRequest.java
index ccaaaf3..f24208d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/UserRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/UserRequest.java
@@ -1,47 +1,47 @@
-/**

- * $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.smackx.workgroup.agent;

-

-/**

- * Requests made by users to get support by some agent.

- *

- * @author Gaston Dombiak

- */

-public class UserRequest extends OfferContent {

-    // TODO Do we want to use a singleton? Should we store the userID here?

-    private static UserRequest instance = new UserRequest();

-

-    public static OfferContent getInstance() {

-        return instance;

-    }

-

-    boolean isUserRequest() {

-        return true;

-    }

-

-    boolean isInvitation() {

-        return false;

-    }

-

-    boolean isTransfer() {

-        return false;

-    }

-

-}

+/**
+ * $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.smackx.workgroup.agent;
+
+/**
+ * Requests made by users to get support by some agent.
+ *
+ * @author Gaston Dombiak
+ */
+public class UserRequest extends OfferContent {
+    // TODO Do we want to use a singleton? Should we store the userID here?
+    private static UserRequest instance = new UserRequest();
+
+    public static OfferContent getInstance() {
+        return instance;
+    }
+
+    boolean isUserRequest() {
+        return true;
+    }
+
+    boolean isInvitation() {
+        return false;
+    }
+
+    boolean isTransfer() {
+        return false;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java
index b43c826..b0d07a6 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java
@@ -1,224 +1,224 @@
-/**

- * $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.smackx.workgroup.agent;

-

-import java.util.*;

-

-import org.jivesoftware.smackx.workgroup.QueueUser;

-

-/**

- * A queue in a workgroup, which is a pool of agents that are routed  a specific type of

- * chat request.

- */

-public class WorkgroupQueue {

-

-    private String name;

-    private Status status = Status.CLOSED;

-

-    private int averageWaitTime = -1;

-    private Date oldestEntry = null;

-    private Set<QueueUser> users = Collections.emptySet();

-

-    private int maxChats = 0;

-    private int currentChats = 0;

-

-    /**

-     * Creates a new workgroup queue instance.

-     *

-     * @param name the name of the queue.

-     */

-    WorkgroupQueue(String name) {

-        this.name = name;

-    }

-

-    /**

-     * Returns the name of the queue.

-     *

-     * @return the name of the queue.

-     */

-    public String getName() {

-        return name;

-    }

-

-    /**

-     * Returns the status of the queue.

-     *

-     * @return the status of the queue.

-     */

-    public Status getStatus() {

-        return status;

-    }

-

-    void setStatus(Status status) {

-        this.status = status;

-    }

-

-    /**

-     * Returns the number of users waiting in the queue waiting to be routed to

-     * an agent.

-     *

-     * @return the number of users waiting in the queue.

-     */

-    public int getUserCount() {

-        if (users == null) {

-            return 0;

-        }

-        return users.size();

-    }

-

-    /**

-     * Returns an Iterator for the users in the queue waiting to be routed to

-     * an agent (QueueUser instances).

-     *

-     * @return an Iterator for the users waiting in the queue.

-     */

-    public Iterator<QueueUser> getUsers() {

-        if (users == null) {

-            return new HashSet<QueueUser>().iterator();

-        }

-        return Collections.unmodifiableSet(users).iterator();

-    }

-

-    void setUsers(Set<QueueUser> users) {

-        this.users = users;

-    }

-

-    /**

-     * Returns the average amount of time users wait in the queue before being

-     * routed to an agent. If average wait time info isn't available, -1 will

-     * be returned.

-     *

-     * @return the average wait time

-     */

-    public int getAverageWaitTime() {

-        return averageWaitTime;

-    }

-

-    void setAverageWaitTime(int averageTime) {

-        this.averageWaitTime = averageTime;

-    }

-

-    /**

-     * Returns the date of the oldest request waiting in the queue. If there

-     * are no requests waiting to be routed, this method will return <tt>null</tt>.

-     *

-     * @return the date of the oldest request in the queue.

-     */

-    public Date getOldestEntry() {

-        return oldestEntry;

-    }

-

-    void setOldestEntry(Date oldestEntry) {

-        this.oldestEntry = oldestEntry;

-    }

-

-    /**

-     * Returns the maximum number of simultaneous chats the queue can handle.

-     *

-     * @return the max number of chats the queue can handle.

-     */

-    public int getMaxChats() {

-        return maxChats;

-    }

-

-    void setMaxChats(int maxChats) {

-        this.maxChats = maxChats;

-    }

-

-    /**

-     * Returns the current number of active chat sessions in the queue.

-     *

-     * @return the current number of active chat sessions in the queue.

-     */

-    public int getCurrentChats() {

-        return currentChats;

-    }

-

-    void setCurrentChats(int currentChats) {

-        this.currentChats = currentChats;

-    }

-

-    /**

-     * A class to represent the status of the workgroup. The possible values are:

-     *

-     * <ul>

-     *      <li>WorkgroupQueue.Status.OPEN -- the queue is active and accepting new chat requests.

-     *      <li>WorkgroupQueue.Status.ACTIVE -- the queue is active but NOT accepting new chat

-     *          requests.

-     *      <li>WorkgroupQueue.Status.CLOSED -- the queue is NOT active and NOT accepting new

-     *          chat requests.

-     * </ul>

-     */

-    public static class Status {

-

-        /**

-         * The queue is active and accepting new chat requests.

-         */

-        public static final Status OPEN = new Status("open");

-

-        /**

-         * The queue is active but NOT accepting new chat requests. This state might

-         * occur when the workgroup has closed because regular support hours have closed,

-         * but there are still several requests left in the queue.

-         */

-        public static final Status ACTIVE = new Status("active");

-

-        /**

-         * The queue is NOT active and NOT accepting new chat requests.

-         */

-        public static final Status CLOSED = new Status("closed");

-

-        /**

-         * Converts a String into the corresponding status. Valid String values

-         * that can be converted to a status are: "open", "active", and "closed".

-         *

-         * @param type the String value to covert.

-         * @return the corresponding Type.

-         */

-        public static Status fromString(String type) {

-            if (type == null) {

-                return null;

-            }

-            type = type.toLowerCase();

-            if (OPEN.toString().equals(type)) {

-                return OPEN;

-            }

-            else if (ACTIVE.toString().equals(type)) {

-                return ACTIVE;

-            }

-            else if (CLOSED.toString().equals(type)) {

-                return CLOSED;

-            }

-            else {

-                return null;

-            }

-        }

-

-        private String value;

-

-        private Status(String value) {

-            this.value = value;

-        }

-

-        public String toString() {

-            return value;

-        }

-    }

+/**
+ * $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.smackx.workgroup.agent;
+
+import java.util.*;
+
+import org.jivesoftware.smackx.workgroup.QueueUser;
+
+/**
+ * A queue in a workgroup, which is a pool of agents that are routed  a specific type of
+ * chat request.
+ */
+public class WorkgroupQueue {
+
+    private String name;
+    private Status status = Status.CLOSED;
+
+    private int averageWaitTime = -1;
+    private Date oldestEntry = null;
+    private Set<QueueUser> users = Collections.emptySet();
+
+    private int maxChats = 0;
+    private int currentChats = 0;
+
+    /**
+     * Creates a new workgroup queue instance.
+     *
+     * @param name the name of the queue.
+     */
+    WorkgroupQueue(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the name of the queue.
+     *
+     * @return the name of the queue.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the status of the queue.
+     *
+     * @return the status of the queue.
+     */
+    public Status getStatus() {
+        return status;
+    }
+
+    void setStatus(Status status) {
+        this.status = status;
+    }
+
+    /**
+     * Returns the number of users waiting in the queue waiting to be routed to
+     * an agent.
+     *
+     * @return the number of users waiting in the queue.
+     */
+    public int getUserCount() {
+        if (users == null) {
+            return 0;
+        }
+        return users.size();
+    }
+
+    /**
+     * Returns an Iterator for the users in the queue waiting to be routed to
+     * an agent (QueueUser instances).
+     *
+     * @return an Iterator for the users waiting in the queue.
+     */
+    public Iterator<QueueUser> getUsers() {
+        if (users == null) {
+            return new HashSet<QueueUser>().iterator();
+        }
+        return Collections.unmodifiableSet(users).iterator();
+    }
+
+    void setUsers(Set<QueueUser> users) {
+        this.users = users;
+    }
+
+    /**
+     * Returns the average amount of time users wait in the queue before being
+     * routed to an agent. If average wait time info isn't available, -1 will
+     * be returned.
+     *
+     * @return the average wait time
+     */
+    public int getAverageWaitTime() {
+        return averageWaitTime;
+    }
+
+    void setAverageWaitTime(int averageTime) {
+        this.averageWaitTime = averageTime;
+    }
+
+    /**
+     * Returns the date of the oldest request waiting in the queue. If there
+     * are no requests waiting to be routed, this method will return <tt>null</tt>.
+     *
+     * @return the date of the oldest request in the queue.
+     */
+    public Date getOldestEntry() {
+        return oldestEntry;
+    }
+
+    void setOldestEntry(Date oldestEntry) {
+        this.oldestEntry = oldestEntry;
+    }
+
+    /**
+     * Returns the maximum number of simultaneous chats the queue can handle.
+     *
+     * @return the max number of chats the queue can handle.
+     */
+    public int getMaxChats() {
+        return maxChats;
+    }
+
+    void setMaxChats(int maxChats) {
+        this.maxChats = maxChats;
+    }
+
+    /**
+     * Returns the current number of active chat sessions in the queue.
+     *
+     * @return the current number of active chat sessions in the queue.
+     */
+    public int getCurrentChats() {
+        return currentChats;
+    }
+
+    void setCurrentChats(int currentChats) {
+        this.currentChats = currentChats;
+    }
+
+    /**
+     * A class to represent the status of the workgroup. The possible values are:
+     *
+     * <ul>
+     *      <li>WorkgroupQueue.Status.OPEN -- the queue is active and accepting new chat requests.
+     *      <li>WorkgroupQueue.Status.ACTIVE -- the queue is active but NOT accepting new chat
+     *          requests.
+     *      <li>WorkgroupQueue.Status.CLOSED -- the queue is NOT active and NOT accepting new
+     *          chat requests.
+     * </ul>
+     */
+    public static class Status {
+
+        /**
+         * The queue is active and accepting new chat requests.
+         */
+        public static final Status OPEN = new Status("open");
+
+        /**
+         * The queue is active but NOT accepting new chat requests. This state might
+         * occur when the workgroup has closed because regular support hours have closed,
+         * but there are still several requests left in the queue.
+         */
+        public static final Status ACTIVE = new Status("active");
+
+        /**
+         * The queue is NOT active and NOT accepting new chat requests.
+         */
+        public static final Status CLOSED = new Status("closed");
+
+        /**
+         * Converts a String into the corresponding status. Valid String values
+         * that can be converted to a status are: "open", "active", and "closed".
+         *
+         * @param type the String value to covert.
+         * @return the corresponding Type.
+         */
+        public static Status fromString(String type) {
+            if (type == null) {
+                return null;
+            }
+            type = type.toLowerCase();
+            if (OPEN.toString().equals(type)) {
+                return OPEN;
+            }
+            else if (ACTIVE.toString().equals(type)) {
+                return ACTIVE;
+            }
+            else if (CLOSED.toString().equals(type)) {
+                return CLOSED;
+            }
+            else {
+                return null;
+            }
+        }
+
+        private String value;
+
+        private Status(String value) {
+            this.value = value;
+        }
+
+        public String toString() {
+            return value;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java
index f2dc08e..8348d97 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java
@@ -1,82 +1,82 @@
-/**

- * $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.smackx.workgroup.ext.forms;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.xmlpull.v1.XmlPullParser;

-

-public class WorkgroupForm extends IQ {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "workgroup-form";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        // Add packet extensions, if any are defined.

-        buf.append(getExtensionsXML());

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for WebForm packets.

-     *

-     * @author Derek DeMoro

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public InternalProvider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            WorkgroupForm answer = new WorkgroupForm();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    // Parse the packet extension

-                    answer.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(),

-                            parser.getNamespace(), parser));

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return answer;

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.ext.forms;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+
+public class WorkgroupForm extends IQ {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "workgroup-form";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        // Add packet extensions, if any are defined.
+        buf.append(getExtensionsXML());
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for WebForm packets.
+     *
+     * @author Derek DeMoro
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public InternalProvider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            WorkgroupForm answer = new WorkgroupForm();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    // Parse the packet extension
+                    answer.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(),
+                            parser.getNamespace(), parser));
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return answer;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java
index 7b8d200..fcc9de0 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java
@@ -1,155 +1,155 @@
-/**

- * $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.smackx.workgroup.ext.history;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Date;

-import java.util.List;

-

-/**

- * IQ provider used to retrieve individual agent information. Each chat session can be mapped

- * to one or more jids and therefore retrievable.

- */

-public class AgentChatHistory extends IQ {

-    private String agentJID;

-    private int maxSessions;

-    private long startDate;

-

-    private List<AgentChatSession> agentChatSessions = new ArrayList<AgentChatSession>();

-

-    public AgentChatHistory(String agentJID, int maxSessions, Date startDate) {

-        this.agentJID = agentJID;

-        this.maxSessions = maxSessions;

-        this.startDate = startDate.getTime();

-    }

-

-    public AgentChatHistory(String agentJID, int maxSessions) {

-        this.agentJID = agentJID;

-        this.maxSessions = maxSessions;

-        this.startDate = 0;

-    }

-

-    public AgentChatHistory() {

-    }

-

-    public void addChatSession(AgentChatSession chatSession) {

-        agentChatSessions.add(chatSession);

-    }

-

-    public Collection<AgentChatSession> getAgentChatSessions() {

-        return agentChatSessions;

-    }

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "chat-sessions";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");

-        buf.append('"');

-        buf.append(NAMESPACE);

-        buf.append('"');

-        buf.append(" agentJID=\"" + agentJID + "\"");

-        buf.append(" maxSessions=\"" + maxSessions + "\"");

-        buf.append(" startDate=\"" + startDate + "\"");

-

-        buf.append("></").append(ELEMENT_NAME).append("> ");

-        return buf.toString();

-    }

-

-    /**

-     * Packet extension provider for AgentHistory packets.

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-

-            AgentChatHistory agentChatHistory = new AgentChatHistory();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("chat-session".equals(parser.getName()))) {

-                    agentChatHistory.addChatSession(parseChatSetting(parser));

-

-                }

-                else if (eventType == XmlPullParser.END_TAG && ELEMENT_NAME.equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return agentChatHistory;

-        }

-

-        private AgentChatSession parseChatSetting(XmlPullParser parser) throws Exception {

-

-            boolean done = false;

-            Date date = null;

-            long duration = 0;

-            String visitorsName = null;

-            String visitorsEmail = null;

-            String sessionID = null;

-            String question = null;

-

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("date".equals(parser.getName()))) {

-                    String dateStr = parser.nextText();

-                    long l = Long.valueOf(dateStr).longValue();

-                    date = new Date(l);

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("duration".equals(parser.getName()))) {

-                    duration = Long.valueOf(parser.nextText()).longValue();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("visitorsName".equals(parser.getName()))) {

-                    visitorsName = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("visitorsEmail".equals(parser.getName()))) {

-                    visitorsEmail = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("sessionID".equals(parser.getName()))) {

-                    sessionID = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("question".equals(parser.getName()))) {

-                    question = parser.nextText();

-                }

-                else if (eventType == XmlPullParser.END_TAG && "chat-session".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return new AgentChatSession(date, duration, visitorsName, visitorsEmail, sessionID, question);

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.ext.history;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * IQ provider used to retrieve individual agent information. Each chat session can be mapped
+ * to one or more jids and therefore retrievable.
+ */
+public class AgentChatHistory extends IQ {
+    private String agentJID;
+    private int maxSessions;
+    private long startDate;
+
+    private List<AgentChatSession> agentChatSessions = new ArrayList<AgentChatSession>();
+
+    public AgentChatHistory(String agentJID, int maxSessions, Date startDate) {
+        this.agentJID = agentJID;
+        this.maxSessions = maxSessions;
+        this.startDate = startDate.getTime();
+    }
+
+    public AgentChatHistory(String agentJID, int maxSessions) {
+        this.agentJID = agentJID;
+        this.maxSessions = maxSessions;
+        this.startDate = 0;
+    }
+
+    public AgentChatHistory() {
+    }
+
+    public void addChatSession(AgentChatSession chatSession) {
+        agentChatSessions.add(chatSession);
+    }
+
+    public Collection<AgentChatSession> getAgentChatSessions() {
+        return agentChatSessions;
+    }
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "chat-sessions";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");
+        buf.append('"');
+        buf.append(NAMESPACE);
+        buf.append('"');
+        buf.append(" agentJID=\"" + agentJID + "\"");
+        buf.append(" maxSessions=\"" + maxSessions + "\"");
+        buf.append(" startDate=\"" + startDate + "\"");
+
+        buf.append("></").append(ELEMENT_NAME).append("> ");
+        return buf.toString();
+    }
+
+    /**
+     * Packet extension provider for AgentHistory packets.
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+
+            AgentChatHistory agentChatHistory = new AgentChatHistory();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("chat-session".equals(parser.getName()))) {
+                    agentChatHistory.addChatSession(parseChatSetting(parser));
+
+                }
+                else if (eventType == XmlPullParser.END_TAG && ELEMENT_NAME.equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return agentChatHistory;
+        }
+
+        private AgentChatSession parseChatSetting(XmlPullParser parser) throws Exception {
+
+            boolean done = false;
+            Date date = null;
+            long duration = 0;
+            String visitorsName = null;
+            String visitorsEmail = null;
+            String sessionID = null;
+            String question = null;
+
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("date".equals(parser.getName()))) {
+                    String dateStr = parser.nextText();
+                    long l = Long.valueOf(dateStr).longValue();
+                    date = new Date(l);
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("duration".equals(parser.getName()))) {
+                    duration = Long.valueOf(parser.nextText()).longValue();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("visitorsName".equals(parser.getName()))) {
+                    visitorsName = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("visitorsEmail".equals(parser.getName()))) {
+                    visitorsEmail = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("sessionID".equals(parser.getName()))) {
+                    sessionID = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("question".equals(parser.getName()))) {
+                    question = parser.nextText();
+                }
+                else if (eventType == XmlPullParser.END_TAG && "chat-session".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return new AgentChatSession(date, duration, visitorsName, visitorsEmail, sessionID, question);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java
index 5113cda..290a562 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java
@@ -1,93 +1,93 @@
-/**

- * $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.smackx.workgroup.ext.history;

-

-import java.util.Date;

-

-/**

- * Represents one chat session for an agent.

- */

-public class AgentChatSession {

-    public Date startDate;

-    public long duration;

-    public String visitorsName;

-    public String visitorsEmail;

-    public String sessionID;

-    public String question;

-

-    public AgentChatSession(Date date, long duration, String visitorsName, String visitorsEmail, String sessionID, String question) {

-        this.startDate = date;

-        this.duration = duration;

-        this.visitorsName = visitorsName;

-        this.visitorsEmail = visitorsEmail;

-        this.sessionID = sessionID;

-        this.question = question;

-    }

-

-    public Date getStartDate() {

-        return startDate;

-    }

-

-    public void setStartDate(Date startDate) {

-        this.startDate = startDate;

-    }

-

-    public long getDuration() {

-        return duration;

-    }

-

-    public void setDuration(long duration) {

-        this.duration = duration;

-    }

-

-    public String getVisitorsName() {

-        return visitorsName;

-    }

-

-    public void setVisitorsName(String visitorsName) {

-        this.visitorsName = visitorsName;

-    }

-

-    public String getVisitorsEmail() {

-        return visitorsEmail;

-    }

-

-    public void setVisitorsEmail(String visitorsEmail) {

-        this.visitorsEmail = visitorsEmail;

-    }

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public void setSessionID(String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-    public void setQuestion(String question){

-        this.question = question;

-    }

-

-    public String getQuestion(){

-        return question;

-    }

-

-

-}

+/**
+ * $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.smackx.workgroup.ext.history;
+
+import java.util.Date;
+
+/**
+ * Represents one chat session for an agent.
+ */
+public class AgentChatSession {
+    public Date startDate;
+    public long duration;
+    public String visitorsName;
+    public String visitorsEmail;
+    public String sessionID;
+    public String question;
+
+    public AgentChatSession(Date date, long duration, String visitorsName, String visitorsEmail, String sessionID, String question) {
+        this.startDate = date;
+        this.duration = duration;
+        this.visitorsName = visitorsName;
+        this.visitorsEmail = visitorsEmail;
+        this.sessionID = sessionID;
+        this.question = question;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public String getVisitorsName() {
+        return visitorsName;
+    }
+
+    public void setVisitorsName(String visitorsName) {
+        this.visitorsName = visitorsName;
+    }
+
+    public String getVisitorsEmail() {
+        return visitorsEmail;
+    }
+
+    public void setVisitorsEmail(String visitorsEmail) {
+        this.visitorsEmail = visitorsEmail;
+    }
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public void setSessionID(String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+    public void setQuestion(String question){
+        this.question = question;
+    }
+
+    public String getQuestion(){
+        return question;
+    }
+
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java
index 301e1a5..1eaf30f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java
@@ -1,116 +1,116 @@
-/**

- * $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.smackx.workgroup.ext.history;

-

-import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.HashMap;

-import java.util.List;

-import java.util.Map;

-

-public class ChatMetadata extends IQ {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "chat-metadata";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-

-    private String sessionID;

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public void setSessionID(String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-

-    private Map<String, List<String>> map = new HashMap<String, List<String>>();

-

-    public void setMetadata(Map<String, List<String>> metadata){

-        this.map = metadata;

-    }

-

-    public Map<String, List<String>> getMetadata(){

-        return map;

-    }

-

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        buf.append("<sessionID>").append(getSessionID()).append("</sessionID>");

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for Metadata packets.

-     *

-     * @author Derek DeMoro

-     */

-    public static class Provider implements IQProvider {

-

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            final ChatMetadata chatM = new ChatMetadata();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    if (parser.getName().equals("sessionID")) {

-                       chatM.setSessionID(parser.nextText());

-                    }

-                    else if (parser.getName().equals("metadata")) {

-                        Map<String, List<String>> map = MetaDataUtils.parseMetaData(parser);

-                        chatM.setMetadata(map);

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return chatM;

-        }

-    }

-}

-

-

-

-

+/**
+ * $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.smackx.workgroup.ext.history;
+
+import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ChatMetadata extends IQ {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "chat-metadata";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+
+    private String sessionID;
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public void setSessionID(String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+
+    private Map<String, List<String>> map = new HashMap<String, List<String>>();
+
+    public void setMetadata(Map<String, List<String>> metadata){
+        this.map = metadata;
+    }
+
+    public Map<String, List<String>> getMetadata(){
+        return map;
+    }
+
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        buf.append("<sessionID>").append(getSessionID()).append("</sessionID>");
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for Metadata packets.
+     *
+     * @author Derek DeMoro
+     */
+    public static class Provider implements IQProvider {
+
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            final ChatMetadata chatM = new ChatMetadata();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if (parser.getName().equals("sessionID")) {
+                       chatM.setSessionID(parser.nextText());
+                    }
+                    else if (parser.getName().equals("metadata")) {
+                        Map<String, List<String>> map = MetaDataUtils.parseMetaData(parser);
+                        chatM.setMetadata(map);
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return chatM;
+        }
+    }
+}
+
+
+
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java
index acf6196..114902f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java
@@ -1,68 +1,68 @@
-/**

- * $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.smackx.workgroup.ext.macros;

-

-/**

- * Macro datamodel.

- */

-public class Macro {

-    public static final int TEXT = 0;

-    public static final int URL = 1;

-    public static final int IMAGE = 2;

-

-

-    private String title;

-    private String description;

-    private String response;

-    private int type;

-

-    public String getTitle() {

-        return title;

-    }

-

-    public void setTitle(String title) {

-        this.title = title;

-    }

-

-    public String getDescription() {

-        return description;

-    }

-

-    public void setDescription(String description) {

-        this.description = description;

-    }

-

-    public String getResponse() {

-        return response;

-    }

-

-    public void setResponse(String response) {

-        this.response = response;

-    }

-

-    public int getType() {

-        return type;

-    }

-

-    public void setType(int type) {

-        this.type = type;

-    }

-

-}

+/**
+ * $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.smackx.workgroup.ext.macros;
+
+/**
+ * Macro datamodel.
+ */
+public class Macro {
+    public static final int TEXT = 0;
+    public static final int URL = 1;
+    public static final int IMAGE = 2;
+
+
+    private String title;
+    private String description;
+    private String response;
+    private int type;
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getResponse() {
+        return response;
+    }
+
+    public void setResponse(String response) {
+        this.response = response;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java
index 0742b3d..bb583ae 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java
@@ -1,143 +1,143 @@
-/**

- * $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.smackx.workgroup.ext.macros;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * MacroGroup datamodel.

- */

-public class MacroGroup {

-    private List<Macro> macros;

-    private List<MacroGroup> macroGroups;

-

-

-    // Define MacroGroup

-    private String title;

-

-    public MacroGroup() {

-        macros = new ArrayList<Macro>();

-        macroGroups = new ArrayList<MacroGroup>();

-    }

-

-    public void addMacro(Macro macro) {

-        macros.add(macro);

-    }

-

-    public void removeMacro(Macro macro) {

-        macros.remove(macro);

-    }

-

-    public Macro getMacroByTitle(String title) {

-        Collection<Macro> col = Collections.unmodifiableList(macros);

-        Iterator<Macro> iter = col.iterator();

-        while (iter.hasNext()) {

-            Macro macro = (Macro)iter.next();

-            if (macro.getTitle().equalsIgnoreCase(title)) {

-                return macro;

-            }

-        }

-        return null;

-    }

-

-    public void addMacroGroup(MacroGroup group) {

-        macroGroups.add(group);

-    }

-

-    public void removeMacroGroup(MacroGroup group) {

-        macroGroups.remove(group);

-    }

-

-    public Macro getMacro(int location) {

-        return (Macro)macros.get(location);

-    }

-

-    public MacroGroup getMacroGroupByTitle(String title) {

-        Collection<MacroGroup> col = Collections.unmodifiableList(macroGroups);

-        Iterator<MacroGroup> iter = col.iterator();

-        while (iter.hasNext()) {

-            MacroGroup group = (MacroGroup)iter.next();

-            if (group.getTitle().equalsIgnoreCase(title)) {

-                return group;

-            }

-        }

-        return null;

-    }

-

-    public MacroGroup getMacroGroup(int location) {

-        return (MacroGroup)macroGroups.get(location);

-    }

-

-

-    public List<Macro>  getMacros() {

-        return macros;

-    }

-

-    public void setMacros(List<Macro> macros) {

-        this.macros = macros;

-    }

-

-    public List<MacroGroup> getMacroGroups() {

-        return macroGroups;

-    }

-

-    public void setMacroGroups(List<MacroGroup> macroGroups) {

-        this.macroGroups = macroGroups;

-    }

-

-    public String getTitle() {

-        return title;

-    }

-

-    public void setTitle(String title) {

-        this.title = title;

-    }

-    

-    public String toXML() {

-    	StringBuilder buf = new StringBuilder();

-    	buf.append("<macrogroup>");

-    	buf.append("<title>" +  getTitle() + "</title>");

-    	buf.append("<macros>");

-    	for (Macro macro : getMacros())

-		{

-    		buf.append("<macro>");

-    		buf.append("<title>" + macro.getTitle() + "</title>");

-    		buf.append("<type>" + macro.getType() + "</type>");

-    		buf.append("<description>" + macro.getDescription() + "</description>");

-    		buf.append("<response>" + macro.getResponse() + "</response>");

-    		buf.append("</macro>");

-		}

-    	buf.append("</macros>");

-    	

-    	if (getMacroGroups().size() > 0) {

-    		buf.append("<macroGroups>");

-    		for (MacroGroup groups : getMacroGroups()) {

-    			buf.append(groups.toXML());

-    		}

-    		buf.append("</macroGroups>");

-    	}

-    	buf.append("</macrogroup>");

-    	return buf.toString(); 

-    }

-}

+/**
+ * $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.smackx.workgroup.ext.macros;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * MacroGroup datamodel.
+ */
+public class MacroGroup {
+    private List<Macro> macros;
+    private List<MacroGroup> macroGroups;
+
+
+    // Define MacroGroup
+    private String title;
+
+    public MacroGroup() {
+        macros = new ArrayList<Macro>();
+        macroGroups = new ArrayList<MacroGroup>();
+    }
+
+    public void addMacro(Macro macro) {
+        macros.add(macro);
+    }
+
+    public void removeMacro(Macro macro) {
+        macros.remove(macro);
+    }
+
+    public Macro getMacroByTitle(String title) {
+        Collection<Macro> col = Collections.unmodifiableList(macros);
+        Iterator<Macro> iter = col.iterator();
+        while (iter.hasNext()) {
+            Macro macro = (Macro)iter.next();
+            if (macro.getTitle().equalsIgnoreCase(title)) {
+                return macro;
+            }
+        }
+        return null;
+    }
+
+    public void addMacroGroup(MacroGroup group) {
+        macroGroups.add(group);
+    }
+
+    public void removeMacroGroup(MacroGroup group) {
+        macroGroups.remove(group);
+    }
+
+    public Macro getMacro(int location) {
+        return (Macro)macros.get(location);
+    }
+
+    public MacroGroup getMacroGroupByTitle(String title) {
+        Collection<MacroGroup> col = Collections.unmodifiableList(macroGroups);
+        Iterator<MacroGroup> iter = col.iterator();
+        while (iter.hasNext()) {
+            MacroGroup group = (MacroGroup)iter.next();
+            if (group.getTitle().equalsIgnoreCase(title)) {
+                return group;
+            }
+        }
+        return null;
+    }
+
+    public MacroGroup getMacroGroup(int location) {
+        return (MacroGroup)macroGroups.get(location);
+    }
+
+
+    public List<Macro>  getMacros() {
+        return macros;
+    }
+
+    public void setMacros(List<Macro> macros) {
+        this.macros = macros;
+    }
+
+    public List<MacroGroup> getMacroGroups() {
+        return macroGroups;
+    }
+
+    public void setMacroGroups(List<MacroGroup> macroGroups) {
+        this.macroGroups = macroGroups;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+    
+    public String toXML() {
+    	StringBuilder buf = new StringBuilder();
+    	buf.append("<macrogroup>");
+    	buf.append("<title>" +  getTitle() + "</title>");
+    	buf.append("<macros>");
+    	for (Macro macro : getMacros())
+		{
+    		buf.append("<macro>");
+    		buf.append("<title>" + macro.getTitle() + "</title>");
+    		buf.append("<type>" + macro.getType() + "</type>");
+    		buf.append("<description>" + macro.getDescription() + "</description>");
+    		buf.append("<response>" + macro.getResponse() + "</response>");
+    		buf.append("</macro>");
+		}
+    	buf.append("</macros>");
+    	
+    	if (getMacroGroups().size() > 0) {
+    		buf.append("<macroGroups>");
+    		for (MacroGroup groups : getMacroGroups()) {
+    			buf.append(groups.toXML());
+    		}
+    		buf.append("</macroGroups>");
+    	}
+    	buf.append("</macrogroup>");
+    	return buf.toString(); 
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java
index b658bf9..c0718c2 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java
@@ -1,198 +1,198 @@
-/**

- * $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.smackx.workgroup.ext.macros;

-

-import java.io.StringReader;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.StringUtils;

-import org.xmlpull.mxp1.MXParser;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Macros iq is responsible for handling global and personal macros in the a Live Assistant

- * Workgroup.

- */

-public class Macros extends IQ {

-

-    private MacroGroup rootGroup;

-    private boolean personal;

-    private MacroGroup personalMacroGroup;

-

-    public MacroGroup getRootGroup() {

-        return rootGroup;

-    }

-

-    public void setRootGroup(MacroGroup rootGroup) {

-        this.rootGroup = rootGroup;

-    }

-

-    public boolean isPersonal() {

-        return personal;

-    }

-

-    public void setPersonal(boolean personal) {

-        this.personal = personal;

-    }

-

-    public MacroGroup getPersonalMacroGroup() {

-        return personalMacroGroup;

-    }

-

-    public void setPersonalMacroGroup(MacroGroup personalMacroGroup) {

-        this.personalMacroGroup = personalMacroGroup;

-    }

-

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "macros";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        if (isPersonal()) {

-            buf.append("<personal>true</personal>");

-        }

-        if (getPersonalMacroGroup() != null) {        	

-        	buf.append("<personalMacro>");

-        	buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML()));

-        	buf.append("</personalMacro>");

-        }

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for Macro packets.

-     *

-     * @author Derek DeMoro

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public InternalProvider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            Macros macroGroup = new Macros();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    if (parser.getName().equals("model")) {

-                        String macros = parser.nextText();

-                        MacroGroup group = parseMacroGroups(macros);

-                        macroGroup.setRootGroup(group);

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return macroGroup;

-        }

-        

-        public Macro parseMacro(XmlPullParser parser) throws Exception {

-        	Macro macro = new Macro();

-        	 boolean done = false;

-            while (!done) {

-	        	int eventType = parser.next();

-	        	if (eventType == XmlPullParser.START_TAG) {

-	        		if (parser.getName().equals("title")) {

-	        			parser.next();

-	        			macro.setTitle(parser.getText());

-	        		}

-	        		else if (parser.getName().equals("description")) {

-	        			macro.setDescription(parser.nextText());

-	        		}

-	        		else if (parser.getName().equals("response")) {

-	        			macro.setResponse(parser.nextText());

-	        		}

-	        		else if (parser.getName().equals("type")) {

-	        			macro.setType(Integer.valueOf(parser.nextText()).intValue());

-	        		}

-	        	}

-	            else if (eventType == XmlPullParser.END_TAG) {

-	                if (parser.getName().equals("macro")) {

-	                    done = true;

-	                }

-	            }

-            }

-        	return macro;

-        }

-        

-        public MacroGroup parseMacroGroup(XmlPullParser parser) throws Exception {

-        	MacroGroup group = new MacroGroup();

-        	

-            boolean done = false;

-            while (!done) {

-	        	int eventType = parser.next();

-	        	if (eventType == XmlPullParser.START_TAG) {

-	        		if (parser.getName().equals("macrogroup")) {

-	        			group.addMacroGroup(parseMacroGroup(parser));

-	        		}

-	        		if (parser.getName().equals("title")) {

-	        			group.setTitle(parser.nextText());

-	        		}

-	        		if (parser.getName().equals("macro")) {

-	        			group.addMacro(parseMacro(parser));

-	        		}

-	        	}

-	            else if (eventType == XmlPullParser.END_TAG) {

-	                if (parser.getName().equals("macrogroup")) {

-	                    done = true;

-	                }

-	            }

-            }

-        	return group; 

-        }

-        

-        public MacroGroup parseMacroGroups(String macros) throws Exception {

-

-        	MacroGroup group = null;

-        	XmlPullParser parser = new MXParser();

-        	parser.setInput(new StringReader(macros));

-			int eventType = parser.getEventType();

-			while (eventType != XmlPullParser.END_DOCUMENT) {		

-				eventType = parser.next();

-				 if (eventType == XmlPullParser.START_TAG) {

-	                    if (parser.getName().equals("macrogroup")) {

-	                    	group = parseMacroGroup(parser);

-	                    }

-				 }

-			}

-			return group;

-        }

-    }

+/**
+ * $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.smackx.workgroup.ext.macros;
+
+import java.io.StringReader;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.StringUtils;
+import org.xmlpull.mxp1.MXParser;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Macros iq is responsible for handling global and personal macros in the a Live Assistant
+ * Workgroup.
+ */
+public class Macros extends IQ {
+
+    private MacroGroup rootGroup;
+    private boolean personal;
+    private MacroGroup personalMacroGroup;
+
+    public MacroGroup getRootGroup() {
+        return rootGroup;
+    }
+
+    public void setRootGroup(MacroGroup rootGroup) {
+        this.rootGroup = rootGroup;
+    }
+
+    public boolean isPersonal() {
+        return personal;
+    }
+
+    public void setPersonal(boolean personal) {
+        this.personal = personal;
+    }
+
+    public MacroGroup getPersonalMacroGroup() {
+        return personalMacroGroup;
+    }
+
+    public void setPersonalMacroGroup(MacroGroup personalMacroGroup) {
+        this.personalMacroGroup = personalMacroGroup;
+    }
+
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "macros";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        if (isPersonal()) {
+            buf.append("<personal>true</personal>");
+        }
+        if (getPersonalMacroGroup() != null) {        	
+        	buf.append("<personalMacro>");
+        	buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML()));
+        	buf.append("</personalMacro>");
+        }
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for Macro packets.
+     *
+     * @author Derek DeMoro
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public InternalProvider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            Macros macroGroup = new Macros();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if (parser.getName().equals("model")) {
+                        String macros = parser.nextText();
+                        MacroGroup group = parseMacroGroups(macros);
+                        macroGroup.setRootGroup(group);
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return macroGroup;
+        }
+        
+        public Macro parseMacro(XmlPullParser parser) throws Exception {
+        	Macro macro = new Macro();
+        	 boolean done = false;
+            while (!done) {
+	        	int eventType = parser.next();
+	        	if (eventType == XmlPullParser.START_TAG) {
+	        		if (parser.getName().equals("title")) {
+	        			parser.next();
+	        			macro.setTitle(parser.getText());
+	        		}
+	        		else if (parser.getName().equals("description")) {
+	        			macro.setDescription(parser.nextText());
+	        		}
+	        		else if (parser.getName().equals("response")) {
+	        			macro.setResponse(parser.nextText());
+	        		}
+	        		else if (parser.getName().equals("type")) {
+	        			macro.setType(Integer.valueOf(parser.nextText()).intValue());
+	        		}
+	        	}
+	            else if (eventType == XmlPullParser.END_TAG) {
+	                if (parser.getName().equals("macro")) {
+	                    done = true;
+	                }
+	            }
+            }
+        	return macro;
+        }
+        
+        public MacroGroup parseMacroGroup(XmlPullParser parser) throws Exception {
+        	MacroGroup group = new MacroGroup();
+        	
+            boolean done = false;
+            while (!done) {
+	        	int eventType = parser.next();
+	        	if (eventType == XmlPullParser.START_TAG) {
+	        		if (parser.getName().equals("macrogroup")) {
+	        			group.addMacroGroup(parseMacroGroup(parser));
+	        		}
+	        		if (parser.getName().equals("title")) {
+	        			group.setTitle(parser.nextText());
+	        		}
+	        		if (parser.getName().equals("macro")) {
+	        			group.addMacro(parseMacro(parser));
+	        		}
+	        	}
+	            else if (eventType == XmlPullParser.END_TAG) {
+	                if (parser.getName().equals("macrogroup")) {
+	                    done = true;
+	                }
+	            }
+            }
+        	return group; 
+        }
+        
+        public MacroGroup parseMacroGroups(String macros) throws Exception {
+
+        	MacroGroup group = null;
+        	XmlPullParser parser = new MXParser();
+        	parser.setInput(new StringReader(macros));
+			int eventType = parser.getEventType();
+			while (eventType != XmlPullParser.END_DOCUMENT) {		
+				eventType = parser.next();
+				 if (eventType == XmlPullParser.START_TAG) {
+	                    if (parser.getName().equals("macrogroup")) {
+	                    	group = parseMacroGroup(parser);
+	                    }
+				 }
+			}
+			return group;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java
index eff3c6c..3540006 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java
@@ -1,155 +1,155 @@
-/**

- * $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.smackx.workgroup.ext.notes;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * IQ packet for retrieving and adding Chat Notes.

- */

-public class ChatNotes extends IQ {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "chat-notes";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-

-    private String sessionID;

-    private String notes;

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public void setSessionID(String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-    public String getNotes() {

-        return notes;

-    }

-

-    public void setNotes(String notes) {

-        this.notes = notes;

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        buf.append("<sessionID>").append(getSessionID()).append("</sessionID>");

-

-        if (getNotes() != null) {

-            buf.append("<notes>").append(getNotes()).append("</notes>");

-        }

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for ChatNotes packets.

-     *

-     * @author Derek DeMoro

-     */

-    public static class Provider implements IQProvider {

-

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            ChatNotes chatNotes = new ChatNotes();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    if (parser.getName().equals("sessionID")) {

-                        chatNotes.setSessionID(parser.nextText());

-                    }

-                    else if (parser.getName().equals("text")) {

-                        String note = parser.nextText();

-                        note = note.replaceAll("\\\\n", "\n");

-                        chatNotes.setNotes(note);

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return chatNotes;

-        }

-    }

-

-    /**

-     * Replaces all instances of oldString with newString in string.

-     *

-     * @param string    the String to search to perform replacements on

-     * @param oldString the String that should be replaced by newString

-     * @param newString the String that will replace all instances of oldString

-     * @return a String will all instances of oldString replaced by newString

-     */

-    public static final String replace(String string, String oldString, String newString) {

-        if (string == null) {

-            return null;

-        }

-        // If the newString is null or zero length, just return the string since there's nothing

-        // to replace.

-        if (newString == null) {

-            return string;

-        }

-        int i = 0;

-        // Make sure that oldString appears at least once before doing any processing.

-        if ((i = string.indexOf(oldString, i)) >= 0) {

-            // Use char []'s, as they are more efficient to deal with.

-            char[] string2 = string.toCharArray();

-            char[] newString2 = newString.toCharArray();

-            int oLength = oldString.length();

-            StringBuilder buf = new StringBuilder(string2.length);

-            buf.append(string2, 0, i).append(newString2);

-            i += oLength;

-            int j = i;

-            // Replace all remaining instances of oldString with newString.

-            while ((i = string.indexOf(oldString, i)) > 0) {

-                buf.append(string2, j, i - j).append(newString2);

-                i += oLength;

-                j = i;

-            }

-            buf.append(string2, j, string2.length - j);

-            return buf.toString();

-        }

-        return string;

-    }

-}

-

-

-

+/**
+ * $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.smackx.workgroup.ext.notes;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * IQ packet for retrieving and adding Chat Notes.
+ */
+public class ChatNotes extends IQ {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "chat-notes";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+
+    private String sessionID;
+    private String notes;
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public void setSessionID(String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+    public String getNotes() {
+        return notes;
+    }
+
+    public void setNotes(String notes) {
+        this.notes = notes;
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        buf.append("<sessionID>").append(getSessionID()).append("</sessionID>");
+
+        if (getNotes() != null) {
+            buf.append("<notes>").append(getNotes()).append("</notes>");
+        }
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for ChatNotes packets.
+     *
+     * @author Derek DeMoro
+     */
+    public static class Provider implements IQProvider {
+
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            ChatNotes chatNotes = new ChatNotes();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if (parser.getName().equals("sessionID")) {
+                        chatNotes.setSessionID(parser.nextText());
+                    }
+                    else if (parser.getName().equals("text")) {
+                        String note = parser.nextText();
+                        note = note.replaceAll("\\\\n", "\n");
+                        chatNotes.setNotes(note);
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return chatNotes;
+        }
+    }
+
+    /**
+     * Replaces all instances of oldString with newString in string.
+     *
+     * @param string    the String to search to perform replacements on
+     * @param oldString the String that should be replaced by newString
+     * @param newString the String that will replace all instances of oldString
+     * @return a String will all instances of oldString replaced by newString
+     */
+    public static final String replace(String string, String oldString, String newString) {
+        if (string == null) {
+            return null;
+        }
+        // If the newString is null or zero length, just return the string since there's nothing
+        // to replace.
+        if (newString == null) {
+            return string;
+        }
+        int i = 0;
+        // Make sure that oldString appears at least once before doing any processing.
+        if ((i = string.indexOf(oldString, i)) >= 0) {
+            // Use char []'s, as they are more efficient to deal with.
+            char[] string2 = string.toCharArray();
+            char[] newString2 = newString.toCharArray();
+            int oLength = oldString.length();
+            StringBuilder buf = new StringBuilder(string2.length);
+            buf.append(string2, 0, i).append(newString2);
+            i += oLength;
+            int j = i;
+            // Replace all remaining instances of oldString with newString.
+            while ((i = string.indexOf(oldString, i)) > 0) {
+                buf.append(string2, j, i - j).append(newString2);
+                i += oLength;
+                j = i;
+            }
+            buf.append(string2, j, string2.length - j);
+            return buf.toString();
+        }
+        return string;
+    }
+}
+
+
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java
index 8b9d230..6502b88 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java
@@ -1,132 +1,132 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * IQ packet for retrieving and changing the Agent personal information.

- */

-public class AgentInfo extends IQ {

-

-    /**

-    * Element name of the packet extension.

-    */

-   public static final String ELEMENT_NAME = "agent-info";

-

-   /**

-    * Namespace of the packet extension.

-    */

-   public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    private String jid;

-    private String name;

-

-    /**

-     * Returns the Agent's jid.

-     *

-     * @return the Agent's jid.

-     */

-    public String getJid() {

-        return jid;

-    }

-

-    /**

-     * Sets the Agent's jid.

-     *

-     * @param jid the jid of the agent.

-     */

-    public void setJid(String jid) {

-        this.jid = jid;

-    }

-

-    /**

-     * Returns the Agent's name. The name of the agent may be different than the user's name.

-     * This property may be shown in the webchat client.

-     *

-     * @return the Agent's name.

-     */

-    public String getName() {

-        return name;

-    }

-

-    /**

-     * Sets the Agent's name. The name of the agent may be different than the user's name.

-     * This property may be shown in the webchat client.

-     *

-     * @param name the new name of the agent.

-     */

-    public void setName(String name) {

-        this.name = name;

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        if (jid != null) {

-            buf.append("<jid>").append(getJid()).append("</jid>");

-        }

-        if (name != null) {

-            buf.append("<name>").append(getName()).append("</name>");

-        }

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for AgentInfo packets.

-     *

-     * @author Gaston Dombiak

-     */

-    public static class Provider implements IQProvider {

-

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            AgentInfo answer = new AgentInfo();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    if (parser.getName().equals("jid")) {

-                        answer.setJid(parser.nextText());

-                    }

-                    else if (parser.getName().equals("name")) {

-                        answer.setName(parser.nextText());

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return answer;

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * IQ packet for retrieving and changing the Agent personal information.
+ */
+public class AgentInfo extends IQ {
+
+    /**
+    * Element name of the packet extension.
+    */
+   public static final String ELEMENT_NAME = "agent-info";
+
+   /**
+    * Namespace of the packet extension.
+    */
+   public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    private String jid;
+    private String name;
+
+    /**
+     * Returns the Agent's jid.
+     *
+     * @return the Agent's jid.
+     */
+    public String getJid() {
+        return jid;
+    }
+
+    /**
+     * Sets the Agent's jid.
+     *
+     * @param jid the jid of the agent.
+     */
+    public void setJid(String jid) {
+        this.jid = jid;
+    }
+
+    /**
+     * Returns the Agent's name. The name of the agent may be different than the user's name.
+     * This property may be shown in the webchat client.
+     *
+     * @return the Agent's name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the Agent's name. The name of the agent may be different than the user's name.
+     * This property may be shown in the webchat client.
+     *
+     * @param name the new name of the agent.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        if (jid != null) {
+            buf.append("<jid>").append(getJid()).append("</jid>");
+        }
+        if (name != null) {
+            buf.append("<name>").append(getName()).append("</name>");
+        }
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for AgentInfo packets.
+     *
+     * @author Gaston Dombiak
+     */
+    public static class Provider implements IQProvider {
+
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            AgentInfo answer = new AgentInfo();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if (parser.getName().equals("jid")) {
+                        answer.setJid(parser.nextText());
+                    }
+                    else if (parser.getName().equals("name")) {
+                        answer.setName(parser.nextText());
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return answer;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java
index 9f49033..f84fe5c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java
@@ -1,266 +1,266 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.text.ParseException;

-import java.text.SimpleDateFormat;

-import java.util.*;

-

-/**

- * Agent status packet.

- *

- * @author Matt Tucker

- */

-public class AgentStatus implements PacketExtension {

-

-    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");

-

-    static {

-        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));

-    }

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "agent-status";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private String workgroupJID;

-    private List<ChatInfo> currentChats = new ArrayList<ChatInfo>();

-    private int maxChats = -1;

-

-    AgentStatus() {

-    }

-

-    public String getWorkgroupJID() {

-        return workgroupJID;

-    }

-

-    /**

-     * Returns a collection of ChatInfo where each ChatInfo represents a Chat where this agent

-     * is participating.

-     *

-     * @return a collection of ChatInfo where each ChatInfo represents a Chat where this agent

-     *         is participating.

-     */

-    public List<ChatInfo> getCurrentChats() {

-        return Collections.unmodifiableList(currentChats);

-    }

-

-    public int getMaxChats() {

-        return maxChats;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\"");

-        if (workgroupJID != null) {

-            buf.append(" jid=\"").append(workgroupJID).append("\"");

-        }

-        buf.append(">");

-        if (maxChats != -1) {

-            buf.append("<max-chats>").append(maxChats).append("</max-chats>");

-        }

-        if (!currentChats.isEmpty()) {

-            buf.append("<current-chats xmlns= \"http://jivesoftware.com/protocol/workgroup\">");

-            for (Iterator<ChatInfo> it = currentChats.iterator(); it.hasNext();) {

-                buf.append(((ChatInfo)it.next()).toXML());

-            }

-            buf.append("</current-chats>");

-        }

-        buf.append("</").append(this.getElementName()).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * Represents information about a Chat where this Agent is participating.

-     *

-     * @author Gaston Dombiak

-     */

-    public static class ChatInfo {

-

-        private String sessionID;

-        private String userID;

-        private Date date;

-        private String email;

-        private String username;

-        private String question;

-

-        public ChatInfo(String sessionID, String userID, Date date, String email, String username, String question) {

-            this.sessionID = sessionID;

-            this.userID = userID;

-            this.date = date;

-            this.email = email;

-            this.username = username;

-            this.question = question;

-        }

-

-        /**

-         * Returns the sessionID associated to this chat. Each chat will have a unique sessionID

-         * that could be used for retrieving the whole transcript of the conversation.

-         *

-         * @return the sessionID associated to this chat.

-         */

-        public String getSessionID() {

-            return sessionID;

-        }

-

-        /**

-         * Returns the user unique identification of the user that made the initial request and

-         * for which this chat was generated. If the user joined using an anonymous connection

-         * then the userID will be the value of the ID attribute of the USER element. Otherwise,

-         * the userID will be the bare JID of the user that made the request.

-         *

-         * @return the user unique identification of the user that made the initial request.

-         */

-        public String getUserID() {

-            return userID;

-        }

-

-        /**

-         * Returns the date when this agent joined the chat.

-         *

-         * @return the date when this agent joined the chat.

-         */

-        public Date getDate() {

-            return date;

-        }

-

-        /**

-         * Returns the email address associated with the user.

-         *

-         * @return the email address associated with the user.

-         */

-        public String getEmail() {

-            return email;

-        }

-

-        /**

-         * Returns the username(nickname) associated with the user.

-         *

-         * @return the username associated with the user.

-         */

-        public String getUsername() {

-            return username;

-        }

-

-        /**

-         * Returns the question the user asked.

-         *

-         * @return the question the user asked, if any.

-         */

-        public String getQuestion() {

-            return question;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<chat ");

-            if (sessionID != null) {

-                buf.append(" sessionID=\"").append(sessionID).append("\"");

-            }

-            if (userID != null) {

-                buf.append(" userID=\"").append(userID).append("\"");

-            }

-            if (date != null) {

-                buf.append(" startTime=\"").append(UTC_FORMAT.format(date)).append("\"");

-            }

-            if (email != null) {

-                buf.append(" email=\"").append(email).append("\"");

-            }

-            if (username != null) {

-                buf.append(" username=\"").append(username).append("\"");

-            }

-            if (question != null) {

-                buf.append(" question=\"").append(question).append("\"");

-            }

-            buf.append("/>");

-

-            return buf.toString();

-        }

-    }

-

-    /**

-     * Packet extension provider for AgentStatus packets.

-     */

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            AgentStatus agentStatus = new AgentStatus();

-

-            agentStatus.workgroupJID = parser.getAttributeValue("", "jid");

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-

-                if (eventType == XmlPullParser.START_TAG) {

-                    if ("chat".equals(parser.getName())) {

-                        agentStatus.currentChats.add(parseChatInfo(parser));

-                    }

-                    else if ("max-chats".equals(parser.getName())) {

-                        agentStatus.maxChats = Integer.parseInt(parser.nextText());

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG &&

-                    ELEMENT_NAME.equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return agentStatus;

-        }

-

-        private ChatInfo parseChatInfo(XmlPullParser parser) {

-

-            String sessionID = parser.getAttributeValue("", "sessionID");

-            String userID = parser.getAttributeValue("", "userID");

-            Date date = null;

-            try {

-                date = UTC_FORMAT.parse(parser.getAttributeValue("", "startTime"));

-            }

-            catch (ParseException e) {

-            }

-

-            String email = parser.getAttributeValue("", "email");

-            String username = parser.getAttributeValue("", "username");

-            String question = parser.getAttributeValue("", "question");

-

-            return new ChatInfo(sessionID, userID, date, email, username, question);

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Agent status packet.
+ *
+ * @author Matt Tucker
+ */
+public class AgentStatus implements PacketExtension {
+
+    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+
+    static {
+        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
+    }
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "agent-status";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private String workgroupJID;
+    private List<ChatInfo> currentChats = new ArrayList<ChatInfo>();
+    private int maxChats = -1;
+
+    AgentStatus() {
+    }
+
+    public String getWorkgroupJID() {
+        return workgroupJID;
+    }
+
+    /**
+     * Returns a collection of ChatInfo where each ChatInfo represents a Chat where this agent
+     * is participating.
+     *
+     * @return a collection of ChatInfo where each ChatInfo represents a Chat where this agent
+     *         is participating.
+     */
+    public List<ChatInfo> getCurrentChats() {
+        return Collections.unmodifiableList(currentChats);
+    }
+
+    public int getMaxChats() {
+        return maxChats;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\"");
+        if (workgroupJID != null) {
+            buf.append(" jid=\"").append(workgroupJID).append("\"");
+        }
+        buf.append(">");
+        if (maxChats != -1) {
+            buf.append("<max-chats>").append(maxChats).append("</max-chats>");
+        }
+        if (!currentChats.isEmpty()) {
+            buf.append("<current-chats xmlns= \"http://jivesoftware.com/protocol/workgroup\">");
+            for (Iterator<ChatInfo> it = currentChats.iterator(); it.hasNext();) {
+                buf.append(((ChatInfo)it.next()).toXML());
+            }
+            buf.append("</current-chats>");
+        }
+        buf.append("</").append(this.getElementName()).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * Represents information about a Chat where this Agent is participating.
+     *
+     * @author Gaston Dombiak
+     */
+    public static class ChatInfo {
+
+        private String sessionID;
+        private String userID;
+        private Date date;
+        private String email;
+        private String username;
+        private String question;
+
+        public ChatInfo(String sessionID, String userID, Date date, String email, String username, String question) {
+            this.sessionID = sessionID;
+            this.userID = userID;
+            this.date = date;
+            this.email = email;
+            this.username = username;
+            this.question = question;
+        }
+
+        /**
+         * Returns the sessionID associated to this chat. Each chat will have a unique sessionID
+         * that could be used for retrieving the whole transcript of the conversation.
+         *
+         * @return the sessionID associated to this chat.
+         */
+        public String getSessionID() {
+            return sessionID;
+        }
+
+        /**
+         * Returns the user unique identification of the user that made the initial request and
+         * for which this chat was generated. If the user joined using an anonymous connection
+         * then the userID will be the value of the ID attribute of the USER element. Otherwise,
+         * the userID will be the bare JID of the user that made the request.
+         *
+         * @return the user unique identification of the user that made the initial request.
+         */
+        public String getUserID() {
+            return userID;
+        }
+
+        /**
+         * Returns the date when this agent joined the chat.
+         *
+         * @return the date when this agent joined the chat.
+         */
+        public Date getDate() {
+            return date;
+        }
+
+        /**
+         * Returns the email address associated with the user.
+         *
+         * @return the email address associated with the user.
+         */
+        public String getEmail() {
+            return email;
+        }
+
+        /**
+         * Returns the username(nickname) associated with the user.
+         *
+         * @return the username associated with the user.
+         */
+        public String getUsername() {
+            return username;
+        }
+
+        /**
+         * Returns the question the user asked.
+         *
+         * @return the question the user asked, if any.
+         */
+        public String getQuestion() {
+            return question;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<chat ");
+            if (sessionID != null) {
+                buf.append(" sessionID=\"").append(sessionID).append("\"");
+            }
+            if (userID != null) {
+                buf.append(" userID=\"").append(userID).append("\"");
+            }
+            if (date != null) {
+                buf.append(" startTime=\"").append(UTC_FORMAT.format(date)).append("\"");
+            }
+            if (email != null) {
+                buf.append(" email=\"").append(email).append("\"");
+            }
+            if (username != null) {
+                buf.append(" username=\"").append(username).append("\"");
+            }
+            if (question != null) {
+                buf.append(" question=\"").append(question).append("\"");
+            }
+            buf.append("/>");
+
+            return buf.toString();
+        }
+    }
+
+    /**
+     * Packet extension provider for AgentStatus packets.
+     */
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            AgentStatus agentStatus = new AgentStatus();
+
+            agentStatus.workgroupJID = parser.getAttributeValue("", "jid");
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+
+                if (eventType == XmlPullParser.START_TAG) {
+                    if ("chat".equals(parser.getName())) {
+                        agentStatus.currentChats.add(parseChatInfo(parser));
+                    }
+                    else if ("max-chats".equals(parser.getName())) {
+                        agentStatus.maxChats = Integer.parseInt(parser.nextText());
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG &&
+                    ELEMENT_NAME.equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return agentStatus;
+        }
+
+        private ChatInfo parseChatInfo(XmlPullParser parser) {
+
+            String sessionID = parser.getAttributeValue("", "sessionID");
+            String userID = parser.getAttributeValue("", "userID");
+            Date date = null;
+            try {
+                date = UTC_FORMAT.parse(parser.getAttributeValue("", "startTime"));
+            }
+            catch (ParseException e) {
+            }
+
+            String email = parser.getAttributeValue("", "email");
+            String username = parser.getAttributeValue("", "username");
+            String question = parser.getAttributeValue("", "question");
+
+            return new ChatInfo(sessionID, userID, date, email, username, question);
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java
index 48549d2..db6626c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java
@@ -1,163 +1,163 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.Collections;

-import java.util.HashSet;

-import java.util.Iterator;

-import java.util.Set;

-

-/**

- * Agent status request packet. This packet is used by agents to request the list of

- * agents in a workgroup. The response packet contains a list of packets. Presence

- * packets from individual agents follow.

- *

- * @author Matt Tucker

- */

-public class AgentStatusRequest extends IQ {

-

-     /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "agent-status-request";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private Set<Item> agents;

-

-    public AgentStatusRequest() {

-        agents = new HashSet<Item>();

-    }

-

-    public int getAgentCount() {

-        return agents.size();

-    }

-

-    public Set<Item> getAgents() {

-        return Collections.unmodifiableSet(agents);

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        synchronized (agents) {

-            for (Iterator<Item> i=agents.iterator(); i.hasNext(); ) {

-                Item item = (Item) i.next();

-                buf.append("<agent jid=\"").append(item.getJID()).append("\">");

-                if (item.getName() != null) {

-                    buf.append("<name xmlns=\""+ AgentInfo.NAMESPACE + "\">");

-                    buf.append(item.getName());

-                    buf.append("</name>");

-                }

-                buf.append("</agent>");

-            }

-        }

-        buf.append("</").append(this.getElementName()).append("> ");

-        return buf.toString();

-    }

-

-    public static class Item {

-

-        private String jid;

-        private String type;

-        private String name;

-

-        public Item(String jid, String type, String name) {

-            this.jid = jid;

-            this.type = type;

-            this.name = name;

-        }

-

-        public String getJID() {

-            return jid;

-        }

-

-        public String getType() {

-            return type;

-        }

-

-        public String getName() {

-            return name;

-        }

-    }

-

-    /**

-     * Packet extension provider for AgentStatusRequest packets.

-     */

-    public static class Provider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            AgentStatusRequest statusRequest = new AgentStatusRequest();

-

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("agent".equals(parser.getName()))) {

-                    statusRequest.agents.add(parseAgent(parser));

-                }

-                else if (eventType == XmlPullParser.END_TAG &&

-                        "agent-status-request".equals(parser.getName()))

-                {

-                    done = true;

-                }

-            }

-            return statusRequest;

-        }

-

-        private Item parseAgent(XmlPullParser parser) throws Exception {

-

-            boolean done = false;

-            String jid = parser.getAttributeValue("", "jid");

-            String type = parser.getAttributeValue("", "type");

-            String name = null;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("name".equals(parser.getName()))) {

-                    name = parser.nextText();

-                }

-                else if (eventType == XmlPullParser.END_TAG &&

-                        "agent".equals(parser.getName()))

-                {

-                    done = true;

-                }

-            }

-            return new Item(jid, type, name);

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Agent status request packet. This packet is used by agents to request the list of
+ * agents in a workgroup. The response packet contains a list of packets. Presence
+ * packets from individual agents follow.
+ *
+ * @author Matt Tucker
+ */
+public class AgentStatusRequest extends IQ {
+
+     /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "agent-status-request";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private Set<Item> agents;
+
+    public AgentStatusRequest() {
+        agents = new HashSet<Item>();
+    }
+
+    public int getAgentCount() {
+        return agents.size();
+    }
+
+    public Set<Item> getAgents() {
+        return Collections.unmodifiableSet(agents);
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        synchronized (agents) {
+            for (Iterator<Item> i=agents.iterator(); i.hasNext(); ) {
+                Item item = (Item) i.next();
+                buf.append("<agent jid=\"").append(item.getJID()).append("\">");
+                if (item.getName() != null) {
+                    buf.append("<name xmlns=\""+ AgentInfo.NAMESPACE + "\">");
+                    buf.append(item.getName());
+                    buf.append("</name>");
+                }
+                buf.append("</agent>");
+            }
+        }
+        buf.append("</").append(this.getElementName()).append("> ");
+        return buf.toString();
+    }
+
+    public static class Item {
+
+        private String jid;
+        private String type;
+        private String name;
+
+        public Item(String jid, String type, String name) {
+            this.jid = jid;
+            this.type = type;
+            this.name = name;
+        }
+
+        public String getJID() {
+            return jid;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    /**
+     * Packet extension provider for AgentStatusRequest packets.
+     */
+    public static class Provider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            AgentStatusRequest statusRequest = new AgentStatusRequest();
+
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("agent".equals(parser.getName()))) {
+                    statusRequest.agents.add(parseAgent(parser));
+                }
+                else if (eventType == XmlPullParser.END_TAG &&
+                        "agent-status-request".equals(parser.getName()))
+                {
+                    done = true;
+                }
+            }
+            return statusRequest;
+        }
+
+        private Item parseAgent(XmlPullParser parser) throws Exception {
+
+            boolean done = false;
+            String jid = parser.getAttributeValue("", "jid");
+            String type = parser.getAttributeValue("", "type");
+            String name = null;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("name".equals(parser.getName()))) {
+                    name = parser.nextText();
+                }
+                else if (eventType == XmlPullParser.END_TAG &&
+                        "agent".equals(parser.getName()))
+                {
+                    done = true;
+                }
+            }
+            return new Item(jid, type, name);
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java
index 292a640..29fa09a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java
@@ -1,129 +1,129 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * Represents a request for getting the jid of the workgroups where an agent can work or could

- * represent the result of such request which will contain the list of workgroups JIDs where the

- * agent can work.

- *

- * @author Gaston Dombiak

- */

-public class AgentWorkgroups extends IQ {

-

-    private String agentJID;

-    private List<String> workgroups;

-

-    /**

-     * Creates an AgentWorkgroups request for the given agent. This IQ will be sent and an answer

-     * will be received with the jid of the workgroups where the agent can work.

-     *

-     * @param agentJID the id of the agent to get his workgroups.

-     */

-    public AgentWorkgroups(String agentJID) {

-        this.agentJID = agentJID;

-        this.workgroups = new ArrayList<String>();

-    }

-

-    /**

-     * Creates an AgentWorkgroups which will contain the JIDs of the workgroups where an agent can

-     * work.

-     *

-     * @param agentJID the id of the agent that can work in the list of workgroups.

-     * @param workgroups the list of workgroup JIDs where the agent can work.

-     */

-    public AgentWorkgroups(String agentJID, List<String> workgroups) {

-        this.agentJID = agentJID;

-        this.workgroups = workgroups;

-    }

-

-    public String getAgentJID() {

-        return agentJID;

-    }

-

-    /**

-     * Returns a list of workgroup JIDs where the agent can work.

-     *

-     * @return a list of workgroup JIDs where the agent can work.

-     */

-    public List<String> getWorkgroups() {

-        return Collections.unmodifiableList(workgroups);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<workgroups xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"")

-                .append(agentJID)

-                .append("\">");

-

-        for (Iterator<String> it=workgroups.iterator(); it.hasNext();) {

-            String workgroupJID = it.next();

-            buf.append("<workgroup jid=\"" + workgroupJID + "\"/>");

-        }

-

-        buf.append("</workgroups>");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for AgentWorkgroups packets.

-     *

-     * @author Gaston Dombiak

-     */

-    public static class Provider implements IQProvider {

-

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            String agentJID = parser.getAttributeValue("", "jid");

-            List<String> workgroups = new ArrayList<String>();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    if (parser.getName().equals("workgroup")) {

-                        workgroups.add(parser.getAttributeValue("", "jid"));

-                    }

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals("workgroups")) {

-                        done = true;

-                    }

-                }

-            }

-

-            return new AgentWorkgroups(agentJID, workgroups);

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents a request for getting the jid of the workgroups where an agent can work or could
+ * represent the result of such request which will contain the list of workgroups JIDs where the
+ * agent can work.
+ *
+ * @author Gaston Dombiak
+ */
+public class AgentWorkgroups extends IQ {
+
+    private String agentJID;
+    private List<String> workgroups;
+
+    /**
+     * Creates an AgentWorkgroups request for the given agent. This IQ will be sent and an answer
+     * will be received with the jid of the workgroups where the agent can work.
+     *
+     * @param agentJID the id of the agent to get his workgroups.
+     */
+    public AgentWorkgroups(String agentJID) {
+        this.agentJID = agentJID;
+        this.workgroups = new ArrayList<String>();
+    }
+
+    /**
+     * Creates an AgentWorkgroups which will contain the JIDs of the workgroups where an agent can
+     * work.
+     *
+     * @param agentJID the id of the agent that can work in the list of workgroups.
+     * @param workgroups the list of workgroup JIDs where the agent can work.
+     */
+    public AgentWorkgroups(String agentJID, List<String> workgroups) {
+        this.agentJID = agentJID;
+        this.workgroups = workgroups;
+    }
+
+    public String getAgentJID() {
+        return agentJID;
+    }
+
+    /**
+     * Returns a list of workgroup JIDs where the agent can work.
+     *
+     * @return a list of workgroup JIDs where the agent can work.
+     */
+    public List<String> getWorkgroups() {
+        return Collections.unmodifiableList(workgroups);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<workgroups xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"")
+                .append(agentJID)
+                .append("\">");
+
+        for (Iterator<String> it=workgroups.iterator(); it.hasNext();) {
+            String workgroupJID = it.next();
+            buf.append("<workgroup jid=\"" + workgroupJID + "\"/>");
+        }
+
+        buf.append("</workgroups>");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for AgentWorkgroups packets.
+     *
+     * @author Gaston Dombiak
+     */
+    public static class Provider implements IQProvider {
+
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            String agentJID = parser.getAttributeValue("", "jid");
+            List<String> workgroups = new ArrayList<String>();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    if (parser.getName().equals("workgroup")) {
+                        workgroups.add(parser.getAttributeValue("", "jid"));
+                    }
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals("workgroups")) {
+                        done = true;
+                    }
+                }
+            }
+
+            return new AgentWorkgroups(agentJID, workgroups);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java
index 620291c..d9db5e1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java
@@ -1,75 +1,75 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-

-/**

- * A IQ packet used to depart a workgroup queue. There are two cases for issuing a depart

- * queue request:<ul>

- *     <li>The user wants to leave the queue. In this case, an instance of this class

- *         should be created without passing in a user address.

- *     <li>An administrator or the server removes wants to remove a user from the queue.

- *         In that case, the address of the user to remove from the queue should be

- *         used to create an instance of this class.</ul>

- *

- * @author loki der quaeler

- */

-public class DepartQueuePacket extends IQ {

-

-    private String user;

-

-    /**

-     * Creates a depart queue request packet to the specified workgroup.

-     *

-     * @param workgroup the workgroup to depart.

-     */

-    public DepartQueuePacket(String workgroup) {

-        this(workgroup, null);

-    }

-

-    /**

-     * Creates a depart queue request to the specified workgroup and for the

-     * specified user.

-     *

-     * @param workgroup the workgroup to depart.

-     * @param user the user to make depart from the queue.

-     */

-    public DepartQueuePacket(String workgroup, String user) {

-        this.user = user;

-

-        setTo(workgroup);

-        setType(IQ.Type.SET);

-        setFrom(user);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder("<depart-queue xmlns=\"http://jabber.org/protocol/workgroup\"");

-

-        if (this.user != null) {

-            buf.append("><jid>").append(this.user).append("</jid></depart-queue>");

-        }

-        else {

-            buf.append("/>");

-        }

-

-        return buf.toString();

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+
+/**
+ * A IQ packet used to depart a workgroup queue. There are two cases for issuing a depart
+ * queue request:<ul>
+ *     <li>The user wants to leave the queue. In this case, an instance of this class
+ *         should be created without passing in a user address.
+ *     <li>An administrator or the server removes wants to remove a user from the queue.
+ *         In that case, the address of the user to remove from the queue should be
+ *         used to create an instance of this class.</ul>
+ *
+ * @author loki der quaeler
+ */
+public class DepartQueuePacket extends IQ {
+
+    private String user;
+
+    /**
+     * Creates a depart queue request packet to the specified workgroup.
+     *
+     * @param workgroup the workgroup to depart.
+     */
+    public DepartQueuePacket(String workgroup) {
+        this(workgroup, null);
+    }
+
+    /**
+     * Creates a depart queue request to the specified workgroup and for the
+     * specified user.
+     *
+     * @param workgroup the workgroup to depart.
+     * @param user the user to make depart from the queue.
+     */
+    public DepartQueuePacket(String workgroup, String user) {
+        this.user = user;
+
+        setTo(workgroup);
+        setType(IQ.Type.SET);
+        setFrom(user);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder("<depart-queue xmlns=\"http://jabber.org/protocol/workgroup\"");
+
+        if (this.user != null) {
+            buf.append("><jid>").append(this.user).append("</jid></depart-queue>");
+        }
+        else {
+            buf.append("/>");
+        }
+
+        return buf.toString();
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java
index af76986..a326212 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java
@@ -1,49 +1,49 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import java.util.List;

-import java.util.Map;

-

-import org.jivesoftware.smackx.workgroup.MetaData;

-import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * This provider parses meta data if it's not contained already in a larger extension provider.

- *

- * @author loki der quaeler

- */

-public class MetaDataProvider implements PacketExtensionProvider {

-

-    /**

-     * PacketExtensionProvider implementation

-     */

-    public PacketExtension parseExtension (XmlPullParser parser)

-        throws Exception {

-        Map<String, List<String>> metaData = MetaDataUtils.parseMetaData(parser);

-

-        return new MetaData(metaData);

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jivesoftware.smackx.workgroup.MetaData;
+import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * This provider parses meta data if it's not contained already in a larger extension provider.
+ *
+ * @author loki der quaeler
+ */
+public class MetaDataProvider implements PacketExtensionProvider {
+
+    /**
+     * PacketExtensionProvider implementation
+     */
+    public PacketExtension parseExtension (XmlPullParser parser)
+        throws Exception {
+        Map<String, List<String>> metaData = MetaDataUtils.parseMetaData(parser);
+
+        return new MetaData(metaData);
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java
index 0f80866..b577bab 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java
@@ -1,173 +1,173 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.text.SimpleDateFormat;

-import java.util.*;

-

-/**

- * Packet used for requesting information about occupants of a room or for retrieving information

- * such information.

- *

- * @author Gaston Dombiak

- */

-public class OccupantsInfo extends IQ {

-

-    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");

-

-    static {

-        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));

-    }

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "occupants-info";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    private String roomID;

-    private final Set<OccupantInfo> occupants;

-

-    public OccupantsInfo(String roomID) {

-        this.roomID = roomID;

-        this.occupants = new HashSet<OccupantInfo>();

-    }

-

-    public String getRoomID() {

-        return roomID;

-    }

-

-    public int getOccupantsCount() {

-        return occupants.size();

-    }

-

-    public Set<OccupantInfo> getOccupants() {

-        return Collections.unmodifiableSet(occupants);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);

-        buf.append("\" roomID=\"").append(roomID).append("\">");

-        synchronized (occupants) {

-            for (OccupantInfo occupant : occupants) {

-                buf.append("<occupant>");

-                // Add the occupant jid

-                buf.append("<jid>");

-                buf.append(occupant.getJID());

-                buf.append("</jid>");

-                // Add the occupant nickname

-                buf.append("<name>");

-                buf.append(occupant.getNickname());

-                buf.append("</name>");

-                // Add the date when the occupant joined the room

-                buf.append("<joined>");

-                buf.append(UTC_FORMAT.format(occupant.getJoined()));

-                buf.append("</joined>");

-                buf.append("</occupant>");

-            }

-        }

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-        return buf.toString();

-    }

-

-    public static class OccupantInfo {

-

-        private String jid;

-        private String nickname;

-        private Date joined;

-

-        public OccupantInfo(String jid, String nickname, Date joined) {

-            this.jid = jid;

-            this.nickname = nickname;

-            this.joined = joined;

-        }

-

-        public String getJID() {

-            return jid;

-        }

-

-        public String getNickname() {

-            return nickname;

-        }

-

-        public Date getJoined() {

-            return joined;

-        }

-    }

-

-    /**

-     * Packet extension provider for AgentStatusRequest packets.

-     */

-    public static class Provider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-            OccupantsInfo occupantsInfo = new OccupantsInfo(parser.getAttributeValue("", "roomID"));

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) &&

-                        ("occupant".equals(parser.getName()))) {

-                    occupantsInfo.occupants.add(parseOccupantInfo(parser));

-                } else if (eventType == XmlPullParser.END_TAG &&

-                        ELEMENT_NAME.equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return occupantsInfo;

-        }

-

-        private OccupantInfo parseOccupantInfo(XmlPullParser parser) throws Exception {

-

-            boolean done = false;

-            String jid = null;

-            String nickname = null;

-            Date joined = null;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("jid".equals(parser.getName()))) {

-                    jid = parser.nextText();

-                } else if ((eventType == XmlPullParser.START_TAG) &&

-                        ("nickname".equals(parser.getName()))) {

-                    nickname = parser.nextText();

-                } else if ((eventType == XmlPullParser.START_TAG) &&

-                        ("joined".equals(parser.getName()))) {

-                    joined = UTC_FORMAT.parse(parser.nextText());

-                } else if (eventType == XmlPullParser.END_TAG &&

-                        "occupant".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return new OccupantInfo(jid, nickname, joined);

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Packet used for requesting information about occupants of a room or for retrieving information
+ * such information.
+ *
+ * @author Gaston Dombiak
+ */
+public class OccupantsInfo extends IQ {
+
+    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+
+    static {
+        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
+    }
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "occupants-info";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    private String roomID;
+    private final Set<OccupantInfo> occupants;
+
+    public OccupantsInfo(String roomID) {
+        this.roomID = roomID;
+        this.occupants = new HashSet<OccupantInfo>();
+    }
+
+    public String getRoomID() {
+        return roomID;
+    }
+
+    public int getOccupantsCount() {
+        return occupants.size();
+    }
+
+    public Set<OccupantInfo> getOccupants() {
+        return Collections.unmodifiableSet(occupants);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);
+        buf.append("\" roomID=\"").append(roomID).append("\">");
+        synchronized (occupants) {
+            for (OccupantInfo occupant : occupants) {
+                buf.append("<occupant>");
+                // Add the occupant jid
+                buf.append("<jid>");
+                buf.append(occupant.getJID());
+                buf.append("</jid>");
+                // Add the occupant nickname
+                buf.append("<name>");
+                buf.append(occupant.getNickname());
+                buf.append("</name>");
+                // Add the date when the occupant joined the room
+                buf.append("<joined>");
+                buf.append(UTC_FORMAT.format(occupant.getJoined()));
+                buf.append("</joined>");
+                buf.append("</occupant>");
+            }
+        }
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+        return buf.toString();
+    }
+
+    public static class OccupantInfo {
+
+        private String jid;
+        private String nickname;
+        private Date joined;
+
+        public OccupantInfo(String jid, String nickname, Date joined) {
+            this.jid = jid;
+            this.nickname = nickname;
+            this.joined = joined;
+        }
+
+        public String getJID() {
+            return jid;
+        }
+
+        public String getNickname() {
+            return nickname;
+        }
+
+        public Date getJoined() {
+            return joined;
+        }
+    }
+
+    /**
+     * Packet extension provider for AgentStatusRequest packets.
+     */
+    public static class Provider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+            OccupantsInfo occupantsInfo = new OccupantsInfo(parser.getAttributeValue("", "roomID"));
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) &&
+                        ("occupant".equals(parser.getName()))) {
+                    occupantsInfo.occupants.add(parseOccupantInfo(parser));
+                } else if (eventType == XmlPullParser.END_TAG &&
+                        ELEMENT_NAME.equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return occupantsInfo;
+        }
+
+        private OccupantInfo parseOccupantInfo(XmlPullParser parser) throws Exception {
+
+            boolean done = false;
+            String jid = null;
+            String nickname = null;
+            Date joined = null;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("jid".equals(parser.getName()))) {
+                    jid = parser.nextText();
+                } else if ((eventType == XmlPullParser.START_TAG) &&
+                        ("nickname".equals(parser.getName()))) {
+                    nickname = parser.nextText();
+                } else if ((eventType == XmlPullParser.START_TAG) &&
+                        ("joined".equals(parser.getName()))) {
+                    joined = UTC_FORMAT.parse(parser.nextText());
+                } else if (eventType == XmlPullParser.END_TAG &&
+                        "occupant".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return new OccupantInfo(jid, nickname, joined);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java
index 8f56b78..c7d245a 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java
@@ -1,211 +1,211 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smackx.workgroup.MetaData;

-import org.jivesoftware.smackx.workgroup.agent.InvitationRequest;

-import org.jivesoftware.smackx.workgroup.agent.OfferContent;

-import org.jivesoftware.smackx.workgroup.agent.TransferRequest;

-import org.jivesoftware.smackx.workgroup.agent.UserRequest;

-import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.HashMap;

-import java.util.List;

-import java.util.Map;

-

-/**

- * An IQProvider for agent offer requests.

- *

- * @author loki der quaeler

- */

-public class OfferRequestProvider implements IQProvider {

-

-    public OfferRequestProvider() {

-    }

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        int eventType = parser.getEventType();

-        String sessionID = null;

-        int timeout = -1;

-        OfferContent content = null;

-        boolean done = false;

-        Map<String, List<String>> metaData = new HashMap<String, List<String>>();

-

-        if (eventType != XmlPullParser.START_TAG) {

-            // throw exception

-        }

-

-        String userJID = parser.getAttributeValue("", "jid");

-        // Default userID to the JID.

-        String userID = userJID;

-

-        while (!done) {

-            eventType = parser.next();

-

-            if (eventType == XmlPullParser.START_TAG) {

-                String elemName = parser.getName();

-

-                if ("timeout".equals(elemName)) {

-                    timeout = Integer.parseInt(parser.nextText());

-                }

-                else if (MetaData.ELEMENT_NAME.equals(elemName)) {

-                    metaData = MetaDataUtils.parseMetaData(parser);

-                }

-                else if (SessionID.ELEMENT_NAME.equals(elemName)) {

-                   sessionID = parser.getAttributeValue("", "id");

-                }

-                else if (UserID.ELEMENT_NAME.equals(elemName)) {

-                    userID = parser.getAttributeValue("", "id");

-                }

-                else if ("user-request".equals(elemName)) {

-                    content = UserRequest.getInstance();

-                }

-                else if (RoomInvitation.ELEMENT_NAME.equals(elemName)) {

-                    RoomInvitation invitation = (RoomInvitation) PacketParserUtils

-                            .parsePacketExtension(RoomInvitation.ELEMENT_NAME, RoomInvitation.NAMESPACE, parser);

-                    content = new InvitationRequest(invitation.getInviter(), invitation.getRoom(),

-                            invitation.getReason());

-                }

-                else if (RoomTransfer.ELEMENT_NAME.equals(elemName)) {

-                    RoomTransfer transfer = (RoomTransfer) PacketParserUtils

-                            .parsePacketExtension(RoomTransfer.ELEMENT_NAME, RoomTransfer.NAMESPACE, parser);

-                    content = new TransferRequest(transfer.getInviter(), transfer.getRoom(), transfer.getReason());

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if ("offer".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-        }

-

-        OfferRequestPacket offerRequest =

-                new OfferRequestPacket(userJID, userID, timeout, metaData, sessionID, content);

-        offerRequest.setType(IQ.Type.SET);

-

-        return offerRequest;

-    }

-

-    public static class OfferRequestPacket extends IQ {

-

-        private int timeout;

-        private String userID;

-        private String userJID;

-        private Map<String, List<String>> metaData;

-        private String sessionID;

-        private OfferContent content;

-

-        public OfferRequestPacket(String userJID, String userID, int timeout, Map<String, List<String>> metaData,

-                String sessionID, OfferContent content)

-        {

-            this.userJID = userJID;

-            this.userID = userID;

-            this.timeout = timeout;

-            this.metaData = metaData;

-            this.sessionID = sessionID;

-            this.content = content;

-        }

-

-        /**

-         * Returns the userID, which is either the same as the userJID or a special

-         * value that the user provided as part of their "join queue" request.

-         *

-         * @return the user ID.

-         */

-        public String getUserID() {

-            return userID;

-        }

-

-        /**

-         * The JID of the user that made the "join queue" request.

-         *

-         * @return the user JID.

-         */

-        public String getUserJID() {

-            return userJID;

-        }

-

-        /**

-         * Returns the session ID associated with the request and ensuing chat. If the offer

-         * does not contain a session ID, <tt>null</tt> will be returned.

-         *

-         * @return the session id associated with the request.

-         */

-        public String getSessionID() {

-            return sessionID;

-        }

-

-        /**

-         * Returns the number of seconds the agent has to accept the offer before

-         * it times out.

-         *

-         * @return the offer timeout (in seconds).

-         */

-        public int getTimeout() {

-            return this.timeout;

-        }

-

-        public OfferContent getContent() {

-            return content;

-        }

-

-        /**

-         * Returns any meta-data associated with the offer.

-         *

-         * @return meta-data associated with the offer.

-         */

-        public Map<String, List<String>> getMetaData() {

-            return this.metaData;

-        }

-

-        public String getChildElementXML () {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<offer xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"").append(userJID).append("\">");

-            buf.append("<timeout>").append(timeout).append("</timeout>");

-

-            if (sessionID != null) {

-                buf.append('<').append(SessionID.ELEMENT_NAME);

-                buf.append(" session=\"");

-                buf.append(getSessionID()).append("\" xmlns=\"");

-                buf.append(SessionID.NAMESPACE).append("\"/>");

-            }

-

-            if (metaData != null) {

-                buf.append(MetaDataUtils.serializeMetaData(metaData));

-            }

-

-            if (userID != null) {

-                buf.append('<').append(UserID.ELEMENT_NAME);

-                buf.append(" id=\"");

-                buf.append(userID).append("\" xmlns=\"");

-                buf.append(UserID.NAMESPACE).append("\"/>");

-            }

-

-            buf.append("</offer>");

-

-            return buf.toString();

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smackx.workgroup.MetaData;
+import org.jivesoftware.smackx.workgroup.agent.InvitationRequest;
+import org.jivesoftware.smackx.workgroup.agent.OfferContent;
+import org.jivesoftware.smackx.workgroup.agent.TransferRequest;
+import org.jivesoftware.smackx.workgroup.agent.UserRequest;
+import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An IQProvider for agent offer requests.
+ *
+ * @author loki der quaeler
+ */
+public class OfferRequestProvider implements IQProvider {
+
+    public OfferRequestProvider() {
+    }
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        int eventType = parser.getEventType();
+        String sessionID = null;
+        int timeout = -1;
+        OfferContent content = null;
+        boolean done = false;
+        Map<String, List<String>> metaData = new HashMap<String, List<String>>();
+
+        if (eventType != XmlPullParser.START_TAG) {
+            // throw exception
+        }
+
+        String userJID = parser.getAttributeValue("", "jid");
+        // Default userID to the JID.
+        String userID = userJID;
+
+        while (!done) {
+            eventType = parser.next();
+
+            if (eventType == XmlPullParser.START_TAG) {
+                String elemName = parser.getName();
+
+                if ("timeout".equals(elemName)) {
+                    timeout = Integer.parseInt(parser.nextText());
+                }
+                else if (MetaData.ELEMENT_NAME.equals(elemName)) {
+                    metaData = MetaDataUtils.parseMetaData(parser);
+                }
+                else if (SessionID.ELEMENT_NAME.equals(elemName)) {
+                   sessionID = parser.getAttributeValue("", "id");
+                }
+                else if (UserID.ELEMENT_NAME.equals(elemName)) {
+                    userID = parser.getAttributeValue("", "id");
+                }
+                else if ("user-request".equals(elemName)) {
+                    content = UserRequest.getInstance();
+                }
+                else if (RoomInvitation.ELEMENT_NAME.equals(elemName)) {
+                    RoomInvitation invitation = (RoomInvitation) PacketParserUtils
+                            .parsePacketExtension(RoomInvitation.ELEMENT_NAME, RoomInvitation.NAMESPACE, parser);
+                    content = new InvitationRequest(invitation.getInviter(), invitation.getRoom(),
+                            invitation.getReason());
+                }
+                else if (RoomTransfer.ELEMENT_NAME.equals(elemName)) {
+                    RoomTransfer transfer = (RoomTransfer) PacketParserUtils
+                            .parsePacketExtension(RoomTransfer.ELEMENT_NAME, RoomTransfer.NAMESPACE, parser);
+                    content = new TransferRequest(transfer.getInviter(), transfer.getRoom(), transfer.getReason());
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if ("offer".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+        }
+
+        OfferRequestPacket offerRequest =
+                new OfferRequestPacket(userJID, userID, timeout, metaData, sessionID, content);
+        offerRequest.setType(IQ.Type.SET);
+
+        return offerRequest;
+    }
+
+    public static class OfferRequestPacket extends IQ {
+
+        private int timeout;
+        private String userID;
+        private String userJID;
+        private Map<String, List<String>> metaData;
+        private String sessionID;
+        private OfferContent content;
+
+        public OfferRequestPacket(String userJID, String userID, int timeout, Map<String, List<String>> metaData,
+                String sessionID, OfferContent content)
+        {
+            this.userJID = userJID;
+            this.userID = userID;
+            this.timeout = timeout;
+            this.metaData = metaData;
+            this.sessionID = sessionID;
+            this.content = content;
+        }
+
+        /**
+         * Returns the userID, which is either the same as the userJID or a special
+         * value that the user provided as part of their "join queue" request.
+         *
+         * @return the user ID.
+         */
+        public String getUserID() {
+            return userID;
+        }
+
+        /**
+         * The JID of the user that made the "join queue" request.
+         *
+         * @return the user JID.
+         */
+        public String getUserJID() {
+            return userJID;
+        }
+
+        /**
+         * Returns the session ID associated with the request and ensuing chat. If the offer
+         * does not contain a session ID, <tt>null</tt> will be returned.
+         *
+         * @return the session id associated with the request.
+         */
+        public String getSessionID() {
+            return sessionID;
+        }
+
+        /**
+         * Returns the number of seconds the agent has to accept the offer before
+         * it times out.
+         *
+         * @return the offer timeout (in seconds).
+         */
+        public int getTimeout() {
+            return this.timeout;
+        }
+
+        public OfferContent getContent() {
+            return content;
+        }
+
+        /**
+         * Returns any meta-data associated with the offer.
+         *
+         * @return meta-data associated with the offer.
+         */
+        public Map<String, List<String>> getMetaData() {
+            return this.metaData;
+        }
+
+        public String getChildElementXML () {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<offer xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"").append(userJID).append("\">");
+            buf.append("<timeout>").append(timeout).append("</timeout>");
+
+            if (sessionID != null) {
+                buf.append('<').append(SessionID.ELEMENT_NAME);
+                buf.append(" session=\"");
+                buf.append(getSessionID()).append("\" xmlns=\"");
+                buf.append(SessionID.NAMESPACE).append("\"/>");
+            }
+
+            if (metaData != null) {
+                buf.append(MetaDataUtils.serializeMetaData(metaData));
+            }
+
+            if (userID != null) {
+                buf.append('<').append(UserID.ELEMENT_NAME);
+                buf.append(" id=\"");
+                buf.append(userID).append("\" xmlns=\"");
+                buf.append(UserID.NAMESPACE).append("\"/>");
+            }
+
+            buf.append("</offer>");
+
+            return buf.toString();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java
index 202824c..b3974b4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java
@@ -1,112 +1,112 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * An IQProvider class which has savvy about the offer-revoke tag.<br>

- *

- * @author loki der quaeler

- */

-public class OfferRevokeProvider implements IQProvider {

-

-    public IQ parseIQ (XmlPullParser parser) throws Exception {

-        // The parser will be positioned on the opening IQ tag, so get the JID attribute.

-        String userJID = parser.getAttributeValue("", "jid");

-        // Default the userID to the JID.

-        String userID = userJID;

-        String reason = null;

-        String sessionID = null;

-        boolean done = false;

-

-        while (!done) {

-            int eventType = parser.next();

-

-            if ((eventType == XmlPullParser.START_TAG) && parser.getName().equals("reason")) {

-                reason = parser.nextText();

-            }

-            else if ((eventType == XmlPullParser.START_TAG)

-                         && parser.getName().equals(SessionID.ELEMENT_NAME)) {

-                sessionID = parser.getAttributeValue("", "id");

-            }

-            else if ((eventType == XmlPullParser.START_TAG)

-                         && parser.getName().equals(UserID.ELEMENT_NAME)) {

-                userID = parser.getAttributeValue("", "id");

-            }

-            else if ((eventType == XmlPullParser.END_TAG) && parser.getName().equals(

-                    "offer-revoke"))

-            {

-                done = true;

-            }

-        }

-

-        return new OfferRevokePacket(userJID, userID, reason, sessionID);

-    }

-

-    public class OfferRevokePacket extends IQ {

-

-        private String userJID;

-        private String userID;

-        private String sessionID;

-        private String reason;

-

-        public OfferRevokePacket (String userJID, String userID, String cause, String sessionID) {

-            this.userJID = userJID;

-            this.userID = userID;

-            this.reason = cause;

-            this.sessionID = sessionID;

-        }

-

-        public String getUserJID() {

-            return userJID;

-        }

-

-        public String getUserID() {

-            return this.userID;

-        }

-

-        public String getReason() {

-            return this.reason;

-        }

-

-        public String getSessionID() {

-            return this.sessionID;

-        }

-

-        public String getChildElementXML () {

-            StringBuilder buf = new StringBuilder();

-            buf.append("<offer-revoke xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"").append(userID).append("\">");

-            if (reason != null) {

-                buf.append("<reason>").append(reason).append("</reason>");

-            }

-            if (sessionID != null) {

-                buf.append(new SessionID(sessionID).toXML());

-            }

-            if (userID != null) {

-                buf.append(new UserID(userID).toXML());

-            }

-            buf.append("</offer-revoke>");

-            return buf.toString();

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * An IQProvider class which has savvy about the offer-revoke tag.<br>
+ *
+ * @author loki der quaeler
+ */
+public class OfferRevokeProvider implements IQProvider {
+
+    public IQ parseIQ (XmlPullParser parser) throws Exception {
+        // The parser will be positioned on the opening IQ tag, so get the JID attribute.
+        String userJID = parser.getAttributeValue("", "jid");
+        // Default the userID to the JID.
+        String userID = userJID;
+        String reason = null;
+        String sessionID = null;
+        boolean done = false;
+
+        while (!done) {
+            int eventType = parser.next();
+
+            if ((eventType == XmlPullParser.START_TAG) && parser.getName().equals("reason")) {
+                reason = parser.nextText();
+            }
+            else if ((eventType == XmlPullParser.START_TAG)
+                         && parser.getName().equals(SessionID.ELEMENT_NAME)) {
+                sessionID = parser.getAttributeValue("", "id");
+            }
+            else if ((eventType == XmlPullParser.START_TAG)
+                         && parser.getName().equals(UserID.ELEMENT_NAME)) {
+                userID = parser.getAttributeValue("", "id");
+            }
+            else if ((eventType == XmlPullParser.END_TAG) && parser.getName().equals(
+                    "offer-revoke"))
+            {
+                done = true;
+            }
+        }
+
+        return new OfferRevokePacket(userJID, userID, reason, sessionID);
+    }
+
+    public class OfferRevokePacket extends IQ {
+
+        private String userJID;
+        private String userID;
+        private String sessionID;
+        private String reason;
+
+        public OfferRevokePacket (String userJID, String userID, String cause, String sessionID) {
+            this.userJID = userJID;
+            this.userID = userID;
+            this.reason = cause;
+            this.sessionID = sessionID;
+        }
+
+        public String getUserJID() {
+            return userJID;
+        }
+
+        public String getUserID() {
+            return this.userID;
+        }
+
+        public String getReason() {
+            return this.reason;
+        }
+
+        public String getSessionID() {
+            return this.sessionID;
+        }
+
+        public String getChildElementXML () {
+            StringBuilder buf = new StringBuilder();
+            buf.append("<offer-revoke xmlns=\"http://jabber.org/protocol/workgroup\" jid=\"").append(userID).append("\">");
+            if (reason != null) {
+                buf.append("<reason>").append(reason).append("</reason>");
+            }
+            if (sessionID != null) {
+                buf.append(new SessionID(sessionID).toXML());
+            }
+            if (userID != null) {
+                buf.append(new UserID(userID).toXML());
+            }
+            buf.append("</offer-revoke>");
+            return buf.toString();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java
index 86b3673..2a7fe3b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java
@@ -1,199 +1,199 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smackx.workgroup.QueueUser;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.text.SimpleDateFormat;

-import java.util.Date;

-import java.util.HashSet;

-import java.util.Iterator;

-import java.util.Set;

-

-/**

- * Queue details packet extension, which contains details about the users

- * currently in a queue.

- */

-public class QueueDetails implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "notify-queue-details";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private static final String DATE_FORMAT = "yyyyMMdd'T'HH:mm:ss";

-

-    private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

-    /**

-     * The list of users in the queue.

-     */

-    private Set<QueueUser> users;

-

-    /**

-     * Creates a new QueueDetails packet

-     */

-    private QueueDetails() {

-        users = new HashSet<QueueUser>();

-    }

-

-    /**

-     * Returns the number of users currently in the queue that are waiting to

-     * be routed to an agent.

-     *

-     * @return the number of users in the queue.

-     */

-    public int getUserCount() {

-        return users.size();

-    }

-

-    /**

-     * Returns the set of users in the queue that are waiting to

-     * be routed to an agent (as QueueUser objects).

-     *

-     * @return a Set for the users waiting in a queue.

-     */

-    public Set<QueueUser> getUsers() {

-        synchronized (users) {

-            return users;

-        }

-    }

-

-    /**

-     * Adds a user to the packet.

-     *

-     * @param user the user.

-     */

-    private void addUser(QueueUser user) {

-        synchronized (users) {

-            users.add(user);

-        }

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-

-        synchronized (users) {

-            for (Iterator<QueueUser> i=users.iterator(); i.hasNext(); ) {

-                QueueUser user = (QueueUser)i.next();

-                int position = user.getQueuePosition();

-                int timeRemaining = user.getEstimatedRemainingTime();

-                Date timestamp = user.getQueueJoinTimestamp();

-

-                buf.append("<user jid=\"").append(user.getUserID()).append("\">");

-

-                if (position != -1) {

-                    buf.append("<position>").append(position).append("</position>");

-                }

-

-                if (timeRemaining != -1) {

-                    buf.append("<time>").append(timeRemaining).append("</time>");

-                }

-

-                if (timestamp != null) {

-                    buf.append("<join-time>");

-                    buf.append(dateFormat.format(timestamp));

-                    buf.append("</join-time>");

-                }

-

-                buf.append("</user>");

-            }

-        }

-        buf.append("</").append(ELEMENT_NAME).append(">");

-        return buf.toString();

-    }

-

-    /**

-     * Provider class for QueueDetails packet extensions.

-     */

-    public static class Provider implements PacketExtensionProvider {

-        

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            

-            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

-            QueueDetails queueDetails = new QueueDetails();

-

-            int eventType = parser.getEventType();

-            while (eventType != XmlPullParser.END_TAG &&

-                    "notify-queue-details".equals(parser.getName()))

-            {

-                eventType = parser.next();

-                while ((eventType == XmlPullParser.START_TAG) && "user".equals(parser.getName())) {

-                    String uid = null;

-                    int position = -1;

-                    int time = -1;

-                    Date joinTime = null;

-

-                    uid = parser.getAttributeValue("", "jid");

-               

-                    if (uid == null) {

-                        // throw exception

-                    }

-

-                    eventType = parser.next();

-                    while ((eventType != XmlPullParser.END_TAG)

-                                || (! "user".equals(parser.getName())))

-                    {                        

-                        if ("position".equals(parser.getName())) {

-                            position = Integer.parseInt(parser.nextText());

-                        }

-                        else if ("time".equals(parser.getName())) {

-                            time = Integer.parseInt(parser.nextText());

-                        }

-                        else if ("join-time".equals(parser.getName())) {

-                            joinTime = dateFormat.parse(parser.nextText());                            

-                        }

-                        else if( parser.getName().equals( "waitTime" ) ) {

-                            Date wait = dateFormat.parse(parser.nextText());

-                            System.out.println( wait );

-                        }

-

-                        eventType = parser.next();

-

-                        if (eventType != XmlPullParser.END_TAG) {

-                            // throw exception

-                        }

-                    }

-

-                    queueDetails.addUser(new QueueUser(uid, position, time, joinTime));

-

-                    eventType = parser.next();

-                }

-            }

-            return queueDetails;

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smackx.workgroup.QueueUser;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Queue details packet extension, which contains details about the users
+ * currently in a queue.
+ */
+public class QueueDetails implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "notify-queue-details";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private static final String DATE_FORMAT = "yyyyMMdd'T'HH:mm:ss";
+
+    private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+    /**
+     * The list of users in the queue.
+     */
+    private Set<QueueUser> users;
+
+    /**
+     * Creates a new QueueDetails packet
+     */
+    private QueueDetails() {
+        users = new HashSet<QueueUser>();
+    }
+
+    /**
+     * Returns the number of users currently in the queue that are waiting to
+     * be routed to an agent.
+     *
+     * @return the number of users in the queue.
+     */
+    public int getUserCount() {
+        return users.size();
+    }
+
+    /**
+     * Returns the set of users in the queue that are waiting to
+     * be routed to an agent (as QueueUser objects).
+     *
+     * @return a Set for the users waiting in a queue.
+     */
+    public Set<QueueUser> getUsers() {
+        synchronized (users) {
+            return users;
+        }
+    }
+
+    /**
+     * Adds a user to the packet.
+     *
+     * @param user the user.
+     */
+    private void addUser(QueueUser user) {
+        synchronized (users) {
+            users.add(user);
+        }
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+
+        synchronized (users) {
+            for (Iterator<QueueUser> i=users.iterator(); i.hasNext(); ) {
+                QueueUser user = (QueueUser)i.next();
+                int position = user.getQueuePosition();
+                int timeRemaining = user.getEstimatedRemainingTime();
+                Date timestamp = user.getQueueJoinTimestamp();
+
+                buf.append("<user jid=\"").append(user.getUserID()).append("\">");
+
+                if (position != -1) {
+                    buf.append("<position>").append(position).append("</position>");
+                }
+
+                if (timeRemaining != -1) {
+                    buf.append("<time>").append(timeRemaining).append("</time>");
+                }
+
+                if (timestamp != null) {
+                    buf.append("<join-time>");
+                    buf.append(dateFormat.format(timestamp));
+                    buf.append("</join-time>");
+                }
+
+                buf.append("</user>");
+            }
+        }
+        buf.append("</").append(ELEMENT_NAME).append(">");
+        return buf.toString();
+    }
+
+    /**
+     * Provider class for QueueDetails packet extensions.
+     */
+    public static class Provider implements PacketExtensionProvider {
+        
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            
+            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+            QueueDetails queueDetails = new QueueDetails();
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.END_TAG &&
+                    "notify-queue-details".equals(parser.getName()))
+            {
+                eventType = parser.next();
+                while ((eventType == XmlPullParser.START_TAG) && "user".equals(parser.getName())) {
+                    String uid = null;
+                    int position = -1;
+                    int time = -1;
+                    Date joinTime = null;
+
+                    uid = parser.getAttributeValue("", "jid");
+               
+                    if (uid == null) {
+                        // throw exception
+                    }
+
+                    eventType = parser.next();
+                    while ((eventType != XmlPullParser.END_TAG)
+                                || (! "user".equals(parser.getName())))
+                    {                        
+                        if ("position".equals(parser.getName())) {
+                            position = Integer.parseInt(parser.nextText());
+                        }
+                        else if ("time".equals(parser.getName())) {
+                            time = Integer.parseInt(parser.nextText());
+                        }
+                        else if ("join-time".equals(parser.getName())) {
+                            joinTime = dateFormat.parse(parser.nextText());                            
+                        }
+                        else if( parser.getName().equals( "waitTime" ) ) {
+                            Date wait = dateFormat.parse(parser.nextText());
+                            System.out.println( wait );
+                        }
+
+                        eventType = parser.next();
+
+                        if (eventType != XmlPullParser.END_TAG) {
+                            // throw exception
+                        }
+                    }
+
+                    queueDetails.addUser(new QueueUser(uid, position, time, joinTime));
+
+                    eventType = parser.next();
+                }
+            }
+            return queueDetails;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java
index a559579..c97473c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java
@@ -1,160 +1,160 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smackx.workgroup.agent.WorkgroupQueue;

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.text.SimpleDateFormat;

-import java.util.Date;

-

-public class QueueOverview implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static String ELEMENT_NAME = "notify-queue";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private static final String DATE_FORMAT = "yyyyMMdd'T'HH:mm:ss";

-    private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

-

-    private int averageWaitTime;

-    private Date oldestEntry;

-    private int userCount;

-    private WorkgroupQueue.Status status;

-

-    QueueOverview() {

-        this.averageWaitTime = -1;

-        this.oldestEntry = null;

-        this.userCount = -1;

-        this.status = null;

-    }

-

-    void setAverageWaitTime(int averageWaitTime) {

-        this.averageWaitTime = averageWaitTime;

-    }

-

-    public int getAverageWaitTime () {

-        return averageWaitTime;

-    }

-

-    void setOldestEntry(Date oldestEntry) {

-        this.oldestEntry = oldestEntry;

-    }

-

-    public Date getOldestEntry() {

-        return oldestEntry;

-    }

-

-    void setUserCount(int userCount) {

-        this.userCount = userCount;

-    }

-

-    public int getUserCount() {

-        return userCount;

-    }

-

-    public WorkgroupQueue.Status getStatus() {

-        return status;

-    }

-

-    void setStatus(WorkgroupQueue.Status status) {

-        this.status = status;

-    }

-

-    public String getElementName () {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace () {

-        return NAMESPACE;

-    }

-

-    public String toXML () {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-

-        if (userCount != -1) {

-            buf.append("<count>").append(userCount).append("</count>");

-        }

-        if (oldestEntry != null) {

-            buf.append("<oldest>").append(dateFormat.format(oldestEntry)).append("</oldest>");

-        }

-        if (averageWaitTime != -1) {

-            buf.append("<time>").append(averageWaitTime).append("</time>");

-        }

-        if (status != null) {

-            buf.append("<status>").append(status).append("</status>");

-        }

-        buf.append("</").append(ELEMENT_NAME).append(">");

-

-        return buf.toString();

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension (XmlPullParser parser) throws Exception {

-            int eventType = parser.getEventType();

-            QueueOverview queueOverview = new QueueOverview();            

-            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

-

-            if (eventType != XmlPullParser.START_TAG) {

-                // throw exception

-            }

-

-            eventType = parser.next();

-            while ((eventType != XmlPullParser.END_TAG)

-                         || (!ELEMENT_NAME.equals(parser.getName())))

-            {

-                if ("count".equals(parser.getName())) {

-                    queueOverview.setUserCount(Integer.parseInt(parser.nextText()));

-                }

-                else if ("time".equals(parser.getName())) {

-                    queueOverview.setAverageWaitTime(Integer.parseInt(parser.nextText()));

-                }

-                else if ("oldest".equals(parser.getName())) {

-                    queueOverview.setOldestEntry((dateFormat.parse(parser.nextText())));                    

-                }

-                else if ("status".equals(parser.getName())) {

-                    queueOverview.setStatus(WorkgroupQueue.Status.fromString(parser.nextText()));

-                }

-

-                eventType = parser.next();

-

-                if (eventType != XmlPullParser.END_TAG) {

-                    // throw exception

-                }

-            }

-

-            if (eventType != XmlPullParser.END_TAG) {

-                // throw exception

-            }

-

-            return queueOverview;

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smackx.workgroup.agent.WorkgroupQueue;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class QueueOverview implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static String ELEMENT_NAME = "notify-queue";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private static final String DATE_FORMAT = "yyyyMMdd'T'HH:mm:ss";
+    private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+    private int averageWaitTime;
+    private Date oldestEntry;
+    private int userCount;
+    private WorkgroupQueue.Status status;
+
+    QueueOverview() {
+        this.averageWaitTime = -1;
+        this.oldestEntry = null;
+        this.userCount = -1;
+        this.status = null;
+    }
+
+    void setAverageWaitTime(int averageWaitTime) {
+        this.averageWaitTime = averageWaitTime;
+    }
+
+    public int getAverageWaitTime () {
+        return averageWaitTime;
+    }
+
+    void setOldestEntry(Date oldestEntry) {
+        this.oldestEntry = oldestEntry;
+    }
+
+    public Date getOldestEntry() {
+        return oldestEntry;
+    }
+
+    void setUserCount(int userCount) {
+        this.userCount = userCount;
+    }
+
+    public int getUserCount() {
+        return userCount;
+    }
+
+    public WorkgroupQueue.Status getStatus() {
+        return status;
+    }
+
+    void setStatus(WorkgroupQueue.Status status) {
+        this.status = status;
+    }
+
+    public String getElementName () {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace () {
+        return NAMESPACE;
+    }
+
+    public String toXML () {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+
+        if (userCount != -1) {
+            buf.append("<count>").append(userCount).append("</count>");
+        }
+        if (oldestEntry != null) {
+            buf.append("<oldest>").append(dateFormat.format(oldestEntry)).append("</oldest>");
+        }
+        if (averageWaitTime != -1) {
+            buf.append("<time>").append(averageWaitTime).append("</time>");
+        }
+        if (status != null) {
+            buf.append("<status>").append(status).append("</status>");
+        }
+        buf.append("</").append(ELEMENT_NAME).append(">");
+
+        return buf.toString();
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension (XmlPullParser parser) throws Exception {
+            int eventType = parser.getEventType();
+            QueueOverview queueOverview = new QueueOverview();            
+            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+            if (eventType != XmlPullParser.START_TAG) {
+                // throw exception
+            }
+
+            eventType = parser.next();
+            while ((eventType != XmlPullParser.END_TAG)
+                         || (!ELEMENT_NAME.equals(parser.getName())))
+            {
+                if ("count".equals(parser.getName())) {
+                    queueOverview.setUserCount(Integer.parseInt(parser.nextText()));
+                }
+                else if ("time".equals(parser.getName())) {
+                    queueOverview.setAverageWaitTime(Integer.parseInt(parser.nextText()));
+                }
+                else if ("oldest".equals(parser.getName())) {
+                    queueOverview.setOldestEntry((dateFormat.parse(parser.nextText())));                    
+                }
+                else if ("status".equals(parser.getName())) {
+                    queueOverview.setStatus(WorkgroupQueue.Status.fromString(parser.nextText()));
+                }
+
+                eventType = parser.next();
+
+                if (eventType != XmlPullParser.END_TAG) {
+                    // throw exception
+                }
+            }
+
+            if (eventType != XmlPullParser.END_TAG) {
+                // throw exception
+            }
+
+            return queueOverview;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java
index c326a57..828f6bb 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java
@@ -1,122 +1,122 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * An IQ packet that encapsulates both types of workgroup queue

- * status notifications -- position updates, and estimated time

- * left in the queue updates.

- */

-public class QueueUpdate implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "queue-status";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private int position;

-    private int remainingTime;

-

-    public QueueUpdate(int position, int remainingTime) {

-        this.position = position;

-        this.remainingTime = remainingTime;

-    }

-

-    /**

-     * Returns the user's position in the workgroup queue, or -1 if the

-     * value isn't set on this packet.

-     *

-     * @return the position in the workgroup queue.

-     */

-    public int getPosition() {

-        return this.position;

-    }

-

-    /**

-     * Returns the user's estimated time left in the workgroup queue, or

-     * -1 if the value isn't set on this packet.

-     *

-     * @return the estimated time left in the workgroup queue.

-     */

-    public int getRemaingTime() {

-        return remainingTime;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-        buf.append("<queue-status xmlns=\"http://jabber.org/protocol/workgroup\">");

-        if (position != -1) {

-            buf.append("<position>").append(position).append("</position>");

-        }

-        if (remainingTime != -1) {

-            buf.append("<time>").append(remainingTime).append("</time>");

-        }

-        buf.append("</queue-status>");

-        return buf.toString();

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            boolean done = false;

-            int position = -1;

-            int timeRemaining = -1;

-            while (!done) {

-                parser.next();

-                String elementName = parser.getName();

-                if (parser.getEventType() == XmlPullParser.START_TAG && "position".equals(elementName)) {

-                    try {

-                        position = Integer.parseInt(parser.nextText());

-                    }

-                    catch (NumberFormatException nfe) {

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.START_TAG && "time".equals(elementName)) {

-                    try {

-                        timeRemaining = Integer.parseInt(parser.nextText());

-                    }

-                    catch (NumberFormatException nfe) {

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.END_TAG && "queue-status".equals(elementName)) {

-                    done = true;

-                }

-            }

-            return new QueueUpdate(position, timeRemaining);

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * An IQ packet that encapsulates both types of workgroup queue
+ * status notifications -- position updates, and estimated time
+ * left in the queue updates.
+ */
+public class QueueUpdate implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "queue-status";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private int position;
+    private int remainingTime;
+
+    public QueueUpdate(int position, int remainingTime) {
+        this.position = position;
+        this.remainingTime = remainingTime;
+    }
+
+    /**
+     * Returns the user's position in the workgroup queue, or -1 if the
+     * value isn't set on this packet.
+     *
+     * @return the position in the workgroup queue.
+     */
+    public int getPosition() {
+        return this.position;
+    }
+
+    /**
+     * Returns the user's estimated time left in the workgroup queue, or
+     * -1 if the value isn't set on this packet.
+     *
+     * @return the estimated time left in the workgroup queue.
+     */
+    public int getRemaingTime() {
+        return remainingTime;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<queue-status xmlns=\"http://jabber.org/protocol/workgroup\">");
+        if (position != -1) {
+            buf.append("<position>").append(position).append("</position>");
+        }
+        if (remainingTime != -1) {
+            buf.append("<time>").append(remainingTime).append("</time>");
+        }
+        buf.append("</queue-status>");
+        return buf.toString();
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            boolean done = false;
+            int position = -1;
+            int timeRemaining = -1;
+            while (!done) {
+                parser.next();
+                String elementName = parser.getName();
+                if (parser.getEventType() == XmlPullParser.START_TAG && "position".equals(elementName)) {
+                    try {
+                        position = Integer.parseInt(parser.nextText());
+                    }
+                    catch (NumberFormatException nfe) {
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.START_TAG && "time".equals(elementName)) {
+                    try {
+                        timeRemaining = Integer.parseInt(parser.nextText());
+                    }
+                    catch (NumberFormatException nfe) {
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.END_TAG && "queue-status".equals(elementName)) {
+                    done = true;
+                }
+            }
+            return new QueueUpdate(position, timeRemaining);
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java
index 34555de..1a6ec9c 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java
@@ -1,177 +1,177 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Packet extension for {@link org.jivesoftware.smackx.workgroup.agent.InvitationRequest}.

- *

- * @author Gaston Dombiak

- */

-public class RoomInvitation implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "invite";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    /**

-     * Type of entity being invited to a groupchat support session.

-     */

-    private Type type;

-    /**

-     * JID of the entity being invited. The entity could be another agent, user , a queue or a workgroup. In

-     * the case of a queue or a workgroup the server will select the best agent to invite. 

-     */

-    private String invitee;

-    /**

-     * Full JID of the user that sent the invitation.

-     */

-    private String inviter;

-    /**

-     * ID of the session that originated the initial user request.

-     */

-    private String sessionID;

-    /**

-     * JID of the room to join if offer is accepted.

-     */

-    private String room;

-    /**

-     * Text provided by the inviter explaining the reason why the invitee is invited. 

-     */

-    private String reason;

-

-    public RoomInvitation(Type type, String invitee, String sessionID, String reason) {

-        this.type = type;

-        this.invitee = invitee;

-        this.sessionID = sessionID;

-        this.reason = reason;

-    }

-

-    private RoomInvitation() {

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String getInviter() {

-        return inviter;

-    }

-

-    public String getRoom() {

-        return room;

-    }

-

-    public String getReason() {

-        return reason;

-    }

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);

-        buf.append("\" type=\"").append(type).append("\">");

-        buf.append("<session xmlns=\"http://jivesoftware.com/protocol/workgroup\" id=\"").append(sessionID).append("\"></session>");

-        if (invitee != null) {

-            buf.append("<invitee>").append(invitee).append("</invitee>");

-        }

-        if (inviter != null) {

-            buf.append("<inviter>").append(inviter).append("</inviter>");

-        }

-        if (reason != null) {

-            buf.append("<reason>").append(reason).append("</reason>");

-        }

-        // Add packet extensions, if any are defined.

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * Type of entity being invited to a groupchat support session.

-     */

-    public static enum Type {

-        /**

-         * A user is being invited to a groupchat support session. The user could be another agent

-         * or just a regular XMPP user.

-         */

-        user,

-        /**

-         * Some agent of the specified queue will be invited to the groupchat support session.

-         */

-        queue,

-        /**

-         * Some agent of the specified workgroup will be invited to the groupchat support session.

-         */

-        workgroup

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            final RoomInvitation invitation = new RoomInvitation();

-            invitation.type = Type.valueOf(parser.getAttributeValue("", "type"));

-

-            boolean done = false;

-            while (!done) {

-                parser.next();

-                String elementName = parser.getName();

-                if (parser.getEventType() == XmlPullParser.START_TAG) {

-                    if ("session".equals(elementName)) {

-                        invitation.sessionID = parser.getAttributeValue("", "id");

-                    }

-                    else if ("invitee".equals(elementName)) {

-                        invitation.invitee = parser.nextText();

-                    }

-                    else if ("inviter".equals(elementName)) {

-                        invitation.inviter = parser.nextText();

-                    }

-                    else if ("reason".equals(elementName)) {

-                        invitation.reason = parser.nextText();

-                    }

-                    else if ("room".equals(elementName)) {

-                        invitation.room = parser.nextText();

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.END_TAG && ELEMENT_NAME.equals(elementName)) {

-                    done = true;

-                }

-            }

-            return invitation;

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Packet extension for {@link org.jivesoftware.smackx.workgroup.agent.InvitationRequest}.
+ *
+ * @author Gaston Dombiak
+ */
+public class RoomInvitation implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "invite";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    /**
+     * Type of entity being invited to a groupchat support session.
+     */
+    private Type type;
+    /**
+     * JID of the entity being invited. The entity could be another agent, user , a queue or a workgroup. In
+     * the case of a queue or a workgroup the server will select the best agent to invite. 
+     */
+    private String invitee;
+    /**
+     * Full JID of the user that sent the invitation.
+     */
+    private String inviter;
+    /**
+     * ID of the session that originated the initial user request.
+     */
+    private String sessionID;
+    /**
+     * JID of the room to join if offer is accepted.
+     */
+    private String room;
+    /**
+     * Text provided by the inviter explaining the reason why the invitee is invited. 
+     */
+    private String reason;
+
+    public RoomInvitation(Type type, String invitee, String sessionID, String reason) {
+        this.type = type;
+        this.invitee = invitee;
+        this.sessionID = sessionID;
+        this.reason = reason;
+    }
+
+    private RoomInvitation() {
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String getInviter() {
+        return inviter;
+    }
+
+    public String getRoom() {
+        return room;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);
+        buf.append("\" type=\"").append(type).append("\">");
+        buf.append("<session xmlns=\"http://jivesoftware.com/protocol/workgroup\" id=\"").append(sessionID).append("\"></session>");
+        if (invitee != null) {
+            buf.append("<invitee>").append(invitee).append("</invitee>");
+        }
+        if (inviter != null) {
+            buf.append("<inviter>").append(inviter).append("</inviter>");
+        }
+        if (reason != null) {
+            buf.append("<reason>").append(reason).append("</reason>");
+        }
+        // Add packet extensions, if any are defined.
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * Type of entity being invited to a groupchat support session.
+     */
+    public static enum Type {
+        /**
+         * A user is being invited to a groupchat support session. The user could be another agent
+         * or just a regular XMPP user.
+         */
+        user,
+        /**
+         * Some agent of the specified queue will be invited to the groupchat support session.
+         */
+        queue,
+        /**
+         * Some agent of the specified workgroup will be invited to the groupchat support session.
+         */
+        workgroup
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            final RoomInvitation invitation = new RoomInvitation();
+            invitation.type = Type.valueOf(parser.getAttributeValue("", "type"));
+
+            boolean done = false;
+            while (!done) {
+                parser.next();
+                String elementName = parser.getName();
+                if (parser.getEventType() == XmlPullParser.START_TAG) {
+                    if ("session".equals(elementName)) {
+                        invitation.sessionID = parser.getAttributeValue("", "id");
+                    }
+                    else if ("invitee".equals(elementName)) {
+                        invitation.invitee = parser.nextText();
+                    }
+                    else if ("inviter".equals(elementName)) {
+                        invitation.inviter = parser.nextText();
+                    }
+                    else if ("reason".equals(elementName)) {
+                        invitation.reason = parser.nextText();
+                    }
+                    else if ("room".equals(elementName)) {
+                        invitation.room = parser.nextText();
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.END_TAG && ELEMENT_NAME.equals(elementName)) {
+                    done = true;
+                }
+            }
+            return invitation;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java
index d1e83e2..54362e4 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java
@@ -1,177 +1,177 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * Packet extension for {@link org.jivesoftware.smackx.workgroup.agent.TransferRequest}.

- *

- * @author Gaston Dombiak

- */

-public class RoomTransfer implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "transfer";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    /**

-     * Type of entity being invited to a groupchat support session.

-     */

-    private RoomTransfer.Type type;

-    /**

-     * JID of the entity being invited. The entity could be another agent, user , a queue or a workgroup. In

-     * the case of a queue or a workgroup the server will select the best agent to invite.

-     */

-    private String invitee;

-    /**

-     * Full JID of the user that sent the invitation.

-     */

-    private String inviter;

-    /**

-     * ID of the session that originated the initial user request.

-     */

-    private String sessionID;

-    /**

-     * JID of the room to join if offer is accepted.

-     */

-    private String room;

-    /**

-     * Text provided by the inviter explaining the reason why the invitee is invited.

-     */

-    private String reason;

-

-    public RoomTransfer(RoomTransfer.Type type, String invitee, String sessionID, String reason) {

-        this.type = type;

-        this.invitee = invitee;

-        this.sessionID = sessionID;

-        this.reason = reason;

-    }

-

-    private RoomTransfer() {

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String getInviter() {

-        return inviter;

-    }

-

-    public String getRoom() {

-        return room;

-    }

-

-    public String getReason() {

-        return reason;

-    }

-

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);

-        buf.append("\" type=\"").append(type).append("\">");

-        buf.append("<session xmlns=\"http://jivesoftware.com/protocol/workgroup\" id=\"").append(sessionID).append("\"></session>");

-        if (invitee != null) {

-            buf.append("<invitee>").append(invitee).append("</invitee>");

-        }

-        if (inviter != null) {

-            buf.append("<inviter>").append(inviter).append("</inviter>");

-        }

-        if (reason != null) {

-            buf.append("<reason>").append(reason).append("</reason>");

-        }

-        // Add packet extensions, if any are defined.

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * Type of entity being invited to a groupchat support session.

-     */

-    public static enum Type {

-        /**

-         * A user is being invited to a groupchat support session. The user could be another agent

-         * or just a regular XMPP user.

-         */

-        user,

-        /**

-         * Some agent of the specified queue will be invited to the groupchat support session.

-         */

-        queue,

-        /**

-         * Some agent of the specified workgroup will be invited to the groupchat support session.

-         */

-        workgroup

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            final RoomTransfer invitation = new RoomTransfer();

-            invitation.type = RoomTransfer.Type.valueOf(parser.getAttributeValue("", "type"));

-

-            boolean done = false;

-            while (!done) {

-                parser.next();

-                String elementName = parser.getName();

-                if (parser.getEventType() == XmlPullParser.START_TAG) {

-                    if ("session".equals(elementName)) {

-                        invitation.sessionID = parser.getAttributeValue("", "id");

-                    }

-                    else if ("invitee".equals(elementName)) {

-                        invitation.invitee = parser.nextText();

-                    }

-                    else if ("inviter".equals(elementName)) {

-                        invitation.inviter = parser.nextText();

-                    }

-                    else if ("reason".equals(elementName)) {

-                        invitation.reason = parser.nextText();

-                    }

-                    else if ("room".equals(elementName)) {

-                        invitation.room = parser.nextText();

-                    }

-                }

-                else if (parser.getEventType() == XmlPullParser.END_TAG && ELEMENT_NAME.equals(elementName)) {

-                    done = true;

-                }

-            }

-            return invitation;

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Packet extension for {@link org.jivesoftware.smackx.workgroup.agent.TransferRequest}.
+ *
+ * @author Gaston Dombiak
+ */
+public class RoomTransfer implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "transfer";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    /**
+     * Type of entity being invited to a groupchat support session.
+     */
+    private RoomTransfer.Type type;
+    /**
+     * JID of the entity being invited. The entity could be another agent, user , a queue or a workgroup. In
+     * the case of a queue or a workgroup the server will select the best agent to invite.
+     */
+    private String invitee;
+    /**
+     * Full JID of the user that sent the invitation.
+     */
+    private String inviter;
+    /**
+     * ID of the session that originated the initial user request.
+     */
+    private String sessionID;
+    /**
+     * JID of the room to join if offer is accepted.
+     */
+    private String room;
+    /**
+     * Text provided by the inviter explaining the reason why the invitee is invited.
+     */
+    private String reason;
+
+    public RoomTransfer(RoomTransfer.Type type, String invitee, String sessionID, String reason) {
+        this.type = type;
+        this.invitee = invitee;
+        this.sessionID = sessionID;
+        this.reason = reason;
+    }
+
+    private RoomTransfer() {
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String getInviter() {
+        return inviter;
+    }
+
+    public String getRoom() {
+        return room;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE);
+        buf.append("\" type=\"").append(type).append("\">");
+        buf.append("<session xmlns=\"http://jivesoftware.com/protocol/workgroup\" id=\"").append(sessionID).append("\"></session>");
+        if (invitee != null) {
+            buf.append("<invitee>").append(invitee).append("</invitee>");
+        }
+        if (inviter != null) {
+            buf.append("<inviter>").append(inviter).append("</inviter>");
+        }
+        if (reason != null) {
+            buf.append("<reason>").append(reason).append("</reason>");
+        }
+        // Add packet extensions, if any are defined.
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * Type of entity being invited to a groupchat support session.
+     */
+    public static enum Type {
+        /**
+         * A user is being invited to a groupchat support session. The user could be another agent
+         * or just a regular XMPP user.
+         */
+        user,
+        /**
+         * Some agent of the specified queue will be invited to the groupchat support session.
+         */
+        queue,
+        /**
+         * Some agent of the specified workgroup will be invited to the groupchat support session.
+         */
+        workgroup
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            final RoomTransfer invitation = new RoomTransfer();
+            invitation.type = RoomTransfer.Type.valueOf(parser.getAttributeValue("", "type"));
+
+            boolean done = false;
+            while (!done) {
+                parser.next();
+                String elementName = parser.getName();
+                if (parser.getEventType() == XmlPullParser.START_TAG) {
+                    if ("session".equals(elementName)) {
+                        invitation.sessionID = parser.getAttributeValue("", "id");
+                    }
+                    else if ("invitee".equals(elementName)) {
+                        invitation.invitee = parser.nextText();
+                    }
+                    else if ("inviter".equals(elementName)) {
+                        invitation.inviter = parser.nextText();
+                    }
+                    else if ("reason".equals(elementName)) {
+                        invitation.reason = parser.nextText();
+                    }
+                    else if ("room".equals(elementName)) {
+                        invitation.room = parser.nextText();
+                    }
+                }
+                else if (parser.getEventType() == XmlPullParser.END_TAG && ELEMENT_NAME.equals(elementName)) {
+                    done = true;
+                }
+            }
+            return invitation;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/SessionID.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/SessionID.java
index bfd7cfd..1930532 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/SessionID.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/SessionID.java
@@ -1,77 +1,77 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-public class SessionID implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "session";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    private String sessionID;

-

-    public SessionID(String sessionID) {

-        this.sessionID = sessionID;

-    }

-

-    public String getSessionID() {

-        return this.sessionID;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\" ");

-        buf.append("id=\"").append(this.getSessionID());

-        buf.append("\"/>");

-

-        return buf.toString();

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            String sessionID = parser.getAttributeValue("", "id");

-

-            // Advance to end of extension.

-            parser.next();

-

-            return new SessionID(sessionID);

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+public class SessionID implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "session";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    private String sessionID;
+
+    public SessionID(String sessionID) {
+        this.sessionID = sessionID;
+    }
+
+    public String getSessionID() {
+        return this.sessionID;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\" ");
+        buf.append("id=\"").append(this.getSessionID());
+        buf.append("\"/>");
+
+        return buf.toString();
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            String sessionID = parser.getAttributeValue("", "id");
+
+            // Advance to end of extension.
+            parser.next();
+
+            return new SessionID(sessionID);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcript.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcript.java
index 7f8f29e..6ccd740 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcript.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcript.java
@@ -1,98 +1,98 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-

-import java.util.ArrayList;

-import java.util.Collections;

-import java.util.Iterator;

-import java.util.List;

-

-/**

- * Represents the conversation transcript that occured in a group chat room between an Agent

- * and a user that requested assistance. The transcript contains all the Messages that were sent

- * to the room as well as the sent presences. 

- *

- * @author Gaston Dombiak

- */

-public class Transcript extends IQ {

-    private String sessionID;

-    private List<Packet> packets;

-

-    /**

-     * Creates a transcript request for the given sessionID.

-     *

-     * @param sessionID the id of the session to get the conversation transcript.

-     */

-    public Transcript(String sessionID) {

-        this.sessionID = sessionID;

-        this.packets = new ArrayList<Packet>();

-    }

-

-    /**

-     * Creates a new transcript for the given sessionID and list of packets. The list of packets

-     * may include Messages and/or Presences.

-     *

-     * @param sessionID the id of the session that generated this conversation transcript.

-     * @param packets the list of messages and presences send to the room.

-     */

-    public Transcript(String sessionID, List<Packet> packets) {

-        this.sessionID = sessionID;

-        this.packets = packets;

-    }

-

-    /**

-     * Returns id of the session that generated this conversation transcript. The sessionID is a

-     * value generated by the server when a new request is received.

-     *

-     * @return id of the session that generated this conversation transcript.

-     */

-    public String getSessionID() {

-        return sessionID;

-    }

-

-    /**

-     * Returns the list of Messages and Presences that were sent to the room.

-     *

-     * @return the list of Messages and Presences that were sent to the room.

-     */

-    public List<Packet> getPackets() {

-        return Collections.unmodifiableList(packets);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<transcript xmlns=\"http://jivesoftware.com/protocol/workgroup\" sessionID=\"")

-                .append(sessionID)

-                .append("\">");

-

-        for (Iterator<Packet> it=packets.iterator(); it.hasNext();) {

-            Packet packet = it.next();

-            buf.append(packet.toXML());

-        }

-

-        buf.append("</transcript>");

-

-        return buf.toString();

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents the conversation transcript that occured in a group chat room between an Agent
+ * and a user that requested assistance. The transcript contains all the Messages that were sent
+ * to the room as well as the sent presences. 
+ *
+ * @author Gaston Dombiak
+ */
+public class Transcript extends IQ {
+    private String sessionID;
+    private List<Packet> packets;
+
+    /**
+     * Creates a transcript request for the given sessionID.
+     *
+     * @param sessionID the id of the session to get the conversation transcript.
+     */
+    public Transcript(String sessionID) {
+        this.sessionID = sessionID;
+        this.packets = new ArrayList<Packet>();
+    }
+
+    /**
+     * Creates a new transcript for the given sessionID and list of packets. The list of packets
+     * may include Messages and/or Presences.
+     *
+     * @param sessionID the id of the session that generated this conversation transcript.
+     * @param packets the list of messages and presences send to the room.
+     */
+    public Transcript(String sessionID, List<Packet> packets) {
+        this.sessionID = sessionID;
+        this.packets = packets;
+    }
+
+    /**
+     * Returns id of the session that generated this conversation transcript. The sessionID is a
+     * value generated by the server when a new request is received.
+     *
+     * @return id of the session that generated this conversation transcript.
+     */
+    public String getSessionID() {
+        return sessionID;
+    }
+
+    /**
+     * Returns the list of Messages and Presences that were sent to the room.
+     *
+     * @return the list of Messages and Presences that were sent to the room.
+     */
+    public List<Packet> getPackets() {
+        return Collections.unmodifiableList(packets);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<transcript xmlns=\"http://jivesoftware.com/protocol/workgroup\" sessionID=\"")
+                .append(sessionID)
+                .append("\">");
+
+        for (Iterator<Packet> it=packets.iterator(); it.hasNext();) {
+            Packet packet = it.next();
+            buf.append(packet.toXML());
+        }
+
+        buf.append("</transcript>");
+
+        return buf.toString();
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java
index 791b06e..0e292ca 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java
@@ -1,66 +1,66 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.packet.Packet;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-import java.util.List;

-

-/**

- * An IQProvider for transcripts.

- *

- * @author Gaston Dombiak

- */

-public class TranscriptProvider implements IQProvider {

-

-    public TranscriptProvider() {

-        super();

-    }

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        String sessionID = parser.getAttributeValue("", "sessionID");

-        List<Packet> packets = new ArrayList<Packet>();

-

-        boolean done = false;

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("message")) {

-                    packets.add(PacketParserUtils.parseMessage(parser));

-                }

-                else if (parser.getName().equals("presence")) {

-                    packets.add(PacketParserUtils.parsePresence(parser));

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("transcript")) {

-                    done = true;

-                }

-            }

-        }

-

-        return new Transcript(sessionID, packets);

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An IQProvider for transcripts.
+ *
+ * @author Gaston Dombiak
+ */
+public class TranscriptProvider implements IQProvider {
+
+    public TranscriptProvider() {
+        super();
+    }
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        String sessionID = parser.getAttributeValue("", "sessionID");
+        List<Packet> packets = new ArrayList<Packet>();
+
+        boolean done = false;
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("message")) {
+                    packets.add(PacketParserUtils.parseMessage(parser));
+                }
+                else if (parser.getName().equals("presence")) {
+                    packets.add(PacketParserUtils.parsePresence(parser));
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("transcript")) {
+                    done = true;
+                }
+            }
+        }
+
+        return new Transcript(sessionID, packets);
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java
index 72693c4..d7a3db6 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java
@@ -1,87 +1,87 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.jivesoftware.smack.util.PacketParserUtils;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * IQ packet for retrieving the transcript search form, submiting the completed search form

- * or retrieving the answer of a transcript search.

- *

- * @author Gaston Dombiak

- */

-public class TranscriptSearch extends IQ {

-

-    /**

-    * Element name of the packet extension.

-    */

-   public static final String ELEMENT_NAME = "transcript-search";

-

-   /**

-    * Namespace of the packet extension.

-    */

-   public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");

-        // Add packet extensions, if any are defined.

-        buf.append(getExtensionsXML());

-        buf.append("</").append(ELEMENT_NAME).append("> ");

-

-        return buf.toString();

-    }

-

-    /**

-     * An IQProvider for TranscriptSearch packets.

-     *

-     * @author Gaston Dombiak

-     */

-    public static class Provider implements IQProvider {

-

-        public Provider() {

-            super();

-        }

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            TranscriptSearch answer = new TranscriptSearch();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if (eventType == XmlPullParser.START_TAG) {

-                    // Parse the packet extension

-                    answer.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));

-                }

-                else if (eventType == XmlPullParser.END_TAG) {

-                    if (parser.getName().equals(ELEMENT_NAME)) {

-                        done = true;

-                    }

-                }

-            }

-

-            return answer;

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * IQ packet for retrieving the transcript search form, submiting the completed search form
+ * or retrieving the answer of a transcript search.
+ *
+ * @author Gaston Dombiak
+ */
+public class TranscriptSearch extends IQ {
+
+    /**
+    * Element name of the packet extension.
+    */
+   public static final String ELEMENT_NAME = "transcript-search";
+
+   /**
+    * Namespace of the packet extension.
+    */
+   public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
+        // Add packet extensions, if any are defined.
+        buf.append(getExtensionsXML());
+        buf.append("</").append(ELEMENT_NAME).append("> ");
+
+        return buf.toString();
+    }
+
+    /**
+     * An IQProvider for TranscriptSearch packets.
+     *
+     * @author Gaston Dombiak
+     */
+    public static class Provider implements IQProvider {
+
+        public Provider() {
+            super();
+        }
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            TranscriptSearch answer = new TranscriptSearch();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if (eventType == XmlPullParser.START_TAG) {
+                    // Parse the packet extension
+                    answer.addExtension(PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser));
+                }
+                else if (eventType == XmlPullParser.END_TAG) {
+                    if (parser.getName().equals(ELEMENT_NAME)) {
+                        done = true;
+                    }
+                }
+            }
+
+            return answer;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcripts.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcripts.java
index 66ddaad..86311c9 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcripts.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/Transcripts.java
@@ -1,247 +1,247 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-

-import java.text.SimpleDateFormat;

-import java.util.*;

-

-/**

- * Represents a list of conversation transcripts that a user had in all his history. Each

- * transcript summary includes the sessionID which may be used for getting more detailed

- * information about the conversation. {@link org.jivesoftware.smackx.workgroup.packet.Transcript}

- *

- * @author Gaston Dombiak

- */

-public class Transcripts extends IQ {

-

-    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");

-    static {

-        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));

-    }

-

-    private String userID;

-    private List<Transcripts.TranscriptSummary> summaries;

-

-

-    /**

-     * Creates a transcripts request for the given userID.

-     *

-     * @param userID the id of the user to get his conversations transcripts.

-     */

-    public Transcripts(String userID) {

-        this.userID = userID;

-        this.summaries = new ArrayList<Transcripts.TranscriptSummary>();

-    }

-

-    /**

-     * Creates a Transcripts which will contain the transcript summaries of the given user.

-     *

-     * @param userID the id of the user. Could be a real JID or a unique String that identifies

-     *        anonymous users.

-     * @param summaries the list of TranscriptSummaries.

-     */

-    public Transcripts(String userID, List<Transcripts.TranscriptSummary> summaries) {

-        this.userID = userID;

-        this.summaries = summaries;

-    }

-

-    /**

-     * Returns the id of the user that was involved in the conversations. The userID could be a

-     * real JID if the connected user was not anonymous. Otherwise, the userID will be a String

-     * that was provided by the anonymous user as a way to idenitify the user across many user

-     * sessions.

-     *

-     * @return the id of the user that was involved in the conversations.

-     */

-    public String getUserID() {

-        return userID;

-    }

-

-    /**

-     * Returns a list of TranscriptSummary. A TranscriptSummary does not contain the conversation

-     * transcript but some summary information like the sessionID and the time when the

-     * conversation started and finished. Once you have the sessionID it is possible to get the

-     * full conversation transcript.

-     *

-     * @return a list of TranscriptSummary.

-     */

-    public List<Transcripts.TranscriptSummary> getSummaries() {

-        return Collections.unmodifiableList(summaries);

-    }

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<transcripts xmlns=\"http://jivesoftware.com/protocol/workgroup\" userID=\"")

-                .append(userID)

-                .append("\">");

-

-        for (TranscriptSummary transcriptSummary : summaries) {

-            buf.append(transcriptSummary.toXML());

-        }

-

-        buf.append("</transcripts>");

-

-        return buf.toString();

-    }

-

-    /**

-     * A TranscriptSummary contains some information about a conversation such as the ID of the

-     * session or the date when the conversation started and finished. You will need to use the

-     * sessionID to get the full conversation transcript.

-     */

-    public static class TranscriptSummary {

-        private String sessionID;

-        private Date joinTime;

-        private Date leftTime;

-        private List<AgentDetail> agentDetails;

-

-        public TranscriptSummary(String sessionID, Date joinTime, Date leftTime, List<AgentDetail> agentDetails) {

-            this.sessionID = sessionID;

-            this.joinTime = joinTime;

-            this.leftTime = leftTime;

-            this.agentDetails = agentDetails;

-        }

-

-        /**

-         * Returns the ID of the session that is related to this conversation transcript. The

-         * sessionID could be used for getting the full conversation transcript.

-         *

-         * @return the ID of the session that is related to this conversation transcript.

-         */

-        public String getSessionID() {

-            return sessionID;

-        }

-

-        /**

-         * Returns the Date when the conversation started.

-         *

-         * @return the Date when the conversation started.

-         */

-        public Date getJoinTime() {

-            return joinTime;

-        }

-

-        /**

-         * Returns the Date when the conversation finished.

-         *

-         * @return the Date when the conversation finished.

-         */

-        public Date getLeftTime() {

-            return leftTime;

-        }

-

-        /**

-         * Returns a list of AgentDetails. For each Agent that was involved in the conversation

-         * the list will include an AgentDetail. An AgentDetail contains the JID of the agent

-         * as well as the time when the Agent joined and left the conversation.

-         *

-         * @return a list of AgentDetails.

-         */

-        public List<AgentDetail> getAgentDetails() {

-            return agentDetails;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<transcript sessionID=\"")

-                    .append(sessionID)

-                    .append("\">");

-

-            if (joinTime != null) {

-                buf.append("<joinTime>").append(UTC_FORMAT.format(joinTime)).append("</joinTime>");

-            }

-            if (leftTime != null) {

-                buf.append("<leftTime>").append(UTC_FORMAT.format(leftTime)).append("</leftTime>");

-            }

-            buf.append("<agents>");

-            for (AgentDetail agentDetail : agentDetails) {

-                buf.append(agentDetail.toXML());

-            }

-            buf.append("</agents></transcript>");

-

-            return buf.toString();

-        }

-    }

-

-    /**

-     * An AgentDetail contains information of an Agent that was involved in a conversation. 

-     */

-    public static class AgentDetail {

-        private String agentJID;

-        private Date joinTime;

-        private Date leftTime;

-

-        public AgentDetail(String agentJID, Date joinTime, Date leftTime) {

-            this.agentJID = agentJID;

-            this.joinTime = joinTime;

-            this.leftTime = leftTime;

-        }

-

-        /**

-         * Returns the bare JID of the Agent that was involved in the conversation.

-         *

-         * @return the bared JID of the Agent that was involved in the conversation.

-         */

-        public String getAgentJID() {

-            return agentJID;

-        }

-

-        /**

-         * Returns the Date when the Agent joined the conversation.

-         *

-         * @return the Date when the Agent joined the conversation.

-         */

-        public Date getJoinTime() {

-            return joinTime;

-        }

-

-        /**

-         * Returns the Date when the Agent left the conversation.

-         *

-         * @return the Date when the Agent left the conversation.

-         */

-        public Date getLeftTime() {

-            return leftTime;

-        }

-

-        public String toXML() {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<agent>");

-

-            if (agentJID != null) {

-                buf.append("<agentJID>").append(agentJID).append("</agentJID>");

-            }

-            if (joinTime != null) {

-                buf.append("<joinTime>").append(UTC_FORMAT.format(joinTime)).append("</joinTime>");

-            }

-            if (leftTime != null) {

-                buf.append("<leftTime>").append(UTC_FORMAT.format(leftTime)).append("</leftTime>");

-            }

-            buf.append("</agent>");

-

-            return buf.toString();

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Represents a list of conversation transcripts that a user had in all his history. Each
+ * transcript summary includes the sessionID which may be used for getting more detailed
+ * information about the conversation. {@link org.jivesoftware.smackx.workgroup.packet.Transcript}
+ *
+ * @author Gaston Dombiak
+ */
+public class Transcripts extends IQ {
+
+    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+    static {
+        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
+    }
+
+    private String userID;
+    private List<Transcripts.TranscriptSummary> summaries;
+
+
+    /**
+     * Creates a transcripts request for the given userID.
+     *
+     * @param userID the id of the user to get his conversations transcripts.
+     */
+    public Transcripts(String userID) {
+        this.userID = userID;
+        this.summaries = new ArrayList<Transcripts.TranscriptSummary>();
+    }
+
+    /**
+     * Creates a Transcripts which will contain the transcript summaries of the given user.
+     *
+     * @param userID the id of the user. Could be a real JID or a unique String that identifies
+     *        anonymous users.
+     * @param summaries the list of TranscriptSummaries.
+     */
+    public Transcripts(String userID, List<Transcripts.TranscriptSummary> summaries) {
+        this.userID = userID;
+        this.summaries = summaries;
+    }
+
+    /**
+     * Returns the id of the user that was involved in the conversations. The userID could be a
+     * real JID if the connected user was not anonymous. Otherwise, the userID will be a String
+     * that was provided by the anonymous user as a way to idenitify the user across many user
+     * sessions.
+     *
+     * @return the id of the user that was involved in the conversations.
+     */
+    public String getUserID() {
+        return userID;
+    }
+
+    /**
+     * Returns a list of TranscriptSummary. A TranscriptSummary does not contain the conversation
+     * transcript but some summary information like the sessionID and the time when the
+     * conversation started and finished. Once you have the sessionID it is possible to get the
+     * full conversation transcript.
+     *
+     * @return a list of TranscriptSummary.
+     */
+    public List<Transcripts.TranscriptSummary> getSummaries() {
+        return Collections.unmodifiableList(summaries);
+    }
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<transcripts xmlns=\"http://jivesoftware.com/protocol/workgroup\" userID=\"")
+                .append(userID)
+                .append("\">");
+
+        for (TranscriptSummary transcriptSummary : summaries) {
+            buf.append(transcriptSummary.toXML());
+        }
+
+        buf.append("</transcripts>");
+
+        return buf.toString();
+    }
+
+    /**
+     * A TranscriptSummary contains some information about a conversation such as the ID of the
+     * session or the date when the conversation started and finished. You will need to use the
+     * sessionID to get the full conversation transcript.
+     */
+    public static class TranscriptSummary {
+        private String sessionID;
+        private Date joinTime;
+        private Date leftTime;
+        private List<AgentDetail> agentDetails;
+
+        public TranscriptSummary(String sessionID, Date joinTime, Date leftTime, List<AgentDetail> agentDetails) {
+            this.sessionID = sessionID;
+            this.joinTime = joinTime;
+            this.leftTime = leftTime;
+            this.agentDetails = agentDetails;
+        }
+
+        /**
+         * Returns the ID of the session that is related to this conversation transcript. The
+         * sessionID could be used for getting the full conversation transcript.
+         *
+         * @return the ID of the session that is related to this conversation transcript.
+         */
+        public String getSessionID() {
+            return sessionID;
+        }
+
+        /**
+         * Returns the Date when the conversation started.
+         *
+         * @return the Date when the conversation started.
+         */
+        public Date getJoinTime() {
+            return joinTime;
+        }
+
+        /**
+         * Returns the Date when the conversation finished.
+         *
+         * @return the Date when the conversation finished.
+         */
+        public Date getLeftTime() {
+            return leftTime;
+        }
+
+        /**
+         * Returns a list of AgentDetails. For each Agent that was involved in the conversation
+         * the list will include an AgentDetail. An AgentDetail contains the JID of the agent
+         * as well as the time when the Agent joined and left the conversation.
+         *
+         * @return a list of AgentDetails.
+         */
+        public List<AgentDetail> getAgentDetails() {
+            return agentDetails;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<transcript sessionID=\"")
+                    .append(sessionID)
+                    .append("\">");
+
+            if (joinTime != null) {
+                buf.append("<joinTime>").append(UTC_FORMAT.format(joinTime)).append("</joinTime>");
+            }
+            if (leftTime != null) {
+                buf.append("<leftTime>").append(UTC_FORMAT.format(leftTime)).append("</leftTime>");
+            }
+            buf.append("<agents>");
+            for (AgentDetail agentDetail : agentDetails) {
+                buf.append(agentDetail.toXML());
+            }
+            buf.append("</agents></transcript>");
+
+            return buf.toString();
+        }
+    }
+
+    /**
+     * An AgentDetail contains information of an Agent that was involved in a conversation. 
+     */
+    public static class AgentDetail {
+        private String agentJID;
+        private Date joinTime;
+        private Date leftTime;
+
+        public AgentDetail(String agentJID, Date joinTime, Date leftTime) {
+            this.agentJID = agentJID;
+            this.joinTime = joinTime;
+            this.leftTime = leftTime;
+        }
+
+        /**
+         * Returns the bare JID of the Agent that was involved in the conversation.
+         *
+         * @return the bared JID of the Agent that was involved in the conversation.
+         */
+        public String getAgentJID() {
+            return agentJID;
+        }
+
+        /**
+         * Returns the Date when the Agent joined the conversation.
+         *
+         * @return the Date when the Agent joined the conversation.
+         */
+        public Date getJoinTime() {
+            return joinTime;
+        }
+
+        /**
+         * Returns the Date when the Agent left the conversation.
+         *
+         * @return the Date when the Agent left the conversation.
+         */
+        public Date getLeftTime() {
+            return leftTime;
+        }
+
+        public String toXML() {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<agent>");
+
+            if (agentJID != null) {
+                buf.append("<agentJID>").append(agentJID).append("</agentJID>");
+            }
+            if (joinTime != null) {
+                buf.append("<joinTime>").append(UTC_FORMAT.format(joinTime)).append("</joinTime>");
+            }
+            if (leftTime != null) {
+                buf.append("<leftTime>").append(UTC_FORMAT.format(leftTime)).append("</leftTime>");
+            }
+            buf.append("</agent>");
+
+            return buf.toString();
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java
index cb8f429..c0ef7a8 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java
@@ -1,148 +1,148 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-import org.xmlpull.v1.XmlPullParserException;

-

-import java.io.IOException;

-import java.text.ParseException;

-import java.text.SimpleDateFormat;

-import java.util.ArrayList;

-import java.util.Date;

-import java.util.List;

-import java.util.TimeZone;

-

-/**

- * An IQProvider for transcripts summaries.

- *

- * @author Gaston Dombiak

- */

-public class TranscriptsProvider implements IQProvider {

-

-    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");

-    static {

-        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));

-    }

-

-    public TranscriptsProvider() {

-        super();

-    }

-

-    public IQ parseIQ(XmlPullParser parser) throws Exception {

-        String userID = parser.getAttributeValue("", "userID");

-        List<Transcripts.TranscriptSummary> summaries = new ArrayList<Transcripts.TranscriptSummary>();

-

-        boolean done = false;

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("transcript")) {

-                    summaries.add(parseSummary(parser));

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("transcripts")) {

-                    done = true;

-                }

-            }

-        }

-

-        return new Transcripts(userID, summaries);

-    }

-

-    private Transcripts.TranscriptSummary parseSummary(XmlPullParser parser) throws IOException,

-            XmlPullParserException {

-        String sessionID =  parser.getAttributeValue("", "sessionID");

-        Date joinTime = null;

-        Date leftTime = null;

-        List<Transcripts.AgentDetail> agents = new ArrayList<Transcripts.AgentDetail>();

-

-        boolean done = false;

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("joinTime")) {

-                    try {

-                        joinTime = UTC_FORMAT.parse(parser.nextText());

-                    } catch (ParseException e) {}

-                }

-                else if (parser.getName().equals("leftTime")) {

-                    try {

-                        leftTime = UTC_FORMAT.parse(parser.nextText());

-                    } catch (ParseException e) {}

-                }

-                else if (parser.getName().equals("agents")) {

-                    agents = parseAgents(parser);

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("transcript")) {

-                    done = true;

-                }

-            }

-        }

-

-        return new Transcripts.TranscriptSummary(sessionID, joinTime, leftTime, agents);

-    }

-

-    private List<Transcripts.AgentDetail> parseAgents(XmlPullParser parser) throws IOException, XmlPullParserException {

-        List<Transcripts.AgentDetail> agents = new ArrayList<Transcripts.AgentDetail>();

-        String agentJID =  null;

-        Date joinTime = null;

-        Date leftTime = null;

-

-        boolean done = false;

-        while (!done) {

-            int eventType = parser.next();

-            if (eventType == XmlPullParser.START_TAG) {

-                if (parser.getName().equals("agentJID")) {

-                    agentJID = parser.nextText();

-                }

-                else if (parser.getName().equals("joinTime")) {

-                    try {

-                        joinTime = UTC_FORMAT.parse(parser.nextText());

-                    } catch (ParseException e) {}

-                }

-                else if (parser.getName().equals("leftTime")) {

-                    try {

-                        leftTime = UTC_FORMAT.parse(parser.nextText());

-                    } catch (ParseException e) {}

-                }

-                else if (parser.getName().equals("agent")) {

-                    agentJID =  null;

-                    joinTime = null;

-                    leftTime = null;

-                }

-            }

-            else if (eventType == XmlPullParser.END_TAG) {

-                if (parser.getName().equals("agents")) {

-                    done = true;

-                }

-                else if (parser.getName().equals("agent")) {

-                    agents.add(new Transcripts.AgentDetail(agentJID, joinTime, leftTime));

-                }

-            }

-        }

-        return agents;

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+/**
+ * An IQProvider for transcripts summaries.
+ *
+ * @author Gaston Dombiak
+ */
+public class TranscriptsProvider implements IQProvider {
+
+    private static final SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+    static {
+        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
+    }
+
+    public TranscriptsProvider() {
+        super();
+    }
+
+    public IQ parseIQ(XmlPullParser parser) throws Exception {
+        String userID = parser.getAttributeValue("", "userID");
+        List<Transcripts.TranscriptSummary> summaries = new ArrayList<Transcripts.TranscriptSummary>();
+
+        boolean done = false;
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("transcript")) {
+                    summaries.add(parseSummary(parser));
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("transcripts")) {
+                    done = true;
+                }
+            }
+        }
+
+        return new Transcripts(userID, summaries);
+    }
+
+    private Transcripts.TranscriptSummary parseSummary(XmlPullParser parser) throws IOException,
+            XmlPullParserException {
+        String sessionID =  parser.getAttributeValue("", "sessionID");
+        Date joinTime = null;
+        Date leftTime = null;
+        List<Transcripts.AgentDetail> agents = new ArrayList<Transcripts.AgentDetail>();
+
+        boolean done = false;
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("joinTime")) {
+                    try {
+                        joinTime = UTC_FORMAT.parse(parser.nextText());
+                    } catch (ParseException e) {}
+                }
+                else if (parser.getName().equals("leftTime")) {
+                    try {
+                        leftTime = UTC_FORMAT.parse(parser.nextText());
+                    } catch (ParseException e) {}
+                }
+                else if (parser.getName().equals("agents")) {
+                    agents = parseAgents(parser);
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("transcript")) {
+                    done = true;
+                }
+            }
+        }
+
+        return new Transcripts.TranscriptSummary(sessionID, joinTime, leftTime, agents);
+    }
+
+    private List<Transcripts.AgentDetail> parseAgents(XmlPullParser parser) throws IOException, XmlPullParserException {
+        List<Transcripts.AgentDetail> agents = new ArrayList<Transcripts.AgentDetail>();
+        String agentJID =  null;
+        Date joinTime = null;
+        Date leftTime = null;
+
+        boolean done = false;
+        while (!done) {
+            int eventType = parser.next();
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals("agentJID")) {
+                    agentJID = parser.nextText();
+                }
+                else if (parser.getName().equals("joinTime")) {
+                    try {
+                        joinTime = UTC_FORMAT.parse(parser.nextText());
+                    } catch (ParseException e) {}
+                }
+                else if (parser.getName().equals("leftTime")) {
+                    try {
+                        leftTime = UTC_FORMAT.parse(parser.nextText());
+                    } catch (ParseException e) {}
+                }
+                else if (parser.getName().equals("agent")) {
+                    agentJID =  null;
+                    joinTime = null;
+                    leftTime = null;
+                }
+            }
+            else if (eventType == XmlPullParser.END_TAG) {
+                if (parser.getName().equals("agents")) {
+                    done = true;
+                }
+                else if (parser.getName().equals("agent")) {
+                    agents.add(new Transcripts.AgentDetail(agentJID, joinTime, leftTime));
+                }
+            }
+        }
+        return agents;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/UserID.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/UserID.java
index 8bf4589..4d50abf 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/UserID.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/UserID.java
@@ -1,77 +1,77 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-public class UserID implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "user";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    private String userID;

-

-    public UserID(String userID) {

-        this.userID = userID;

-    }

-

-    public String getUserID() {

-        return this.userID;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\" ");

-        buf.append("id=\"").append(this.getUserID());

-        buf.append("\"/>");

-

-        return buf.toString();

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {

-            String userID = parser.getAttributeValue("", "id");

-

-            // Advance to end of extension.

-            parser.next();

-

-            return new UserID(userID);

-        }

-    }

-}

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+public class UserID implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "user";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    private String userID;
+
+    public UserID(String userID) {
+        this.userID = userID;
+    }
+
+    public String getUserID() {
+        return this.userID;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\" ");
+        buf.append("id=\"").append(this.getUserID());
+        buf.append("\"/>");
+
+        return buf.toString();
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+            String userID = parser.getAttributeValue("", "id");
+
+            // Advance to end of extension.
+            parser.next();
+
+            return new UserID(userID);
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java
index b0ea447..87086bd 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java
@@ -1,86 +1,86 @@
-/**

- * $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.smackx.workgroup.packet;

-

-import org.jivesoftware.smack.packet.PacketExtension;

-import org.jivesoftware.smack.provider.PacketExtensionProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-/**

- * A packet extension that contains information about the user and agent in a

- * workgroup chat. The packet extension is attached to group chat invitations.

- */

-public class WorkgroupInformation implements PacketExtension {

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "workgroup";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";

-

-    private String workgroupJID;

-

-    public WorkgroupInformation(String workgroupJID){

-        this.workgroupJID = workgroupJID;

-    }

-

-    public String getWorkgroupJID() {

-        return workgroupJID;

-    }

-

-    public String getElementName() {

-        return ELEMENT_NAME;

-    }

-

-    public String getNamespace() {

-        return NAMESPACE;

-    }

-

-    public String toXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append('<').append(ELEMENT_NAME);

-        buf.append(" jid=\"").append(getWorkgroupJID()).append("\"");

-        buf.append(" xmlns=\"").append(NAMESPACE).append("\" />");

-

-        return buf.toString();

-    }

-

-    public static class Provider implements PacketExtensionProvider {

-

-        /**

-         * PacketExtensionProvider implementation

-         */

-        public PacketExtension parseExtension (XmlPullParser parser)

-            throws Exception {

-            String workgroupJID = parser.getAttributeValue("", "jid");

-

-            // since this is a start and end tag, and we arrive on the start, this should guarantee

-            //      we leave on the end

-            parser.next();

-

-            return new WorkgroupInformation(workgroupJID);

-        }

-    }

+/**
+ * $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.smackx.workgroup.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A packet extension that contains information about the user and agent in a
+ * workgroup chat. The packet extension is attached to group chat invitations.
+ */
+public class WorkgroupInformation implements PacketExtension {
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "workgroup";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jabber.org/protocol/workgroup";
+
+    private String workgroupJID;
+
+    public WorkgroupInformation(String workgroupJID){
+        this.workgroupJID = workgroupJID;
+    }
+
+    public String getWorkgroupJID() {
+        return workgroupJID;
+    }
+
+    public String getElementName() {
+        return ELEMENT_NAME;
+    }
+
+    public String getNamespace() {
+        return NAMESPACE;
+    }
+
+    public String toXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append('<').append(ELEMENT_NAME);
+        buf.append(" jid=\"").append(getWorkgroupJID()).append("\"");
+        buf.append(" xmlns=\"").append(NAMESPACE).append("\" />");
+
+        return buf.toString();
+    }
+
+    public static class Provider implements PacketExtensionProvider {
+
+        /**
+         * PacketExtensionProvider implementation
+         */
+        public PacketExtension parseExtension (XmlPullParser parser)
+            throws Exception {
+            String workgroupJID = parser.getAttributeValue("", "jid");
+
+            // since this is a start and end tag, and we arrive on the start, this should guarantee
+            //      we leave on the end
+            parser.next();
+
+            return new WorkgroupInformation(workgroupJID);
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java
index 921134a..30674a1 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java
@@ -1,56 +1,56 @@
-/**

- * $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.smackx.workgroup.settings;

-

-public class ChatSetting {

-    private String key;

-    private String value;

-    private int type;

-

-    public ChatSetting(String key, String value, int type){

-        setKey(key);

-        setValue(value);

-        setType(type);

-    }

-

-    public String getKey() {

-        return key;

-    }

-

-    public void setKey(String key) {

-        this.key = key;

-    }

-

-    public String getValue() {

-        return value;

-    }

-

-    public void setValue(String value) {

-        this.value = value;

-    }

-

-    public int getType() {

-        return type;

-    }

-

-    public void setType(int type) {

-        this.type = type;

-    }

-}

+/**
+ * $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.smackx.workgroup.settings;
+
+public class ChatSetting {
+    private String key;
+    private String value;
+    private int type;
+
+    public ChatSetting(String key, String value, int type){
+        setKey(key);
+        setValue(value);
+        setType(type);
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java
index ccc7a40..eec5884 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java
@@ -1,179 +1,179 @@
-/**

- * $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.smackx.workgroup.settings;

-

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-import java.util.ArrayList;

-import java.util.Collection;

-import java.util.Iterator;

-import java.util.List;

-

-public class ChatSettings extends IQ {

-

-    /**

-     * Defined as image type.

-     */

-    public static final int IMAGE_SETTINGS = 0;

-

-    /**

-     * Defined as Text settings type.

-     */

-    public static final int TEXT_SETTINGS = 1;

-

-    /**

-     * Defined as Bot settings type.

-     */

-    public static final int BOT_SETTINGS = 2;

-

-    private List<ChatSetting> settings;

-    private String key;

-    private int type = -1;

-

-    public ChatSettings() {

-        settings = new ArrayList<ChatSetting>();

-    }

-

-    public ChatSettings(String key) {

-        setKey(key);

-    }

-

-    public void setKey(String key) {

-        this.key = key;

-    }

-

-    public void setType(int type) {

-        this.type = type;

-    }

-

-    public void addSetting(ChatSetting setting) {

-        settings.add(setting);

-    }

-

-    public Collection<ChatSetting> getSettings() {

-        return settings;

-    }

-

-    public ChatSetting getChatSetting(String key) {

-        Collection<ChatSetting> col = getSettings();

-        if (col != null) {

-            Iterator<ChatSetting> iter = col.iterator();

-            while (iter.hasNext()) {

-                ChatSetting chatSetting = iter.next();

-                if (chatSetting.getKey().equals(key)) {

-                    return chatSetting;

-                }

-            }

-        }

-        return null;

-    }

-

-    public ChatSetting getFirstEntry() {

-        if (settings.size() > 0) {

-            return (ChatSetting)settings.get(0);

-        }

-        return null;

-    }

-

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "chat-settings";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");

-        buf.append('"');

-        buf.append(NAMESPACE);

-        buf.append('"');

-        if (key != null) {

-            buf.append(" key=\"" + key + "\"");

-        }

-

-        if (type != -1) {

-            buf.append(" type=\"" + type + "\"");

-        }

-

-        buf.append("></").append(ELEMENT_NAME).append("> ");

-        return buf.toString();

-    }

-

-    /**

-     * Packet extension provider for AgentStatusRequest packets.

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-

-            ChatSettings chatSettings = new ChatSettings();

-

-            boolean done = false;

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("chat-setting".equals(parser.getName()))) {

-                    chatSettings.addSetting(parseChatSetting(parser));

-

-                }

-                else if (eventType == XmlPullParser.END_TAG && ELEMENT_NAME.equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return chatSettings;

-        }

-

-        private ChatSetting parseChatSetting(XmlPullParser parser) throws Exception {

-

-            boolean done = false;

-            String key = null;

-            String value = null;

-            int type = 0;

-

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("key".equals(parser.getName()))) {

-                    key = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("value".equals(parser.getName()))) {

-                    value = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("type".equals(parser.getName()))) {

-                    type = Integer.parseInt(parser.nextText());

-                }

-                else if (eventType == XmlPullParser.END_TAG && "chat-setting".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-            return new ChatSetting(key, value, type);

-        }

-    }

-}

-

+/**
+ * $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.smackx.workgroup.settings;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public class ChatSettings extends IQ {
+
+    /**
+     * Defined as image type.
+     */
+    public static final int IMAGE_SETTINGS = 0;
+
+    /**
+     * Defined as Text settings type.
+     */
+    public static final int TEXT_SETTINGS = 1;
+
+    /**
+     * Defined as Bot settings type.
+     */
+    public static final int BOT_SETTINGS = 2;
+
+    private List<ChatSetting> settings;
+    private String key;
+    private int type = -1;
+
+    public ChatSettings() {
+        settings = new ArrayList<ChatSetting>();
+    }
+
+    public ChatSettings(String key) {
+        setKey(key);
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public void addSetting(ChatSetting setting) {
+        settings.add(setting);
+    }
+
+    public Collection<ChatSetting> getSettings() {
+        return settings;
+    }
+
+    public ChatSetting getChatSetting(String key) {
+        Collection<ChatSetting> col = getSettings();
+        if (col != null) {
+            Iterator<ChatSetting> iter = col.iterator();
+            while (iter.hasNext()) {
+                ChatSetting chatSetting = iter.next();
+                if (chatSetting.getKey().equals(key)) {
+                    return chatSetting;
+                }
+            }
+        }
+        return null;
+    }
+
+    public ChatSetting getFirstEntry() {
+        if (settings.size() > 0) {
+            return (ChatSetting)settings.get(0);
+        }
+        return null;
+    }
+
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "chat-settings";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");
+        buf.append('"');
+        buf.append(NAMESPACE);
+        buf.append('"');
+        if (key != null) {
+            buf.append(" key=\"" + key + "\"");
+        }
+
+        if (type != -1) {
+            buf.append(" type=\"" + type + "\"");
+        }
+
+        buf.append("></").append(ELEMENT_NAME).append("> ");
+        return buf.toString();
+    }
+
+    /**
+     * Packet extension provider for AgentStatusRequest packets.
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+
+            ChatSettings chatSettings = new ChatSettings();
+
+            boolean done = false;
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("chat-setting".equals(parser.getName()))) {
+                    chatSettings.addSetting(parseChatSetting(parser));
+
+                }
+                else if (eventType == XmlPullParser.END_TAG && ELEMENT_NAME.equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return chatSettings;
+        }
+
+        private ChatSetting parseChatSetting(XmlPullParser parser) throws Exception {
+
+            boolean done = false;
+            String key = null;
+            String value = null;
+            int type = 0;
+
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("key".equals(parser.getName()))) {
+                    key = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("value".equals(parser.getName()))) {
+                    value = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("type".equals(parser.getName()))) {
+                    type = Integer.parseInt(parser.nextText());
+                }
+                else if (eventType == XmlPullParser.END_TAG && "chat-setting".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+            return new ChatSetting(key, value, type);
+        }
+    }
+}
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java
index 15136fd..14102af 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java
@@ -1,155 +1,155 @@
-/**

- * $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.smackx.workgroup.settings;

-

-import org.jivesoftware.smackx.workgroup.util.ModelUtil;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-public class OfflineSettings extends IQ {

-    private String redirectURL;

-

-    private String offlineText;

-    private String emailAddress;

-    private String subject;

-

-    public String getRedirectURL() {

-        if (!ModelUtil.hasLength(redirectURL)) {

-            return "";

-        }

-        return redirectURL;

-    }

-

-    public void setRedirectURL(String redirectURL) {

-        this.redirectURL = redirectURL;

-    }

-

-    public String getOfflineText() {

-        if (!ModelUtil.hasLength(offlineText)) {

-            return "";

-        }

-        return offlineText;

-    }

-

-    public void setOfflineText(String offlineText) {

-        this.offlineText = offlineText;

-    }

-

-    public String getEmailAddress() {

-        if (!ModelUtil.hasLength(emailAddress)) {

-            return "";

-        }

-        return emailAddress;

-    }

-

-    public void setEmailAddress(String emailAddress) {

-        this.emailAddress = emailAddress;

-    }

-

-    public String getSubject() {

-        if (!ModelUtil.hasLength(subject)) {

-            return "";

-        }

-        return subject;

-    }

-

-    public void setSubject(String subject) {

-        this.subject = subject;

-    }

-

-    public boolean redirects() {

-        return (ModelUtil.hasLength(getRedirectURL()));

-    }

-

-    public boolean isConfigured(){

-        return ModelUtil.hasLength(getEmailAddress()) &&

-               ModelUtil.hasLength(getSubject()) &&

-               ModelUtil.hasLength(getOfflineText());

-    }

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "offline-settings";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");

-        buf.append('"');

-        buf.append(NAMESPACE);

-        buf.append('"');

-        buf.append("></").append(ELEMENT_NAME).append("> ");

-        return buf.toString();

-    }

-

-

-    /**

-     * Packet extension provider for AgentStatusRequest packets.

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-

-            OfflineSettings offlineSettings = new OfflineSettings();

-

-            boolean done = false;

-            String redirectPage = null;

-            String subject = null;

-            String offlineText = null;

-            String emailAddress = null;

-

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("redirectPage".equals(parser.getName()))) {

-                    redirectPage = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("subject".equals(parser.getName()))) {

-                    subject = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("offlineText".equals(parser.getName()))) {

-                    offlineText = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("emailAddress".equals(parser.getName()))) {

-                    emailAddress = parser.nextText();

-                }

-                else if (eventType == XmlPullParser.END_TAG && "offline-settings".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-

-            offlineSettings.setEmailAddress(emailAddress);

-            offlineSettings.setRedirectURL(redirectPage);

-            offlineSettings.setSubject(subject);

-            offlineSettings.setOfflineText(offlineText);

-            return offlineSettings;

-        }

-    }

-}

-

+/**
+ * $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.smackx.workgroup.settings;
+
+import org.jivesoftware.smackx.workgroup.util.ModelUtil;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+public class OfflineSettings extends IQ {
+    private String redirectURL;
+
+    private String offlineText;
+    private String emailAddress;
+    private String subject;
+
+    public String getRedirectURL() {
+        if (!ModelUtil.hasLength(redirectURL)) {
+            return "";
+        }
+        return redirectURL;
+    }
+
+    public void setRedirectURL(String redirectURL) {
+        this.redirectURL = redirectURL;
+    }
+
+    public String getOfflineText() {
+        if (!ModelUtil.hasLength(offlineText)) {
+            return "";
+        }
+        return offlineText;
+    }
+
+    public void setOfflineText(String offlineText) {
+        this.offlineText = offlineText;
+    }
+
+    public String getEmailAddress() {
+        if (!ModelUtil.hasLength(emailAddress)) {
+            return "";
+        }
+        return emailAddress;
+    }
+
+    public void setEmailAddress(String emailAddress) {
+        this.emailAddress = emailAddress;
+    }
+
+    public String getSubject() {
+        if (!ModelUtil.hasLength(subject)) {
+            return "";
+        }
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public boolean redirects() {
+        return (ModelUtil.hasLength(getRedirectURL()));
+    }
+
+    public boolean isConfigured(){
+        return ModelUtil.hasLength(getEmailAddress()) &&
+               ModelUtil.hasLength(getSubject()) &&
+               ModelUtil.hasLength(getOfflineText());
+    }
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "offline-settings";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");
+        buf.append('"');
+        buf.append(NAMESPACE);
+        buf.append('"');
+        buf.append("></").append(ELEMENT_NAME).append("> ");
+        return buf.toString();
+    }
+
+
+    /**
+     * Packet extension provider for AgentStatusRequest packets.
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+
+            OfflineSettings offlineSettings = new OfflineSettings();
+
+            boolean done = false;
+            String redirectPage = null;
+            String subject = null;
+            String offlineText = null;
+            String emailAddress = null;
+
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("redirectPage".equals(parser.getName()))) {
+                    redirectPage = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("subject".equals(parser.getName()))) {
+                    subject = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("offlineText".equals(parser.getName()))) {
+                    offlineText = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("emailAddress".equals(parser.getName()))) {
+                    emailAddress = parser.nextText();
+                }
+                else if (eventType == XmlPullParser.END_TAG && "offline-settings".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+
+            offlineSettings.setEmailAddress(emailAddress);
+            offlineSettings.setRedirectURL(redirectPage);
+            offlineSettings.setSubject(subject);
+            offlineSettings.setOfflineText(offlineText);
+            return offlineSettings;
+        }
+    }
+}
+
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java
index 98d59fc..037af78 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java
@@ -1,112 +1,112 @@
-/**

- * 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.smackx.workgroup.settings;

-

-import org.jivesoftware.smackx.workgroup.util.ModelUtil;

-import org.jivesoftware.smack.packet.IQ;

-import org.jivesoftware.smack.provider.IQProvider;

-import org.xmlpull.v1.XmlPullParser;

-

-public class SearchSettings extends IQ {

-    private String forumsLocation;

-    private String kbLocation;

-

-    public boolean isSearchEnabled() {

-        return ModelUtil.hasLength(getForumsLocation()) && ModelUtil.hasLength(getKbLocation());

-    }

-

-    public String getForumsLocation() {

-        return forumsLocation;

-    }

-

-    public void setForumsLocation(String forumsLocation) {

-        this.forumsLocation = forumsLocation;

-    }

-

-    public String getKbLocation() {

-        return kbLocation;

-    }

-

-    public void setKbLocation(String kbLocation) {

-        this.kbLocation = kbLocation;

-    }

-

-    public boolean hasKB(){

-        return ModelUtil.hasLength(getKbLocation());

-    }

-

-    public boolean hasForums(){

-        return ModelUtil.hasLength(getForumsLocation());

-    }

-

-

-    /**

-     * Element name of the packet extension.

-     */

-    public static final String ELEMENT_NAME = "search-settings";

-

-    /**

-     * Namespace of the packet extension.

-     */

-    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";

-

-    public String getChildElementXML() {

-        StringBuilder buf = new StringBuilder();

-

-        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");

-        buf.append('"');

-        buf.append(NAMESPACE);

-        buf.append('"');

-        buf.append("></").append(ELEMENT_NAME).append("> ");

-        return buf.toString();

-    }

-

-

-    /**

-     * Packet extension provider for AgentStatusRequest packets.

-     */

-    public static class InternalProvider implements IQProvider {

-

-        public IQ parseIQ(XmlPullParser parser) throws Exception {

-            if (parser.getEventType() != XmlPullParser.START_TAG) {

-                throw new IllegalStateException("Parser not in proper position, or bad XML.");

-            }

-

-            SearchSettings settings = new SearchSettings();

-

-            boolean done = false;

-            String kb = null;

-            String forums = null;

-

-            while (!done) {

-                int eventType = parser.next();

-                if ((eventType == XmlPullParser.START_TAG) && ("forums".equals(parser.getName()))) {

-                    forums = parser.nextText();

-                }

-                else if ((eventType == XmlPullParser.START_TAG) && ("kb".equals(parser.getName()))) {

-                    kb = parser.nextText();

-                }

-                else if (eventType == XmlPullParser.END_TAG && "search-settings".equals(parser.getName())) {

-                    done = true;

-                }

-            }

-

-            settings.setForumsLocation(forums);

-            settings.setKbLocation(kb);

-            return settings;

-        }

-    }

-}

+/**
+ * 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.smackx.workgroup.settings;
+
+import org.jivesoftware.smackx.workgroup.util.ModelUtil;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+public class SearchSettings extends IQ {
+    private String forumsLocation;
+    private String kbLocation;
+
+    public boolean isSearchEnabled() {
+        return ModelUtil.hasLength(getForumsLocation()) && ModelUtil.hasLength(getKbLocation());
+    }
+
+    public String getForumsLocation() {
+        return forumsLocation;
+    }
+
+    public void setForumsLocation(String forumsLocation) {
+        this.forumsLocation = forumsLocation;
+    }
+
+    public String getKbLocation() {
+        return kbLocation;
+    }
+
+    public void setKbLocation(String kbLocation) {
+        this.kbLocation = kbLocation;
+    }
+
+    public boolean hasKB(){
+        return ModelUtil.hasLength(getKbLocation());
+    }
+
+    public boolean hasForums(){
+        return ModelUtil.hasLength(getForumsLocation());
+    }
+
+
+    /**
+     * Element name of the packet extension.
+     */
+    public static final String ELEMENT_NAME = "search-settings";
+
+    /**
+     * Namespace of the packet extension.
+     */
+    public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
+
+    public String getChildElementXML() {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("<").append(ELEMENT_NAME).append(" xmlns=");
+        buf.append('"');
+        buf.append(NAMESPACE);
+        buf.append('"');
+        buf.append("></").append(ELEMENT_NAME).append("> ");
+        return buf.toString();
+    }
+
+
+    /**
+     * Packet extension provider for AgentStatusRequest packets.
+     */
+    public static class InternalProvider implements IQProvider {
+
+        public IQ parseIQ(XmlPullParser parser) throws Exception {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("Parser not in proper position, or bad XML.");
+            }
+
+            SearchSettings settings = new SearchSettings();
+
+            boolean done = false;
+            String kb = null;
+            String forums = null;
+
+            while (!done) {
+                int eventType = parser.next();
+                if ((eventType == XmlPullParser.START_TAG) && ("forums".equals(parser.getName()))) {
+                    forums = parser.nextText();
+                }
+                else if ((eventType == XmlPullParser.START_TAG) && ("kb".equals(parser.getName()))) {
+                    kb = parser.nextText();
+                }
+                else if (eventType == XmlPullParser.END_TAG && "search-settings".equals(parser.getName())) {
+                    done = true;
+                }
+            }
+
+            settings.setForumsLocation(forums);
+            settings.setKbLocation(kb);
+            return settings;
+        }
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/QueueListener.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/QueueListener.java
index fa3e6a6..734897f 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/QueueListener.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/QueueListener.java
@@ -1,55 +1,55 @@
-/**

- * $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.smackx.workgroup.user;

-

-/**

- * Listener interface for those that wish to be notified of workgroup queue events.

- *

- * @see Workgroup#addQueueListener(QueueListener)

- * @author loki der quaeler

- */

-public interface QueueListener {

-

-    /**

-     * The user joined the workgroup queue.

-     */

-    public void joinedQueue();

-

-    /**

-     * The user departed the workgroup queue.

-     */

-    public void departedQueue();

-

-    /**

-     * The user's queue position has been updated to a new value.

-     *

-     * @param currentPosition the user's current position in the queue.

-     */

-    public void queuePositionUpdated(int currentPosition);

-

-    /**

-     * The user's estimated remaining wait time in the queue has been updated.

-     *

-     * @param secondsRemaining the estimated number of seconds remaining until the

-     *      the user is routed to the agent.

-     */

-    public void queueWaitTimeUpdated(int secondsRemaining);

-

-}

+/**
+ * $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.smackx.workgroup.user;
+
+/**
+ * Listener interface for those that wish to be notified of workgroup queue events.
+ *
+ * @see Workgroup#addQueueListener(QueueListener)
+ * @author loki der quaeler
+ */
+public interface QueueListener {
+
+    /**
+     * The user joined the workgroup queue.
+     */
+    public void joinedQueue();
+
+    /**
+     * The user departed the workgroup queue.
+     */
+    public void departedQueue();
+
+    /**
+     * The user's queue position has been updated to a new value.
+     *
+     * @param currentPosition the user's current position in the queue.
+     */
+    public void queuePositionUpdated(int currentPosition);
+
+    /**
+     * The user's estimated remaining wait time in the queue has been updated.
+     *
+     * @param secondsRemaining the estimated number of seconds remaining until the
+     *      the user is routed to the agent.
+     */
+    public void queueWaitTimeUpdated(int secondsRemaining);
+
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/Workgroup.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/Workgroup.java
index 237337f..4ccd034 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/Workgroup.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/user/Workgroup.java
@@ -1,868 +1,868 @@
-/**

- * 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.smackx.workgroup.user;

-

-import org.jivesoftware.smackx.workgroup.MetaData;

-import org.jivesoftware.smackx.workgroup.WorkgroupInvitation;

-import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener;

-import org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm;

-import org.jivesoftware.smackx.workgroup.packet.DepartQueuePacket;

-import org.jivesoftware.smackx.workgroup.packet.QueueUpdate;

-import org.jivesoftware.smackx.workgroup.packet.SessionID;

-import org.jivesoftware.smackx.workgroup.packet.UserID;

-import org.jivesoftware.smackx.workgroup.settings.*;

-import org.jivesoftware.smack.*;

-import org.jivesoftware.smack.filter.*;

-import org.jivesoftware.smack.packet.*;

-import org.jivesoftware.smack.util.StringUtils;

-import org.jivesoftware.smackx.Form;

-import org.jivesoftware.smackx.FormField;

-import org.jivesoftware.smackx.ServiceDiscoveryManager;

-import org.jivesoftware.smackx.muc.MultiUserChat;

-import org.jivesoftware.smackx.packet.DataForm;

-import org.jivesoftware.smackx.packet.DiscoverInfo;

-import org.jivesoftware.smackx.packet.MUCUser;

-

-import java.util.ArrayList;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

-

-/**

- * Provides workgroup services for users. Users can join the workgroup queue, depart the

- * queue, find status information about their placement in the queue, and register to

- * be notified when they are routed to an agent.<p>

- * <p/>

- * This class only provides a users perspective into a workgroup and is not intended

- * for use by agents.

- *

- * @author Matt Tucker

- * @author Derek DeMoro

- */

-public class Workgroup {

-

-    private String workgroupJID;

-    private Connection connection;

-    private boolean inQueue;

-    private List<WorkgroupInvitationListener> invitationListeners;

-    private List<QueueListener> queueListeners;

-

-    private int queuePosition = -1;

-    private int queueRemainingTime = -1;

-

-    /**

-     * Creates a new workgroup instance using the specified workgroup JID

-     * (eg support@workgroup.example.com) and XMPP connection. The connection must have

-     * undergone a successful login before being used to construct an instance of

-     * this class.

-     *

-     * @param workgroupJID the JID of the workgroup.

-     * @param connection   an XMPP connection which must have already undergone a

-     *                     successful login.

-     */

-    public Workgroup(String workgroupJID, Connection connection) {

-        // Login must have been done before passing in connection.

-        if (!connection.isAuthenticated()) {

-            throw new IllegalStateException("Must login to server before creating workgroup.");

-        }

-

-        this.workgroupJID = workgroupJID;

-        this.connection = connection;

-        inQueue = false;

-        invitationListeners = new ArrayList<WorkgroupInvitationListener>();

-        queueListeners = new ArrayList<QueueListener>();

-

-        // Register as a queue listener for internal usage by this instance.

-        addQueueListener(new QueueListener() {

-            public void joinedQueue() {

-                inQueue = true;

-            }

-

-            public void departedQueue() {

-                inQueue = false;

-                queuePosition = -1;

-                queueRemainingTime = -1;

-            }

-

-            public void queuePositionUpdated(int currentPosition) {

-                queuePosition = currentPosition;

-            }

-

-            public void queueWaitTimeUpdated(int secondsRemaining) {

-                queueRemainingTime = secondsRemaining;

-            }

-        });

-

-        /**

-         * Internal handling of an invitation.Recieving an invitation removes the user from the queue.

-         */

-        MultiUserChat.addInvitationListener(connection,

-                new org.jivesoftware.smackx.muc.InvitationListener() {

-                    public void invitationReceived(Connection conn, String room, String inviter,

-                                                   String reason, String password, Message message) {

-                        inQueue = false;

-                        queuePosition = -1;

-                        queueRemainingTime = -1;

-                    }

-                });

-

-        // Register a packet listener for all the messages sent to this client.

-        PacketFilter typeFilter = new PacketTypeFilter(Message.class);

-

-        connection.addPacketListener(new PacketListener() {

-            public void processPacket(Packet packet) {

-                handlePacket(packet);

-            }

-        }, typeFilter);

-    }

-

-    /**

-     * Returns the name of this workgroup (eg support@example.com).

-     *

-     * @return the name of the workgroup.

-     */

-    public String getWorkgroupJID() {

-        return workgroupJID;

-    }

-

-    /**

-     * Returns true if the user is currently waiting in the workgroup queue.

-     *

-     * @return true if currently waiting in the queue.

-     */

-    public boolean isInQueue() {

-        return inQueue;

-    }

-

-    /**

-     * Returns true if the workgroup is available for receiving new requests. The workgroup will be

-     * available only when agents are available for this workgroup.

-     *

-     * @return true if the workgroup is available for receiving new requests.

-     */

-    public boolean isAvailable() {

-        Presence directedPresence = new Presence(Presence.Type.available);

-        directedPresence.setTo(workgroupJID);

-        PacketFilter typeFilter = new PacketTypeFilter(Presence.class);

-        PacketFilter fromFilter = new FromContainsFilter(workgroupJID);

-        PacketCollector collector = connection.createPacketCollector(new AndFilter(fromFilter,

-                typeFilter));

-

-        connection.sendPacket(directedPresence);

-

-        Presence response = (Presence)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            return false;

-        }

-        else if (response.getError() != null) {

-            return false;

-        }

-        else {

-            return Presence.Type.available == response.getType();

-        }

-    }

-

-    /**

-     * Returns the users current position in the workgroup queue. A value of 0 means

-     * the user is next in line to be routed; therefore, if the queue position

-     * is being displayed to the end user it is usually a good idea to add 1 to

-     * the value this method returns before display. If the user is not currently

-     * waiting in the workgroup, or no queue position information is available, -1

-     * will be returned.

-     *

-     * @return the user's current position in the workgroup queue, or -1 if the

-     *         position isn't available or if the user isn't in the queue.

-     */

-    public int getQueuePosition() {

-        return queuePosition;

-    }

-

-    /**

-     * Returns the estimated time (in seconds) that the user has to left wait in

-     * the workgroup queue before being routed. If the user is not currently waiting

-     * int he workgroup, or no queue time information is available, -1 will be

-     * returned.

-     *

-     * @return the estimated time remaining (in seconds) that the user has to

-     *         wait inthe workgroupu queue, or -1 if time information isn't available

-     *         or if the user isn't int the queue.

-     */

-    public int getQueueRemainingTime() {

-        return queueRemainingTime;

-    }

-

-    /**

-     * Joins the workgroup queue to wait to be routed to an agent. After joining

-     * the queue, queue status events will be sent to indicate the user's position and

-     * estimated time left in the queue. Once joining the queue, there are three ways

-     * the user can leave the queue: <ul>

-     * <p/>

-     * <li>The user is routed to an agent, which triggers a GroupChat invitation.

-     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.

-     * <li>A server error occurs, or an administrator explicitly removes the user

-     * from the queue.

-     * </ul>

-     * <p/>

-     * A user cannot request to join the queue again if already in the queue. Therefore,

-     * this method will throw an IllegalStateException if the user is already in the queue.<p>

-     * <p/>

-     * Some servers may be configured to require certain meta-data in order to

-     * join the queue. In that case, the {@link #joinQueue(Form)} method should be

-     * used instead of this method so that meta-data may be passed in.<p>

-     * <p/>

-     * The server tracks the conversations that a user has with agents over time. By

-     * default, that tracking is done using the user's JID. However, this is not always

-     * possible. For example, when the user is logged in anonymously using a web client.

-     * In that case the user ID might be a randomly generated value put into a persistent

-     * cookie or a username obtained via the session. A userID can be explicitly

-     * passed in by using the {@link #joinQueue(Form, String)} method. When specified,

-     * that userID will be used instead of the user's JID to track conversations. The

-     * server will ignore a manually specified userID if the user's connection to the server

-     * is not anonymous.

-     *

-     * @throws XMPPException if an error occured joining the queue. An error may indicate

-     *                       that a connection failure occured or that the server explicitly rejected the

-     *                       request to join the queue.

-     */

-    public void joinQueue() throws XMPPException {

-        joinQueue(null);

-    }

-

-    /**

-     * Joins the workgroup queue to wait to be routed to an agent. After joining

-     * the queue, queue status events will be sent to indicate the user's position and

-     * estimated time left in the queue. Once joining the queue, there are three ways

-     * the user can leave the queue: <ul>

-     * <p/>

-     * <li>The user is routed to an agent, which triggers a GroupChat invitation.

-     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.

-     * <li>A server error occurs, or an administrator explicitly removes the user

-     * from the queue.

-     * </ul>

-     * <p/>

-     * A user cannot request to join the queue again if already in the queue. Therefore,

-     * this method will throw an IllegalStateException if the user is already in the queue.<p>

-     * <p/>

-     * Some servers may be configured to require certain meta-data in order to

-     * join the queue.<p>

-     * <p/>

-     * The server tracks the conversations that a user has with agents over time. By

-     * default, that tracking is done using the user's JID. However, this is not always

-     * possible. For example, when the user is logged in anonymously using a web client.

-     * In that case the user ID might be a randomly generated value put into a persistent

-     * cookie or a username obtained via the session. A userID can be explicitly

-     * passed in by using the {@link #joinQueue(Form, String)} method. When specified,

-     * that userID will be used instead of the user's JID to track conversations. The

-     * server will ignore a manually specified userID if the user's connection to the server

-     * is not anonymous.

-     *

-     * @param answerForm the completed form the send for the join request.

-     * @throws XMPPException if an error occured joining the queue. An error may indicate

-     *                       that a connection failure occured or that the server explicitly rejected the

-     *                       request to join the queue.

-     */

-    public void joinQueue(Form answerForm) throws XMPPException {

-        joinQueue(answerForm, null);

-    }

-

-    /**

-     * <p>Joins the workgroup queue to wait to be routed to an agent. After joining

-     * the queue, queue status events will be sent to indicate the user's position and

-     * estimated time left in the queue. Once joining the queue, there are three ways

-     * the user can leave the queue: <ul>

-     * <p/>

-     * <li>The user is routed to an agent, which triggers a GroupChat invitation.

-     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.

-     * <li>A server error occurs, or an administrator explicitly removes the user

-     * from the queue.

-     * </ul>

-     * <p/>

-     * A user cannot request to join the queue again if already in the queue. Therefore,

-     * this method will throw an IllegalStateException if the user is already in the queue.<p>

-     * <p/>

-     * Some servers may be configured to require certain meta-data in order to

-     * join the queue.<p>

-     * <p/>

-     * The server tracks the conversations that a user has with agents over time. By

-     * default, that tracking is done using the user's JID. However, this is not always

-     * possible. For example, when the user is logged in anonymously using a web client.

-     * In that case the user ID might be a randomly generated value put into a persistent

-     * cookie or a username obtained via the session. When specified, that userID will

-     * be used instead of the user's JID to track conversations. The server will ignore a

-     * manually specified userID if the user's connection to the server is not anonymous.

-     *

-     * @param answerForm the completed form associated with the join reqest.

-     * @param userID     String that represents the ID of the user when using anonymous sessions

-     *                   or <tt>null</tt> if a userID should not be used.

-     * @throws XMPPException if an error occured joining the queue. An error may indicate

-     *                       that a connection failure occured or that the server explicitly rejected the

-     *                       request to join the queue.

-     */

-    public void joinQueue(Form answerForm, String userID) throws XMPPException {

-        // If already in the queue ignore the join request.

-        if (inQueue) {

-            throw new IllegalStateException("Already in queue " + workgroupJID);

-        }

-

-        JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupJID, answerForm, userID);

-

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(joinPacket.getPacketID()));

-

-        this.connection.sendPacket(joinPacket);

-

-        IQ response = (IQ)collector.nextResult(10000);

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from the server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-

-        // Notify listeners that we've joined the queue.

-        fireQueueJoinedEvent();

-    }

-

-    /**

-     * <p>Joins the workgroup queue to wait to be routed to an agent. After joining

-     * the queue, queue status events will be sent to indicate the user's position and

-     * estimated time left in the queue. Once joining the queue, there are three ways

-     * the user can leave the queue: <ul>

-     * <p/>

-     * <li>The user is routed to an agent, which triggers a GroupChat invitation.

-     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.

-     * <li>A server error occurs, or an administrator explicitly removes the user

-     * from the queue.

-     * </ul>

-     * <p/>

-     * A user cannot request to join the queue again if already in the queue. Therefore,

-     * this method will throw an IllegalStateException if the user is already in the queue.<p>

-     * <p/>

-     * Some servers may be configured to require certain meta-data in order to

-     * join the queue.<p>

-     * <p/>

-     * The server tracks the conversations that a user has with agents over time. By

-     * default, that tracking is done using the user's JID. However, this is not always

-     * possible. For example, when the user is logged in anonymously using a web client.

-     * In that case the user ID might be a randomly generated value put into a persistent

-     * cookie or a username obtained via the session. When specified, that userID will

-     * be used instead of the user's JID to track conversations. The server will ignore a

-     * manually specified userID if the user's connection to the server is not anonymous.

-     *

-     * @param metadata metadata to create a dataform from.

-     * @param userID   String that represents the ID of the user when using anonymous sessions

-     *                 or <tt>null</tt> if a userID should not be used.

-     * @throws XMPPException if an error occured joining the queue. An error may indicate

-     *                       that a connection failure occured or that the server explicitly rejected the

-     *                       request to join the queue.

-     */

-    public void joinQueue(Map<String,Object> metadata, String userID) throws XMPPException {

-        // If already in the queue ignore the join request.

-        if (inQueue) {

-            throw new IllegalStateException("Already in queue " + workgroupJID);

-        }

-

-        // Build dataform from metadata

-        Form form = new Form(Form.TYPE_SUBMIT);

-        Iterator<String> iter = metadata.keySet().iterator();

-        while (iter.hasNext()) {

-            String name = iter.next();

-            String value = metadata.get(name).toString();

-

-            String escapedName = StringUtils.escapeForXML(name);

-            String escapedValue = StringUtils.escapeForXML(value);

-

-            FormField field = new FormField(escapedName);

-            field.setType(FormField.TYPE_TEXT_SINGLE);

-            form.addField(field);

-            form.setAnswer(escapedName, escapedValue);

-        }

-        joinQueue(form, userID);

-    }

-

-    /**

-     * Departs the workgroup queue. If the user is not currently in the queue, this

-     * method will do nothing.<p>

-     * <p/>

-     * Normally, the user would not manually leave the queue. However, they may wish to

-     * under certain circumstances -- for example, if they no longer wish to be routed

-     * to an agent because they've been waiting too long.

-     *

-     * @throws XMPPException if an error occured trying to send the depart queue

-     *                       request to the server.

-     */

-    public void departQueue() throws XMPPException {

-        // If not in the queue ignore the depart request.

-        if (!inQueue) {

-            return;

-        }

-

-        DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID);

-        PacketCollector collector = this.connection.createPacketCollector(new PacketIDFilter(departPacket.getPacketID()));

-

-        connection.sendPacket(departPacket);

-

-        IQ response = (IQ)collector.nextResult(5000);

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from the server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-

-        // Notify listeners that we're no longer in the queue.

-        fireQueueDepartedEvent();

-    }

-

-    /**

-     * Adds a queue listener that will be notified of queue events for the user

-     * that created this Workgroup instance.

-     *

-     * @param queueListener the queue listener.

-     */

-    public void addQueueListener(QueueListener queueListener) {

-        synchronized (queueListeners) {

-            if (!queueListeners.contains(queueListener)) {

-                queueListeners.add(queueListener);

-            }

-        }

-    }

-

-    /**

-     * Removes a queue listener.

-     *

-     * @param queueListener the queue listener.

-     */

-    public void removeQueueListener(QueueListener queueListener) {

-        synchronized (queueListeners) {

-            queueListeners.remove(queueListener);

-        }

-    }

-

-    /**

-     * Adds an invitation listener that will be notified of groupchat invitations

-     * from the workgroup for the the user that created this Workgroup instance.

-     *

-     * @param invitationListener the invitation listener.

-     */

-    public void addInvitationListener(WorkgroupInvitationListener invitationListener) {

-        synchronized (invitationListeners) {

-            if (!invitationListeners.contains(invitationListener)) {

-                invitationListeners.add(invitationListener);

-            }

-        }

-    }

-

-    /**

-     * Removes an invitation listener.

-     *

-     * @param invitationListener the invitation listener.

-     */

-    public void removeQueueListener(WorkgroupInvitationListener invitationListener) {

-        synchronized (invitationListeners) {

-            invitationListeners.remove(invitationListener);

-        }

-    }

-

-    private void fireInvitationEvent(WorkgroupInvitation invitation) {

-        synchronized (invitationListeners) {

-            for (Iterator<WorkgroupInvitationListener> i = invitationListeners.iterator(); i.hasNext();) {

-                WorkgroupInvitationListener listener = i.next();

-                listener.invitationReceived(invitation);

-            }

-        }

-    }

-

-    private void fireQueueJoinedEvent() {

-        synchronized (queueListeners) {

-            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {

-                QueueListener listener = i.next();

-                listener.joinedQueue();

-            }

-        }

-    }

-

-    private void fireQueueDepartedEvent() {

-        synchronized (queueListeners) {

-            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {

-                QueueListener listener = i.next();

-                listener.departedQueue();

-            }

-        }

-    }

-

-    private void fireQueuePositionEvent(int currentPosition) {

-        synchronized (queueListeners) {

-            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {

-                QueueListener listener = i.next();

-                listener.queuePositionUpdated(currentPosition);

-            }

-        }

-    }

-

-    private void fireQueueTimeEvent(int secondsRemaining) {

-        synchronized (queueListeners) {

-            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {

-                QueueListener listener = i.next();

-                listener.queueWaitTimeUpdated(secondsRemaining);

-            }

-        }

-    }

-

-    // PacketListener Implementation.

-

-    private void handlePacket(Packet packet) {

-        if (packet instanceof Message) {

-            Message msg = (Message)packet;

-            // Check to see if the user left the queue.

-            PacketExtension pe = msg.getExtension("depart-queue", "http://jabber.org/protocol/workgroup");

-            PacketExtension queueStatus = msg.getExtension("queue-status", "http://jabber.org/protocol/workgroup");

-

-            if (pe != null) {

-                fireQueueDepartedEvent();

-            }

-            else if (queueStatus != null) {

-                QueueUpdate queueUpdate = (QueueUpdate)queueStatus;

-                if (queueUpdate.getPosition() != -1) {

-                    fireQueuePositionEvent(queueUpdate.getPosition());

-                }

-                if (queueUpdate.getRemaingTime() != -1) {

-                    fireQueueTimeEvent(queueUpdate.getRemaingTime());

-                }

-            }

-

-            else {

-                // Check if a room invitation was sent and if the sender is the workgroup

-                MUCUser mucUser = (MUCUser)msg.getExtension("x", "http://jabber.org/protocol/muc#user");

-                MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null;

-                if (invite != null && workgroupJID.equals(invite.getFrom())) {

-                    String sessionID = null;

-                    Map<String, List<String>> metaData = null;

-

-                    pe = msg.getExtension(SessionID.ELEMENT_NAME,

-                            SessionID.NAMESPACE);

-                    if (pe != null) {

-                        sessionID = ((SessionID)pe).getSessionID();

-                    }

-

-                    pe = msg.getExtension(MetaData.ELEMENT_NAME,

-                            MetaData.NAMESPACE);

-                    if (pe != null) {

-                        metaData = ((MetaData)pe).getMetaData();

-                    }

-

-                    WorkgroupInvitation inv = new WorkgroupInvitation(connection.getUser(), msg.getFrom(),

-                            workgroupJID, sessionID, msg.getBody(),

-                            msg.getFrom(), metaData);

-

-                    fireInvitationEvent(inv);

-                }

-            }

-        }

-    }

-

-    /**

-     * IQ packet to request joining the workgroup queue.

-     */

-    private class JoinQueuePacket extends IQ {

-

-        private String userID = null;

-        private DataForm form;

-

-        public JoinQueuePacket(String workgroup, Form answerForm, String userID) {

-            this.userID = userID;

-

-            setTo(workgroup);

-            setType(IQ.Type.SET);

-

-            form = answerForm.getDataFormToSend();

-            addExtension(form);

-        }

-

-        public String getChildElementXML() {

-            StringBuilder buf = new StringBuilder();

-

-            buf.append("<join-queue xmlns=\"http://jabber.org/protocol/workgroup\">");

-            buf.append("<queue-notifications/>");

-            // Add the user unique identification if the session is anonymous

-            if (connection.isAnonymous()) {

-                buf.append(new UserID(userID).toXML());

-            }

-

-            // Append data form text

-            buf.append(form.toXML());

-

-            buf.append("</join-queue>");

-

-            return buf.toString();

-        }

-    }

-

-    /**

-     * Returns a single chat setting based on it's identified key.

-     *

-     * @param key the key to find.

-     * @return the ChatSetting if found, otherwise false.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public ChatSetting getChatSetting(String key) throws XMPPException {

-        ChatSettings chatSettings = getChatSettings(key, -1);

-        return chatSettings.getFirstEntry();

-    }

-

-    /**

-     * Returns ChatSettings based on type.

-     *

-     * @param type the type of ChatSettings to return.

-     * @return the ChatSettings of given type, otherwise null.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public ChatSettings getChatSettings(int type) throws XMPPException {

-        return getChatSettings(null, type);

-    }

-

-    /**

-     * Returns all ChatSettings.

-     *

-     * @return all ChatSettings of a given workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public ChatSettings getChatSettings() throws XMPPException {

-        return getChatSettings(null, -1);

-    }

-

-

-    /**

-     * Asks the workgroup for it's Chat Settings.

-     *

-     * @return key specify a key to retrieve only that settings. Otherwise for all settings, key should be null.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    private ChatSettings getChatSettings(String key, int type) throws XMPPException {

-        ChatSettings request = new ChatSettings();

-        if (key != null) {

-            request.setKey(key);

-        }

-        if (type != -1) {

-            request.setType(type);

-        }

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        ChatSettings response = (ChatSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * The workgroup service may be configured to send email. This queries the Workgroup Service

-     * to see if the email service has been configured and is available.

-     *

-     * @return true if the email service is available, otherwise return false.

-     */

-    public boolean isEmailAvailable() {

-        ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);

-

-        try {

-            String workgroupService = StringUtils.parseServer(workgroupJID);

-            DiscoverInfo infoResult = discoManager.discoverInfo(workgroupService);

-            return infoResult.containsFeature("jive:email:provider");

-        }

-        catch (XMPPException e) {

-            return false;

-        }

-    }

-

-    /**

-     * Asks the workgroup for it's Offline Settings.

-     *

-     * @return offlineSettings the offline settings for this workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public OfflineSettings getOfflineSettings() throws XMPPException {

-        OfflineSettings request = new OfflineSettings();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        OfflineSettings response = (OfflineSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Asks the workgroup for it's Sound Settings.

-     *

-     * @return soundSettings the sound settings for the specified workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public SoundSettings getSoundSettings() throws XMPPException {

-        SoundSettings request = new SoundSettings();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        SoundSettings response = (SoundSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Asks the workgroup for it's Properties

-     *

-     * @return the WorkgroupProperties for the specified workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public WorkgroupProperties getWorkgroupProperties() throws XMPPException {

-        WorkgroupProperties request = new WorkgroupProperties();

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        WorkgroupProperties response = (WorkgroupProperties)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-    /**

-     * Asks the workgroup for it's Properties

-     *

-     * @param jid the jid of the user who's information you would like the workgroup to retreive.

-     * @return the WorkgroupProperties for the specified workgroup.

-     * @throws XMPPException if an error occurs while getting information from the server.

-     */

-    public WorkgroupProperties getWorkgroupProperties(String jid) throws XMPPException {

-        WorkgroupProperties request = new WorkgroupProperties();

-        request.setJid(jid);

-        request.setType(IQ.Type.GET);

-        request.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));

-        connection.sendPacket(request);

-

-

-        WorkgroupProperties response = (WorkgroupProperties)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return response;

-    }

-

-

-    /**

-     * Returns the Form to use for all clients of a workgroup. It is unlikely that the server

-     * will change the form (without a restart) so it is safe to keep the returned form

-     * for future submissions.

-     *

-     * @return the Form to use for searching transcripts.

-     * @throws XMPPException if an error occurs while sending the request to the server.

-     */

-    public Form getWorkgroupForm() throws XMPPException {

-        WorkgroupForm workgroupForm = new WorkgroupForm();

-        workgroupForm.setType(IQ.Type.GET);

-        workgroupForm.setTo(workgroupJID);

-

-        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(workgroupForm.getPacketID()));

-        connection.sendPacket(workgroupForm);

-

-        WorkgroupForm response = (WorkgroupForm)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());

-

-        // Cancel the collector.

-        collector.cancel();

-        if (response == null) {

-            throw new XMPPException("No response from server on status set.");

-        }

-        if (response.getError() != null) {

-            throw new XMPPException(response.getError());

-        }

-        return Form.getFormFrom(response);

-    }

-

-    /*

-    public static void main(String args[]) throws Exception {

-        Connection con = new XMPPConnection("anteros");

-        con.connect();

-        con.loginAnonymously();

-

-        Workgroup workgroup = new Workgroup("demo@workgroup.anteros", con);

-        WorkgroupProperties props = workgroup.getWorkgroupProperties("derek@anteros.com");

-

-        System.out.print(props);

-        con.disconnect();

-    }

-    */

-

-

+/**
+ * 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.smackx.workgroup.user;
+
+import org.jivesoftware.smackx.workgroup.MetaData;
+import org.jivesoftware.smackx.workgroup.WorkgroupInvitation;
+import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener;
+import org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm;
+import org.jivesoftware.smackx.workgroup.packet.DepartQueuePacket;
+import org.jivesoftware.smackx.workgroup.packet.QueueUpdate;
+import org.jivesoftware.smackx.workgroup.packet.SessionID;
+import org.jivesoftware.smackx.workgroup.packet.UserID;
+import org.jivesoftware.smackx.workgroup.settings.*;
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.Form;
+import org.jivesoftware.smackx.FormField;
+import org.jivesoftware.smackx.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.muc.MultiUserChat;
+import org.jivesoftware.smackx.packet.DataForm;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.MUCUser;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides workgroup services for users. Users can join the workgroup queue, depart the
+ * queue, find status information about their placement in the queue, and register to
+ * be notified when they are routed to an agent.<p>
+ * <p/>
+ * This class only provides a users perspective into a workgroup and is not intended
+ * for use by agents.
+ *
+ * @author Matt Tucker
+ * @author Derek DeMoro
+ */
+public class Workgroup {
+
+    private String workgroupJID;
+    private Connection connection;
+    private boolean inQueue;
+    private List<WorkgroupInvitationListener> invitationListeners;
+    private List<QueueListener> queueListeners;
+
+    private int queuePosition = -1;
+    private int queueRemainingTime = -1;
+
+    /**
+     * Creates a new workgroup instance using the specified workgroup JID
+     * (eg support@workgroup.example.com) and XMPP connection. The connection must have
+     * undergone a successful login before being used to construct an instance of
+     * this class.
+     *
+     * @param workgroupJID the JID of the workgroup.
+     * @param connection   an XMPP connection which must have already undergone a
+     *                     successful login.
+     */
+    public Workgroup(String workgroupJID, Connection connection) {
+        // Login must have been done before passing in connection.
+        if (!connection.isAuthenticated()) {
+            throw new IllegalStateException("Must login to server before creating workgroup.");
+        }
+
+        this.workgroupJID = workgroupJID;
+        this.connection = connection;
+        inQueue = false;
+        invitationListeners = new ArrayList<WorkgroupInvitationListener>();
+        queueListeners = new ArrayList<QueueListener>();
+
+        // Register as a queue listener for internal usage by this instance.
+        addQueueListener(new QueueListener() {
+            public void joinedQueue() {
+                inQueue = true;
+            }
+
+            public void departedQueue() {
+                inQueue = false;
+                queuePosition = -1;
+                queueRemainingTime = -1;
+            }
+
+            public void queuePositionUpdated(int currentPosition) {
+                queuePosition = currentPosition;
+            }
+
+            public void queueWaitTimeUpdated(int secondsRemaining) {
+                queueRemainingTime = secondsRemaining;
+            }
+        });
+
+        /**
+         * Internal handling of an invitation.Recieving an invitation removes the user from the queue.
+         */
+        MultiUserChat.addInvitationListener(connection,
+                new org.jivesoftware.smackx.muc.InvitationListener() {
+                    public void invitationReceived(Connection conn, String room, String inviter,
+                                                   String reason, String password, Message message) {
+                        inQueue = false;
+                        queuePosition = -1;
+                        queueRemainingTime = -1;
+                    }
+                });
+
+        // Register a packet listener for all the messages sent to this client.
+        PacketFilter typeFilter = new PacketTypeFilter(Message.class);
+
+        connection.addPacketListener(new PacketListener() {
+            public void processPacket(Packet packet) {
+                handlePacket(packet);
+            }
+        }, typeFilter);
+    }
+
+    /**
+     * Returns the name of this workgroup (eg support@example.com).
+     *
+     * @return the name of the workgroup.
+     */
+    public String getWorkgroupJID() {
+        return workgroupJID;
+    }
+
+    /**
+     * Returns true if the user is currently waiting in the workgroup queue.
+     *
+     * @return true if currently waiting in the queue.
+     */
+    public boolean isInQueue() {
+        return inQueue;
+    }
+
+    /**
+     * Returns true if the workgroup is available for receiving new requests. The workgroup will be
+     * available only when agents are available for this workgroup.
+     *
+     * @return true if the workgroup is available for receiving new requests.
+     */
+    public boolean isAvailable() {
+        Presence directedPresence = new Presence(Presence.Type.available);
+        directedPresence.setTo(workgroupJID);
+        PacketFilter typeFilter = new PacketTypeFilter(Presence.class);
+        PacketFilter fromFilter = new FromContainsFilter(workgroupJID);
+        PacketCollector collector = connection.createPacketCollector(new AndFilter(fromFilter,
+                typeFilter));
+
+        connection.sendPacket(directedPresence);
+
+        Presence response = (Presence)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            return false;
+        }
+        else if (response.getError() != null) {
+            return false;
+        }
+        else {
+            return Presence.Type.available == response.getType();
+        }
+    }
+
+    /**
+     * Returns the users current position in the workgroup queue. A value of 0 means
+     * the user is next in line to be routed; therefore, if the queue position
+     * is being displayed to the end user it is usually a good idea to add 1 to
+     * the value this method returns before display. If the user is not currently
+     * waiting in the workgroup, or no queue position information is available, -1
+     * will be returned.
+     *
+     * @return the user's current position in the workgroup queue, or -1 if the
+     *         position isn't available or if the user isn't in the queue.
+     */
+    public int getQueuePosition() {
+        return queuePosition;
+    }
+
+    /**
+     * Returns the estimated time (in seconds) that the user has to left wait in
+     * the workgroup queue before being routed. If the user is not currently waiting
+     * int he workgroup, or no queue time information is available, -1 will be
+     * returned.
+     *
+     * @return the estimated time remaining (in seconds) that the user has to
+     *         wait inthe workgroupu queue, or -1 if time information isn't available
+     *         or if the user isn't int the queue.
+     */
+    public int getQueueRemainingTime() {
+        return queueRemainingTime;
+    }
+
+    /**
+     * Joins the workgroup queue to wait to be routed to an agent. After joining
+     * the queue, queue status events will be sent to indicate the user's position and
+     * estimated time left in the queue. Once joining the queue, there are three ways
+     * the user can leave the queue: <ul>
+     * <p/>
+     * <li>The user is routed to an agent, which triggers a GroupChat invitation.
+     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.
+     * <li>A server error occurs, or an administrator explicitly removes the user
+     * from the queue.
+     * </ul>
+     * <p/>
+     * A user cannot request to join the queue again if already in the queue. Therefore,
+     * this method will throw an IllegalStateException if the user is already in the queue.<p>
+     * <p/>
+     * Some servers may be configured to require certain meta-data in order to
+     * join the queue. In that case, the {@link #joinQueue(Form)} method should be
+     * used instead of this method so that meta-data may be passed in.<p>
+     * <p/>
+     * The server tracks the conversations that a user has with agents over time. By
+     * default, that tracking is done using the user's JID. However, this is not always
+     * possible. For example, when the user is logged in anonymously using a web client.
+     * In that case the user ID might be a randomly generated value put into a persistent
+     * cookie or a username obtained via the session. A userID can be explicitly
+     * passed in by using the {@link #joinQueue(Form, String)} method. When specified,
+     * that userID will be used instead of the user's JID to track conversations. The
+     * server will ignore a manually specified userID if the user's connection to the server
+     * is not anonymous.
+     *
+     * @throws XMPPException if an error occured joining the queue. An error may indicate
+     *                       that a connection failure occured or that the server explicitly rejected the
+     *                       request to join the queue.
+     */
+    public void joinQueue() throws XMPPException {
+        joinQueue(null);
+    }
+
+    /**
+     * Joins the workgroup queue to wait to be routed to an agent. After joining
+     * the queue, queue status events will be sent to indicate the user's position and
+     * estimated time left in the queue. Once joining the queue, there are three ways
+     * the user can leave the queue: <ul>
+     * <p/>
+     * <li>The user is routed to an agent, which triggers a GroupChat invitation.
+     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.
+     * <li>A server error occurs, or an administrator explicitly removes the user
+     * from the queue.
+     * </ul>
+     * <p/>
+     * A user cannot request to join the queue again if already in the queue. Therefore,
+     * this method will throw an IllegalStateException if the user is already in the queue.<p>
+     * <p/>
+     * Some servers may be configured to require certain meta-data in order to
+     * join the queue.<p>
+     * <p/>
+     * The server tracks the conversations that a user has with agents over time. By
+     * default, that tracking is done using the user's JID. However, this is not always
+     * possible. For example, when the user is logged in anonymously using a web client.
+     * In that case the user ID might be a randomly generated value put into a persistent
+     * cookie or a username obtained via the session. A userID can be explicitly
+     * passed in by using the {@link #joinQueue(Form, String)} method. When specified,
+     * that userID will be used instead of the user's JID to track conversations. The
+     * server will ignore a manually specified userID if the user's connection to the server
+     * is not anonymous.
+     *
+     * @param answerForm the completed form the send for the join request.
+     * @throws XMPPException if an error occured joining the queue. An error may indicate
+     *                       that a connection failure occured or that the server explicitly rejected the
+     *                       request to join the queue.
+     */
+    public void joinQueue(Form answerForm) throws XMPPException {
+        joinQueue(answerForm, null);
+    }
+
+    /**
+     * <p>Joins the workgroup queue to wait to be routed to an agent. After joining
+     * the queue, queue status events will be sent to indicate the user's position and
+     * estimated time left in the queue. Once joining the queue, there are three ways
+     * the user can leave the queue: <ul>
+     * <p/>
+     * <li>The user is routed to an agent, which triggers a GroupChat invitation.
+     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.
+     * <li>A server error occurs, or an administrator explicitly removes the user
+     * from the queue.
+     * </ul>
+     * <p/>
+     * A user cannot request to join the queue again if already in the queue. Therefore,
+     * this method will throw an IllegalStateException if the user is already in the queue.<p>
+     * <p/>
+     * Some servers may be configured to require certain meta-data in order to
+     * join the queue.<p>
+     * <p/>
+     * The server tracks the conversations that a user has with agents over time. By
+     * default, that tracking is done using the user's JID. However, this is not always
+     * possible. For example, when the user is logged in anonymously using a web client.
+     * In that case the user ID might be a randomly generated value put into a persistent
+     * cookie or a username obtained via the session. When specified, that userID will
+     * be used instead of the user's JID to track conversations. The server will ignore a
+     * manually specified userID if the user's connection to the server is not anonymous.
+     *
+     * @param answerForm the completed form associated with the join reqest.
+     * @param userID     String that represents the ID of the user when using anonymous sessions
+     *                   or <tt>null</tt> if a userID should not be used.
+     * @throws XMPPException if an error occured joining the queue. An error may indicate
+     *                       that a connection failure occured or that the server explicitly rejected the
+     *                       request to join the queue.
+     */
+    public void joinQueue(Form answerForm, String userID) throws XMPPException {
+        // If already in the queue ignore the join request.
+        if (inQueue) {
+            throw new IllegalStateException("Already in queue " + workgroupJID);
+        }
+
+        JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupJID, answerForm, userID);
+
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(joinPacket.getPacketID()));
+
+        this.connection.sendPacket(joinPacket);
+
+        IQ response = (IQ)collector.nextResult(10000);
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from the server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+
+        // Notify listeners that we've joined the queue.
+        fireQueueJoinedEvent();
+    }
+
+    /**
+     * <p>Joins the workgroup queue to wait to be routed to an agent. After joining
+     * the queue, queue status events will be sent to indicate the user's position and
+     * estimated time left in the queue. Once joining the queue, there are three ways
+     * the user can leave the queue: <ul>
+     * <p/>
+     * <li>The user is routed to an agent, which triggers a GroupChat invitation.
+     * <li>The user asks to leave the queue by calling the {@link #departQueue} method.
+     * <li>A server error occurs, or an administrator explicitly removes the user
+     * from the queue.
+     * </ul>
+     * <p/>
+     * A user cannot request to join the queue again if already in the queue. Therefore,
+     * this method will throw an IllegalStateException if the user is already in the queue.<p>
+     * <p/>
+     * Some servers may be configured to require certain meta-data in order to
+     * join the queue.<p>
+     * <p/>
+     * The server tracks the conversations that a user has with agents over time. By
+     * default, that tracking is done using the user's JID. However, this is not always
+     * possible. For example, when the user is logged in anonymously using a web client.
+     * In that case the user ID might be a randomly generated value put into a persistent
+     * cookie or a username obtained via the session. When specified, that userID will
+     * be used instead of the user's JID to track conversations. The server will ignore a
+     * manually specified userID if the user's connection to the server is not anonymous.
+     *
+     * @param metadata metadata to create a dataform from.
+     * @param userID   String that represents the ID of the user when using anonymous sessions
+     *                 or <tt>null</tt> if a userID should not be used.
+     * @throws XMPPException if an error occured joining the queue. An error may indicate
+     *                       that a connection failure occured or that the server explicitly rejected the
+     *                       request to join the queue.
+     */
+    public void joinQueue(Map<String,Object> metadata, String userID) throws XMPPException {
+        // If already in the queue ignore the join request.
+        if (inQueue) {
+            throw new IllegalStateException("Already in queue " + workgroupJID);
+        }
+
+        // Build dataform from metadata
+        Form form = new Form(Form.TYPE_SUBMIT);
+        Iterator<String> iter = metadata.keySet().iterator();
+        while (iter.hasNext()) {
+            String name = iter.next();
+            String value = metadata.get(name).toString();
+
+            String escapedName = StringUtils.escapeForXML(name);
+            String escapedValue = StringUtils.escapeForXML(value);
+
+            FormField field = new FormField(escapedName);
+            field.setType(FormField.TYPE_TEXT_SINGLE);
+            form.addField(field);
+            form.setAnswer(escapedName, escapedValue);
+        }
+        joinQueue(form, userID);
+    }
+
+    /**
+     * Departs the workgroup queue. If the user is not currently in the queue, this
+     * method will do nothing.<p>
+     * <p/>
+     * Normally, the user would not manually leave the queue. However, they may wish to
+     * under certain circumstances -- for example, if they no longer wish to be routed
+     * to an agent because they've been waiting too long.
+     *
+     * @throws XMPPException if an error occured trying to send the depart queue
+     *                       request to the server.
+     */
+    public void departQueue() throws XMPPException {
+        // If not in the queue ignore the depart request.
+        if (!inQueue) {
+            return;
+        }
+
+        DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID);
+        PacketCollector collector = this.connection.createPacketCollector(new PacketIDFilter(departPacket.getPacketID()));
+
+        connection.sendPacket(departPacket);
+
+        IQ response = (IQ)collector.nextResult(5000);
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from the server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+
+        // Notify listeners that we're no longer in the queue.
+        fireQueueDepartedEvent();
+    }
+
+    /**
+     * Adds a queue listener that will be notified of queue events for the user
+     * that created this Workgroup instance.
+     *
+     * @param queueListener the queue listener.
+     */
+    public void addQueueListener(QueueListener queueListener) {
+        synchronized (queueListeners) {
+            if (!queueListeners.contains(queueListener)) {
+                queueListeners.add(queueListener);
+            }
+        }
+    }
+
+    /**
+     * Removes a queue listener.
+     *
+     * @param queueListener the queue listener.
+     */
+    public void removeQueueListener(QueueListener queueListener) {
+        synchronized (queueListeners) {
+            queueListeners.remove(queueListener);
+        }
+    }
+
+    /**
+     * Adds an invitation listener that will be notified of groupchat invitations
+     * from the workgroup for the the user that created this Workgroup instance.
+     *
+     * @param invitationListener the invitation listener.
+     */
+    public void addInvitationListener(WorkgroupInvitationListener invitationListener) {
+        synchronized (invitationListeners) {
+            if (!invitationListeners.contains(invitationListener)) {
+                invitationListeners.add(invitationListener);
+            }
+        }
+    }
+
+    /**
+     * Removes an invitation listener.
+     *
+     * @param invitationListener the invitation listener.
+     */
+    public void removeQueueListener(WorkgroupInvitationListener invitationListener) {
+        synchronized (invitationListeners) {
+            invitationListeners.remove(invitationListener);
+        }
+    }
+
+    private void fireInvitationEvent(WorkgroupInvitation invitation) {
+        synchronized (invitationListeners) {
+            for (Iterator<WorkgroupInvitationListener> i = invitationListeners.iterator(); i.hasNext();) {
+                WorkgroupInvitationListener listener = i.next();
+                listener.invitationReceived(invitation);
+            }
+        }
+    }
+
+    private void fireQueueJoinedEvent() {
+        synchronized (queueListeners) {
+            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {
+                QueueListener listener = i.next();
+                listener.joinedQueue();
+            }
+        }
+    }
+
+    private void fireQueueDepartedEvent() {
+        synchronized (queueListeners) {
+            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {
+                QueueListener listener = i.next();
+                listener.departedQueue();
+            }
+        }
+    }
+
+    private void fireQueuePositionEvent(int currentPosition) {
+        synchronized (queueListeners) {
+            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {
+                QueueListener listener = i.next();
+                listener.queuePositionUpdated(currentPosition);
+            }
+        }
+    }
+
+    private void fireQueueTimeEvent(int secondsRemaining) {
+        synchronized (queueListeners) {
+            for (Iterator<QueueListener> i = queueListeners.iterator(); i.hasNext();) {
+                QueueListener listener = i.next();
+                listener.queueWaitTimeUpdated(secondsRemaining);
+            }
+        }
+    }
+
+    // PacketListener Implementation.
+
+    private void handlePacket(Packet packet) {
+        if (packet instanceof Message) {
+            Message msg = (Message)packet;
+            // Check to see if the user left the queue.
+            PacketExtension pe = msg.getExtension("depart-queue", "http://jabber.org/protocol/workgroup");
+            PacketExtension queueStatus = msg.getExtension("queue-status", "http://jabber.org/protocol/workgroup");
+
+            if (pe != null) {
+                fireQueueDepartedEvent();
+            }
+            else if (queueStatus != null) {
+                QueueUpdate queueUpdate = (QueueUpdate)queueStatus;
+                if (queueUpdate.getPosition() != -1) {
+                    fireQueuePositionEvent(queueUpdate.getPosition());
+                }
+                if (queueUpdate.getRemaingTime() != -1) {
+                    fireQueueTimeEvent(queueUpdate.getRemaingTime());
+                }
+            }
+
+            else {
+                // Check if a room invitation was sent and if the sender is the workgroup
+                MUCUser mucUser = (MUCUser)msg.getExtension("x", "http://jabber.org/protocol/muc#user");
+                MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null;
+                if (invite != null && workgroupJID.equals(invite.getFrom())) {
+                    String sessionID = null;
+                    Map<String, List<String>> metaData = null;
+
+                    pe = msg.getExtension(SessionID.ELEMENT_NAME,
+                            SessionID.NAMESPACE);
+                    if (pe != null) {
+                        sessionID = ((SessionID)pe).getSessionID();
+                    }
+
+                    pe = msg.getExtension(MetaData.ELEMENT_NAME,
+                            MetaData.NAMESPACE);
+                    if (pe != null) {
+                        metaData = ((MetaData)pe).getMetaData();
+                    }
+
+                    WorkgroupInvitation inv = new WorkgroupInvitation(connection.getUser(), msg.getFrom(),
+                            workgroupJID, sessionID, msg.getBody(),
+                            msg.getFrom(), metaData);
+
+                    fireInvitationEvent(inv);
+                }
+            }
+        }
+    }
+
+    /**
+     * IQ packet to request joining the workgroup queue.
+     */
+    private class JoinQueuePacket extends IQ {
+
+        private String userID = null;
+        private DataForm form;
+
+        public JoinQueuePacket(String workgroup, Form answerForm, String userID) {
+            this.userID = userID;
+
+            setTo(workgroup);
+            setType(IQ.Type.SET);
+
+            form = answerForm.getDataFormToSend();
+            addExtension(form);
+        }
+
+        public String getChildElementXML() {
+            StringBuilder buf = new StringBuilder();
+
+            buf.append("<join-queue xmlns=\"http://jabber.org/protocol/workgroup\">");
+            buf.append("<queue-notifications/>");
+            // Add the user unique identification if the session is anonymous
+            if (connection.isAnonymous()) {
+                buf.append(new UserID(userID).toXML());
+            }
+
+            // Append data form text
+            buf.append(form.toXML());
+
+            buf.append("</join-queue>");
+
+            return buf.toString();
+        }
+    }
+
+    /**
+     * Returns a single chat setting based on it's identified key.
+     *
+     * @param key the key to find.
+     * @return the ChatSetting if found, otherwise false.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public ChatSetting getChatSetting(String key) throws XMPPException {
+        ChatSettings chatSettings = getChatSettings(key, -1);
+        return chatSettings.getFirstEntry();
+    }
+
+    /**
+     * Returns ChatSettings based on type.
+     *
+     * @param type the type of ChatSettings to return.
+     * @return the ChatSettings of given type, otherwise null.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public ChatSettings getChatSettings(int type) throws XMPPException {
+        return getChatSettings(null, type);
+    }
+
+    /**
+     * Returns all ChatSettings.
+     *
+     * @return all ChatSettings of a given workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public ChatSettings getChatSettings() throws XMPPException {
+        return getChatSettings(null, -1);
+    }
+
+
+    /**
+     * Asks the workgroup for it's Chat Settings.
+     *
+     * @return key specify a key to retrieve only that settings. Otherwise for all settings, key should be null.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    private ChatSettings getChatSettings(String key, int type) throws XMPPException {
+        ChatSettings request = new ChatSettings();
+        if (key != null) {
+            request.setKey(key);
+        }
+        if (type != -1) {
+            request.setType(type);
+        }
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        ChatSettings response = (ChatSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * The workgroup service may be configured to send email. This queries the Workgroup Service
+     * to see if the email service has been configured and is available.
+     *
+     * @return true if the email service is available, otherwise return false.
+     */
+    public boolean isEmailAvailable() {
+        ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
+
+        try {
+            String workgroupService = StringUtils.parseServer(workgroupJID);
+            DiscoverInfo infoResult = discoManager.discoverInfo(workgroupService);
+            return infoResult.containsFeature("jive:email:provider");
+        }
+        catch (XMPPException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Asks the workgroup for it's Offline Settings.
+     *
+     * @return offlineSettings the offline settings for this workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public OfflineSettings getOfflineSettings() throws XMPPException {
+        OfflineSettings request = new OfflineSettings();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        OfflineSettings response = (OfflineSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Asks the workgroup for it's Sound Settings.
+     *
+     * @return soundSettings the sound settings for the specified workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public SoundSettings getSoundSettings() throws XMPPException {
+        SoundSettings request = new SoundSettings();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        SoundSettings response = (SoundSettings)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Asks the workgroup for it's Properties
+     *
+     * @return the WorkgroupProperties for the specified workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public WorkgroupProperties getWorkgroupProperties() throws XMPPException {
+        WorkgroupProperties request = new WorkgroupProperties();
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        WorkgroupProperties response = (WorkgroupProperties)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+    /**
+     * Asks the workgroup for it's Properties
+     *
+     * @param jid the jid of the user who's information you would like the workgroup to retreive.
+     * @return the WorkgroupProperties for the specified workgroup.
+     * @throws XMPPException if an error occurs while getting information from the server.
+     */
+    public WorkgroupProperties getWorkgroupProperties(String jid) throws XMPPException {
+        WorkgroupProperties request = new WorkgroupProperties();
+        request.setJid(jid);
+        request.setType(IQ.Type.GET);
+        request.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
+        connection.sendPacket(request);
+
+
+        WorkgroupProperties response = (WorkgroupProperties)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return response;
+    }
+
+
+    /**
+     * Returns the Form to use for all clients of a workgroup. It is unlikely that the server
+     * will change the form (without a restart) so it is safe to keep the returned form
+     * for future submissions.
+     *
+     * @return the Form to use for searching transcripts.
+     * @throws XMPPException if an error occurs while sending the request to the server.
+     */
+    public Form getWorkgroupForm() throws XMPPException {
+        WorkgroupForm workgroupForm = new WorkgroupForm();
+        workgroupForm.setType(IQ.Type.GET);
+        workgroupForm.setTo(workgroupJID);
+
+        PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(workgroupForm.getPacketID()));
+        connection.sendPacket(workgroupForm);
+
+        WorkgroupForm response = (WorkgroupForm)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+        // Cancel the collector.
+        collector.cancel();
+        if (response == null) {
+            throw new XMPPException("No response from server on status set.");
+        }
+        if (response.getError() != null) {
+            throw new XMPPException(response.getError());
+        }
+        return Form.getFormFrom(response);
+    }
+
+    /*
+    public static void main(String args[]) throws Exception {
+        Connection con = new XMPPConnection("anteros");
+        con.connect();
+        con.loginAnonymously();
+
+        Workgroup workgroup = new Workgroup("demo@workgroup.anteros", con);
+        WorkgroupProperties props = workgroup.getWorkgroupProperties("derek@anteros.com");
+
+        System.out.print(props);
+        con.disconnect();
+    }
+    */
+
+
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java
index 533b9a1..2376701 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java
@@ -1,132 +1,132 @@
-/**

- * 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.smackx.workgroup.util;

-

-import java.lang.reflect.Method;

-import java.util.ArrayList;

-import java.util.ListIterator;

-

-/**

- * This class is a very flexible event dispatcher which implements Runnable so that it can

- * dispatch easily from a newly created thread. The usage of this in code is more or less:

- * create a new instance of this class, use addListenerTriplet to add as many listeners

- * as desired to be messaged, create a new Thread using the instance of this class created

- * as the argument to the constructor, start the new Thread instance.<p>

- *

- * Also, this is intended to be used to message methods that either return void, or have

- * a return which the developer using this class is uninterested in receiving.

- *

- * @author loki der quaeler

- */

-public class ListenerEventDispatcher

-    implements Runnable {

-

-    protected transient ArrayList<TripletContainer> triplets;

-

-    protected transient boolean hasFinishedDispatching;

-    protected transient boolean isRunning;

-

-    public ListenerEventDispatcher () {

-        super();

-

-        this.triplets = new ArrayList<TripletContainer>();

-

-        this.hasFinishedDispatching = false;

-        this.isRunning = false;

-    }

-

-    /**

-     * Add a listener triplet - the instance of the listener to be messaged, the Method on which

-     *  the listener should be messaged, and the Object array of arguments to be supplied to the

-     *  Method. No attempts are made to determine whether this triplet was already added.<br>

-     *

-     * Messages are dispatched in the order in which they're added via this method; so if triplet

-     *  X is added after triplet Z, then triplet Z will undergo messaging prior to triplet X.<br>

-     *

-     * This method should not be called once the owning Thread instance has been started; if it

-     *  is called, the triplet will not be added to the messaging queue.<br>

-     *

-     * @param listenerInstance the instance of the listener to receive the associated notification

-     * @param listenerMethod the Method instance representing the method through which notification

-     *                          will occur

-     * @param methodArguments the arguments supplied to the notification method

-     */

-    public void addListenerTriplet(Object listenerInstance, Method listenerMethod,

-            Object[] methodArguments)

-    {

-        if (!this.isRunning) {

-            this.triplets.add(new TripletContainer(listenerInstance, listenerMethod,

-                    methodArguments));

-        }

-    }

-

-    /**

-     * @return whether this instance has finished dispatching its messages

-     */

-    public boolean hasFinished() {

-        return this.hasFinishedDispatching;

-    }

-

-    public void run() {

-        ListIterator<TripletContainer> li = null;

-

-        this.isRunning = true;

-

-        li = this.triplets.listIterator();

-        while (li.hasNext()) {

-            TripletContainer tc = li.next();

-

-            try {

-                tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments());

-            } catch (Exception e) {

-                System.err.println("Exception dispatching an event: " + e);

-

-                e.printStackTrace();

-            }

-        }

-

-        this.hasFinishedDispatching = true;

-    }

-

-

-    protected class TripletContainer {

-

-        protected Object listenerInstance;

-        protected Method listenerMethod;

-        protected Object[] methodArguments;

-

-        protected TripletContainer (Object inst, Method meth, Object[] args) {

-            super();

-

-            this.listenerInstance = inst;

-            this.listenerMethod = meth;

-            this.methodArguments = args;

-        }

-

-        protected Object getListenerInstance() {

-            return this.listenerInstance;

-        }

-

-        protected Method getListenerMethod() {

-            return this.listenerMethod;

-        }

-

-        protected Object[] getMethodArguments() {

-            return this.methodArguments;

-        }

-    }

+/**
+ * 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.smackx.workgroup.util;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.ListIterator;
+
+/**
+ * This class is a very flexible event dispatcher which implements Runnable so that it can
+ * dispatch easily from a newly created thread. The usage of this in code is more or less:
+ * create a new instance of this class, use addListenerTriplet to add as many listeners
+ * as desired to be messaged, create a new Thread using the instance of this class created
+ * as the argument to the constructor, start the new Thread instance.<p>
+ *
+ * Also, this is intended to be used to message methods that either return void, or have
+ * a return which the developer using this class is uninterested in receiving.
+ *
+ * @author loki der quaeler
+ */
+public class ListenerEventDispatcher
+    implements Runnable {
+
+    protected transient ArrayList<TripletContainer> triplets;
+
+    protected transient boolean hasFinishedDispatching;
+    protected transient boolean isRunning;
+
+    public ListenerEventDispatcher () {
+        super();
+
+        this.triplets = new ArrayList<TripletContainer>();
+
+        this.hasFinishedDispatching = false;
+        this.isRunning = false;
+    }
+
+    /**
+     * Add a listener triplet - the instance of the listener to be messaged, the Method on which
+     *  the listener should be messaged, and the Object array of arguments to be supplied to the
+     *  Method. No attempts are made to determine whether this triplet was already added.<br>
+     *
+     * Messages are dispatched in the order in which they're added via this method; so if triplet
+     *  X is added after triplet Z, then triplet Z will undergo messaging prior to triplet X.<br>
+     *
+     * This method should not be called once the owning Thread instance has been started; if it
+     *  is called, the triplet will not be added to the messaging queue.<br>
+     *
+     * @param listenerInstance the instance of the listener to receive the associated notification
+     * @param listenerMethod the Method instance representing the method through which notification
+     *                          will occur
+     * @param methodArguments the arguments supplied to the notification method
+     */
+    public void addListenerTriplet(Object listenerInstance, Method listenerMethod,
+            Object[] methodArguments)
+    {
+        if (!this.isRunning) {
+            this.triplets.add(new TripletContainer(listenerInstance, listenerMethod,
+                    methodArguments));
+        }
+    }
+
+    /**
+     * @return whether this instance has finished dispatching its messages
+     */
+    public boolean hasFinished() {
+        return this.hasFinishedDispatching;
+    }
+
+    public void run() {
+        ListIterator<TripletContainer> li = null;
+
+        this.isRunning = true;
+
+        li = this.triplets.listIterator();
+        while (li.hasNext()) {
+            TripletContainer tc = li.next();
+
+            try {
+                tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments());
+            } catch (Exception e) {
+                System.err.println("Exception dispatching an event: " + e);
+
+                e.printStackTrace();
+            }
+        }
+
+        this.hasFinishedDispatching = true;
+    }
+
+
+    protected class TripletContainer {
+
+        protected Object listenerInstance;
+        protected Method listenerMethod;
+        protected Object[] methodArguments;
+
+        protected TripletContainer (Object inst, Method meth, Object[] args) {
+            super();
+
+            this.listenerInstance = inst;
+            this.listenerMethod = meth;
+            this.methodArguments = args;
+        }
+
+        protected Object getListenerInstance() {
+            return this.listenerInstance;
+        }
+
+        protected Method getListenerMethod() {
+            return this.listenerMethod;
+        }
+
+        protected Object[] getMethodArguments() {
+            return this.methodArguments;
+        }
+    }
 }
\ No newline at end of file
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java
index 5be1c1a..7f8df0d 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java
@@ -1,103 +1,103 @@
-/**

- * 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.smackx.workgroup.util;

-

-import org.jivesoftware.smackx.workgroup.MetaData;

-import org.jivesoftware.smack.util.StringUtils;

-import org.xmlpull.v1.XmlPullParser;

-import org.xmlpull.v1.XmlPullParserException;

-

-import java.io.IOException;

-import java.util.*;

-

-/**

- * Utility class for meta-data parsing and writing.

- *

- * @author Matt Tucker

- */

-public class MetaDataUtils {

-

-    /**

-     * Parses any available meta-data and returns it as a Map of String name/value pairs. The

-     * parser must be positioned at an opening meta-data tag, or the an empty map will be returned.

-     *

-     * @param parser the XML parser positioned at an opening meta-data tag.

-     * @return the meta-data.

-     * @throws XmlPullParserException if an error occurs while parsing the XML.

-     * @throws IOException            if an error occurs while parsing the XML.

-     */

-    public static Map<String, List<String>> parseMetaData(XmlPullParser parser) throws XmlPullParserException, IOException {

-        int eventType = parser.getEventType();

-

-        // If correctly positioned on an opening meta-data tag, parse meta-data.

-        if ((eventType == XmlPullParser.START_TAG)

-                && parser.getName().equals(MetaData.ELEMENT_NAME)

-                && parser.getNamespace().equals(MetaData.NAMESPACE)) {

-            Map<String, List<String>> metaData = new Hashtable<String, List<String>>();

-

-            eventType = parser.nextTag();

-

-            // Keep parsing until we've gotten to end of meta-data.

-            while ((eventType != XmlPullParser.END_TAG)

-                    || (!parser.getName().equals(MetaData.ELEMENT_NAME))) {

-                String name = parser.getAttributeValue(0);

-                String value = parser.nextText();

-

-                if (metaData.containsKey(name)) {

-                    List<String> values = metaData.get(name);

-                    values.add(value);

-                }

-                else {

-                    List<String> values = new ArrayList<String>();

-                    values.add(value);

-                    metaData.put(name, values);

-                }

-

-                eventType = parser.nextTag();

-            }

-

-            return metaData;

-        }

-

-        return Collections.emptyMap();

-    }

-

-    /**

-     * Serializes a Map of String name/value pairs into the meta-data XML format.

-     *

-     * @param metaData the Map of meta-data as Map&lt;String,List&lt;String>>

-     * @return the meta-data values in XML form.

-     */

-    public static String serializeMetaData(Map<String, List<String>> metaData) {

-        StringBuilder buf = new StringBuilder();

-        if (metaData != null && metaData.size() > 0) {

-            buf.append("<metadata xmlns=\"http://jivesoftware.com/protocol/workgroup\">");

-            for (Iterator<String> i = metaData.keySet().iterator(); i.hasNext();) {

-                String key = i.next();

-                List<String> value = metaData.get(key);

-                for (Iterator<String> it = value.iterator(); it.hasNext();) {

-                    String v = it.next();

-                    buf.append("<value name=\"").append(key).append("\">");

-                    buf.append(StringUtils.escapeForXML(v));

-                    buf.append("</value>");

-                }

-            }

-            buf.append("</metadata>");

-        }

-        return buf.toString();

-    }

-}

+/**
+ * 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.smackx.workgroup.util;
+
+import org.jivesoftware.smackx.workgroup.MetaData;
+import org.jivesoftware.smack.util.StringUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Utility class for meta-data parsing and writing.
+ *
+ * @author Matt Tucker
+ */
+public class MetaDataUtils {
+
+    /**
+     * Parses any available meta-data and returns it as a Map of String name/value pairs. The
+     * parser must be positioned at an opening meta-data tag, or the an empty map will be returned.
+     *
+     * @param parser the XML parser positioned at an opening meta-data tag.
+     * @return the meta-data.
+     * @throws XmlPullParserException if an error occurs while parsing the XML.
+     * @throws IOException            if an error occurs while parsing the XML.
+     */
+    public static Map<String, List<String>> parseMetaData(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int eventType = parser.getEventType();
+
+        // If correctly positioned on an opening meta-data tag, parse meta-data.
+        if ((eventType == XmlPullParser.START_TAG)
+                && parser.getName().equals(MetaData.ELEMENT_NAME)
+                && parser.getNamespace().equals(MetaData.NAMESPACE)) {
+            Map<String, List<String>> metaData = new Hashtable<String, List<String>>();
+
+            eventType = parser.nextTag();
+
+            // Keep parsing until we've gotten to end of meta-data.
+            while ((eventType != XmlPullParser.END_TAG)
+                    || (!parser.getName().equals(MetaData.ELEMENT_NAME))) {
+                String name = parser.getAttributeValue(0);
+                String value = parser.nextText();
+
+                if (metaData.containsKey(name)) {
+                    List<String> values = metaData.get(name);
+                    values.add(value);
+                }
+                else {
+                    List<String> values = new ArrayList<String>();
+                    values.add(value);
+                    metaData.put(name, values);
+                }
+
+                eventType = parser.nextTag();
+            }
+
+            return metaData;
+        }
+
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Serializes a Map of String name/value pairs into the meta-data XML format.
+     *
+     * @param metaData the Map of meta-data as Map&lt;String,List&lt;String>>
+     * @return the meta-data values in XML form.
+     */
+    public static String serializeMetaData(Map<String, List<String>> metaData) {
+        StringBuilder buf = new StringBuilder();
+        if (metaData != null && metaData.size() > 0) {
+            buf.append("<metadata xmlns=\"http://jivesoftware.com/protocol/workgroup\">");
+            for (Iterator<String> i = metaData.keySet().iterator(); i.hasNext();) {
+                String key = i.next();
+                List<String> value = metaData.get(key);
+                for (Iterator<String> it = value.iterator(); it.hasNext();) {
+                    String v = it.next();
+                    buf.append("<value name=\"").append(key).append("\">");
+                    buf.append(StringUtils.escapeForXML(v));
+                    buf.append("</value>");
+                }
+            }
+            buf.append("</metadata>");
+        }
+        return buf.toString();
+    }
+}
diff --git a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ModelUtil.java b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ModelUtil.java
index 06a72b0..7f0c59b 100644
--- a/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ModelUtil.java
+++ b/protocols/bundles/org.jivesoftware.smack/src/org/jivesoftware/smackx/workgroup/util/ModelUtil.java
@@ -1,321 +1,321 @@
-/**

- * 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.smackx.workgroup.util;

-

-import java.util.*;

-

-/**

- * Utility methods frequently used by data classes and design-time

- * classes.

- */

-public final class ModelUtil {

-    private ModelUtil() {

-        //  Prevents instantiation.

-    }

-

-    /**

-     * This is a utility method that compares two objects when one or

-     * both of the objects might be <CODE>null</CODE>  The result of

-     * this method is determined as follows:

-     * <OL>

-     * <LI>If <CODE>o1</CODE> and <CODE>o2</CODE> are the same object

-     * according to the <CODE>==</CODE> operator, return

-     * <CODE>true</CODE>.

-     * <LI>Otherwise, if either <CODE>o1</CODE> or <CODE>o2</CODE> is

-     * <CODE>null</CODE>, return <CODE>false</CODE>.

-     * <LI>Otherwise, return <CODE>o1.equals(o2)</CODE>.

-     * </OL>

-     * <p/>

-     * This method produces the exact logically inverted result as the

-     * {@link #areDifferent(Object, Object)} method.<P>

-     * <p/>

-     * For array types, one of the <CODE>equals</CODE> methods in

-     * {@link java.util.Arrays} should be used instead of this method.

-     * Note that arrays with more than one dimension will require some

-     * custom code in order to implement <CODE>equals</CODE> properly.

-     */

-    public static final boolean areEqual(Object o1, Object o2) {

-        if (o1 == o2) {

-            return true;

-        }

-        else if (o1 == null || o2 == null) {

-            return false;

-        }

-        else {

-            return o1.equals(o2);

-        }

-    }

-

-    /**

-     * This is a utility method that compares two Booleans when one or

-     * both of the objects might be <CODE>null</CODE>  The result of

-     * this method is determined as follows:

-     * <OL>

-     * <LI>If <CODE>b1</CODE> and <CODE>b2</CODE> are both TRUE or

-     * neither <CODE>b1</CODE> nor <CODE>b2</CODE> is TRUE,

-     * return <CODE>true</CODE>.

-     * <LI>Otherwise, return <CODE>false</CODE>.

-     * </OL>

-     * <p/>

-     */

-    public static final boolean areBooleansEqual(Boolean b1, Boolean b2) {

-        // !jwetherb treat NULL the same as Boolean.FALSE

-        return (b1 == Boolean.TRUE && b2 == Boolean.TRUE) ||

-                (b1 != Boolean.TRUE && b2 != Boolean.TRUE);

-    }

-

-    /**

-     * This is a utility method that compares two objects when one or

-     * both of the objects might be <CODE>null</CODE>.  The result

-     * returned by this method is determined as follows:

-     * <OL>

-     * <LI>If <CODE>o1</CODE> and <CODE>o2</CODE> are the same object

-     * according to the <CODE>==</CODE> operator, return

-     * <CODE>false</CODE>.

-     * <LI>Otherwise, if either <CODE>o1</CODE> or <CODE>o2</CODE> is

-     * <CODE>null</CODE>, return <CODE>true</CODE>.

-     * <LI>Otherwise, return <CODE>!o1.equals(o2)</CODE>.

-     * </OL>

-     * <p/>

-     * This method produces the exact logically inverted result as the

-     * {@link #areEqual(Object, Object)} method.<P>

-     * <p/>

-     * For array types, one of the <CODE>equals</CODE> methods in

-     * {@link java.util.Arrays} should be used instead of this method.

-     * Note that arrays with more than one dimension will require some

-     * custom code in order to implement <CODE>equals</CODE> properly.

-     */

-    public static final boolean areDifferent(Object o1, Object o2) {

-        return !areEqual(o1, o2);

-    }

-

-

-    /**

-     * This is a utility method that compares two Booleans when one or

-     * both of the objects might be <CODE>null</CODE>  The result of

-     * this method is determined as follows:

-     * <OL>

-     * <LI>If <CODE>b1</CODE> and <CODE>b2</CODE> are both TRUE or

-     * neither <CODE>b1</CODE> nor <CODE>b2</CODE> is TRUE,

-     * return <CODE>false</CODE>.

-     * <LI>Otherwise, return <CODE>true</CODE>.

-     * </OL>

-     * <p/>

-     * This method produces the exact logically inverted result as the

-     * {@link #areBooleansEqual(Boolean, Boolean)} method.<P>

-     */

-    public static final boolean areBooleansDifferent(Boolean b1, Boolean b2) {

-        return !areBooleansEqual(b1, b2);

-    }

-

-

-    /**

-     * Returns <CODE>true</CODE> if the specified array is not null

-     * and contains a non-null element.  Returns <CODE>false</CODE>

-     * if the array is null or if all the array elements are null.

-     */

-    public static final boolean hasNonNullElement(Object[] array) {

-        if (array != null) {

-            final int n = array.length;

-            for (int i = 0; i < n; i++) {

-                if (array[i] != null) {

-                    return true;

-                }

-            }

-        }

-        return false;

-    }

-

-    /**

-     * Returns a single string that is the concatenation of all the

-     * strings in the specified string array.  A single space is

-     * put between each string array element.  Null array elements

-     * are skipped.  If the array itself is null, the empty string

-     * is returned.  This method is guaranteed to return a non-null

-     * value, if no expections are thrown.

-     */

-    public static final String concat(String[] strs) {

-        return concat(strs, " ");  //NOTRANS

-    }

-

-    /**

-     * Returns a single string that is the concatenation of all the

-     * strings in the specified string array.  The strings are separated

-     * by the specified delimiter.  Null array elements are skipped.  If

-     * the array itself is null, the empty string is returned.  This

-     * method is guaranteed to return a non-null value, if no expections

-     * are thrown.

-     */

-    public static final String concat(String[] strs, String delim) {

-        if (strs != null) {

-            final StringBuilder buf = new StringBuilder();

-            final int n = strs.length;

-            for (int i = 0; i < n; i++) {

-                final String str = strs[i];

-                if (str != null) {

-                    buf.append(str).append(delim);

-                }

-            }

-            final int length = buf.length();

-            if (length > 0) {

-                //  Trim trailing space.

-                buf.setLength(length - 1);

-            }

-            return buf.toString();

-        }

-        else {

-            return ""; // NOTRANS

-        }

-    }

-

-    /**

-     * Returns <CODE>true</CODE> if the specified {@link String} is not

-     * <CODE>null</CODE> and has a length greater than zero.  This is

-     * a very frequently occurring check.

-     */

-    public static final boolean hasLength(String s) {

-        return (s != null && s.length() > 0);

-    }

-

-

-    /**

-     * Returns <CODE>null</CODE> if the specified string is empty or

-     * <CODE>null</CODE>.  Otherwise the string itself is returned.

-     */

-    public static final String nullifyIfEmpty(String s) {

-        return ModelUtil.hasLength(s) ? s : null;

-    }

-

-    /**

-     * Returns <CODE>null</CODE> if the specified object is null

-     * or if its <CODE>toString()</CODE> representation is empty.

-     * Otherwise, the <CODE>toString()</CODE> representation of the

-     * object itself is returned.

-     */

-    public static final String nullifyingToString(Object o) {

-        return o != null ? nullifyIfEmpty(o.toString()) : null;

-    }

-

-    /**

-     * Determines if a string has been changed.

-     *

-     * @param oldString is the initial value of the String

-     * @param newString is the new value of the String

-     * @return true If both oldString and newString are null or if they are

-     *         both not null and equal to each other.  Otherwise returns false.

-     */

-    public static boolean hasStringChanged(String oldString, String newString) {

-        if (oldString == null && newString == null) {

-            return false;

-        }

-        else if ((oldString == null && newString != null)

-                || (oldString != null && newString == null)) {

-            return true;

-        }

-        else {

-            return !oldString.equals(newString);

-        }

-    }

-

-    public static String getTimeFromLong(long diff) {

-        final String HOURS = "h";

-        final String MINUTES = "min";

-        final String SECONDS = "sec";

-

-        final long MS_IN_A_DAY = 1000 * 60 * 60 * 24;

-        final long MS_IN_AN_HOUR = 1000 * 60 * 60;

-        final long MS_IN_A_MINUTE = 1000 * 60;

-        final long MS_IN_A_SECOND = 1000;

-        diff = diff % MS_IN_A_DAY;

-        long numHours = diff / MS_IN_AN_HOUR;

-        diff = diff % MS_IN_AN_HOUR;

-        long numMinutes = diff / MS_IN_A_MINUTE;

-        diff = diff % MS_IN_A_MINUTE;

-        long numSeconds = diff / MS_IN_A_SECOND;

-        diff = diff % MS_IN_A_SECOND;

-

-        StringBuilder buf = new StringBuilder();

-        if (numHours > 0) {

-            buf.append(numHours + " " + HOURS + ", ");

-        }

-

-        if (numMinutes > 0) {

-            buf.append(numMinutes + " " + MINUTES + ", ");

-        }

-

-        buf.append(numSeconds + " " + SECONDS);

-

-        String result = buf.toString();

-        return result;

-    }

-

-

-    /**

-     * Build a List of all elements in an Iterator.

-     */

-    public static <T> List<T> iteratorAsList(Iterator<T> i) {

-        ArrayList<T> list = new ArrayList<T>(10);

-        while (i.hasNext()) {

-            list.add(i.next());

-        }

-        return list;

-    }

-

-    /**

-     * Creates an Iterator that is the reverse of a ListIterator.

-     */

-    public static <T> Iterator<T> reverseListIterator(ListIterator<T> i) {

-        return new ReverseListIterator<T>(i);

-    }

-}

-

-/**

- * An Iterator that is the reverse of a ListIterator.

- */

-class ReverseListIterator<T> implements Iterator<T> {

-    private ListIterator<T> _i;

-

-    ReverseListIterator(ListIterator<T> i) {

-        _i = i;

-        while (_i.hasNext())

-            _i.next();

-    }

-

-    public boolean hasNext() {

-        return _i.hasPrevious();

-    }

-

-    public T next() {

-        return _i.previous();

-    }

-

-    public void remove() {

-        _i.remove();

-    }

-

-}

-

-

-

-

-

-

-

-

-

-

-

+/**
+ * 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.smackx.workgroup.util;
+
+import java.util.*;
+
+/**
+ * Utility methods frequently used by data classes and design-time
+ * classes.
+ */
+public final class ModelUtil {
+    private ModelUtil() {
+        //  Prevents instantiation.
+    }
+
+    /**
+     * This is a utility method that compares two objects when one or
+     * both of the objects might be <CODE>null</CODE>  The result of
+     * this method is determined as follows:
+     * <OL>
+     * <LI>If <CODE>o1</CODE> and <CODE>o2</CODE> are the same object
+     * according to the <CODE>==</CODE> operator, return
+     * <CODE>true</CODE>.
+     * <LI>Otherwise, if either <CODE>o1</CODE> or <CODE>o2</CODE> is
+     * <CODE>null</CODE>, return <CODE>false</CODE>.
+     * <LI>Otherwise, return <CODE>o1.equals(o2)</CODE>.
+     * </OL>
+     * <p/>
+     * This method produces the exact logically inverted result as the
+     * {@link #areDifferent(Object, Object)} method.<P>
+     * <p/>
+     * For array types, one of the <CODE>equals</CODE> methods in
+     * {@link java.util.Arrays} should be used instead of this method.
+     * Note that arrays with more than one dimension will require some
+     * custom code in order to implement <CODE>equals</CODE> properly.
+     */
+    public static final boolean areEqual(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        }
+        else if (o1 == null || o2 == null) {
+            return false;
+        }
+        else {
+            return o1.equals(o2);
+        }
+    }
+
+    /**
+     * This is a utility method that compares two Booleans when one or
+     * both of the objects might be <CODE>null</CODE>  The result of
+     * this method is determined as follows:
+     * <OL>
+     * <LI>If <CODE>b1</CODE> and <CODE>b2</CODE> are both TRUE or
+     * neither <CODE>b1</CODE> nor <CODE>b2</CODE> is TRUE,
+     * return <CODE>true</CODE>.
+     * <LI>Otherwise, return <CODE>false</CODE>.
+     * </OL>
+     * <p/>
+     */
+    public static final boolean areBooleansEqual(Boolean b1, Boolean b2) {
+        // !jwetherb treat NULL the same as Boolean.FALSE
+        return (b1 == Boolean.TRUE && b2 == Boolean.TRUE) ||
+                (b1 != Boolean.TRUE && b2 != Boolean.TRUE);
+    }
+
+    /**
+     * This is a utility method that compares two objects when one or
+     * both of the objects might be <CODE>null</CODE>.  The result
+     * returned by this method is determined as follows:
+     * <OL>
+     * <LI>If <CODE>o1</CODE> and <CODE>o2</CODE> are the same object
+     * according to the <CODE>==</CODE> operator, return
+     * <CODE>false</CODE>.
+     * <LI>Otherwise, if either <CODE>o1</CODE> or <CODE>o2</CODE> is
+     * <CODE>null</CODE>, return <CODE>true</CODE>.
+     * <LI>Otherwise, return <CODE>!o1.equals(o2)</CODE>.
+     * </OL>
+     * <p/>
+     * This method produces the exact logically inverted result as the
+     * {@link #areEqual(Object, Object)} method.<P>
+     * <p/>
+     * For array types, one of the <CODE>equals</CODE> methods in
+     * {@link java.util.Arrays} should be used instead of this method.
+     * Note that arrays with more than one dimension will require some
+     * custom code in order to implement <CODE>equals</CODE> properly.
+     */
+    public static final boolean areDifferent(Object o1, Object o2) {
+        return !areEqual(o1, o2);
+    }
+
+
+    /**
+     * This is a utility method that compares two Booleans when one or
+     * both of the objects might be <CODE>null</CODE>  The result of
+     * this method is determined as follows:
+     * <OL>
+     * <LI>If <CODE>b1</CODE> and <CODE>b2</CODE> are both TRUE or
+     * neither <CODE>b1</CODE> nor <CODE>b2</CODE> is TRUE,
+     * return <CODE>false</CODE>.
+     * <LI>Otherwise, return <CODE>true</CODE>.
+     * </OL>
+     * <p/>
+     * This method produces the exact logically inverted result as the
+     * {@link #areBooleansEqual(Boolean, Boolean)} method.<P>
+     */
+    public static final boolean areBooleansDifferent(Boolean b1, Boolean b2) {
+        return !areBooleansEqual(b1, b2);
+    }
+
+
+    /**
+     * Returns <CODE>true</CODE> if the specified array is not null
+     * and contains a non-null element.  Returns <CODE>false</CODE>
+     * if the array is null or if all the array elements are null.
+     */
+    public static final boolean hasNonNullElement(Object[] array) {
+        if (array != null) {
+            final int n = array.length;
+            for (int i = 0; i < n; i++) {
+                if (array[i] != null) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns a single string that is the concatenation of all the
+     * strings in the specified string array.  A single space is
+     * put between each string array element.  Null array elements
+     * are skipped.  If the array itself is null, the empty string
+     * is returned.  This method is guaranteed to return a non-null
+     * value, if no expections are thrown.
+     */
+    public static final String concat(String[] strs) {
+        return concat(strs, " ");  //NOTRANS
+    }
+
+    /**
+     * Returns a single string that is the concatenation of all the
+     * strings in the specified string array.  The strings are separated
+     * by the specified delimiter.  Null array elements are skipped.  If
+     * the array itself is null, the empty string is returned.  This
+     * method is guaranteed to return a non-null value, if no expections
+     * are thrown.
+     */
+    public static final String concat(String[] strs, String delim) {
+        if (strs != null) {
+            final StringBuilder buf = new StringBuilder();
+            final int n = strs.length;
+            for (int i = 0; i < n; i++) {
+                final String str = strs[i];
+                if (str != null) {
+                    buf.append(str).append(delim);
+                }
+            }
+            final int length = buf.length();
+            if (length > 0) {
+                //  Trim trailing space.
+                buf.setLength(length - 1);
+            }
+            return buf.toString();
+        }
+        else {
+            return ""; // NOTRANS
+        }
+    }
+
+    /**
+     * Returns <CODE>true</CODE> if the specified {@link String} is not
+     * <CODE>null</CODE> and has a length greater than zero.  This is
+     * a very frequently occurring check.
+     */
+    public static final boolean hasLength(String s) {
+        return (s != null && s.length() > 0);
+    }
+
+
+    /**
+     * Returns <CODE>null</CODE> if the specified string is empty or
+     * <CODE>null</CODE>.  Otherwise the string itself is returned.
+     */
+    public static final String nullifyIfEmpty(String s) {
+        return ModelUtil.hasLength(s) ? s : null;
+    }
+
+    /**
+     * Returns <CODE>null</CODE> if the specified object is null
+     * or if its <CODE>toString()</CODE> representation is empty.
+     * Otherwise, the <CODE>toString()</CODE> representation of the
+     * object itself is returned.
+     */
+    public static final String nullifyingToString(Object o) {
+        return o != null ? nullifyIfEmpty(o.toString()) : null;
+    }
+
+    /**
+     * Determines if a string has been changed.
+     *
+     * @param oldString is the initial value of the String
+     * @param newString is the new value of the String
+     * @return true If both oldString and newString are null or if they are
+     *         both not null and equal to each other.  Otherwise returns false.
+     */
+    public static boolean hasStringChanged(String oldString, String newString) {
+        if (oldString == null && newString == null) {
+            return false;
+        }
+        else if ((oldString == null && newString != null)
+                || (oldString != null && newString == null)) {
+            return true;
+        }
+        else {
+            return !oldString.equals(newString);
+        }
+    }
+
+    public static String getTimeFromLong(long diff) {
+        final String HOURS = "h";
+        final String MINUTES = "min";
+        final String SECONDS = "sec";
+
+        final long MS_IN_A_DAY = 1000 * 60 * 60 * 24;
+        final long MS_IN_AN_HOUR = 1000 * 60 * 60;
+        final long MS_IN_A_MINUTE = 1000 * 60;
+        final long MS_IN_A_SECOND = 1000;
+        diff = diff % MS_IN_A_DAY;
+        long numHours = diff / MS_IN_AN_HOUR;
+        diff = diff % MS_IN_AN_HOUR;
+        long numMinutes = diff / MS_IN_A_MINUTE;
+        diff = diff % MS_IN_A_MINUTE;
+        long numSeconds = diff / MS_IN_A_SECOND;
+        diff = diff % MS_IN_A_SECOND;
+
+        StringBuilder buf = new StringBuilder();
+        if (numHours > 0) {
+            buf.append(numHours + " " + HOURS + ", ");
+        }
+
+        if (numMinutes > 0) {
+            buf.append(numMinutes + " " + MINUTES + ", ");
+        }
+
+        buf.append(numSeconds + " " + SECONDS);
+
+        String result = buf.toString();
+        return result;
+    }
+
+
+    /**
+     * Build a List of all elements in an Iterator.
+     */
+    public static <T> List<T> iteratorAsList(Iterator<T> i) {
+        ArrayList<T> list = new ArrayList<T>(10);
+        while (i.hasNext()) {
+            list.add(i.next());
+        }
+        return list;
+    }
+
+    /**
+     * Creates an Iterator that is the reverse of a ListIterator.
+     */
+    public static <T> Iterator<T> reverseListIterator(ListIterator<T> i) {
+        return new ReverseListIterator<T>(i);
+    }
+}
+
+/**
+ * An Iterator that is the reverse of a ListIterator.
+ */
+class ReverseListIterator<T> implements Iterator<T> {
+    private ListIterator<T> _i;
+
+    ReverseListIterator(ListIterator<T> i) {
+        _i = i;
+        while (_i.hasNext())
+            _i.next();
+    }
+
+    public boolean hasNext() {
+        return _i.hasPrevious();
+    }
+
+    public T next() {
+        return _i.previous();
+    }
+
+    public void remove() {
+        _i.remove();
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+