diff --git a/config/checkstyle.xml b/config/checkstyle.xml index 8b4bbacb4..ec3a64ab5 100644 --- a/config/checkstyle.xml +++ b/config/checkstyle.xml @@ -11,6 +11,9 @@ + + + diff --git a/core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java b/core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java index ee2d6474e..3e8cee6c8 100644 --- a/core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java +++ b/core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java @@ -1,49 +1,49 @@ -/** - * - * Copyright 2009 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 - } - -} +/** + * + * Copyright 2009 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/AccountManager.java b/core/src/main/java/org/jivesoftware/smack/AccountManager.java index 5cb7aa986..c540ad5cc 100644 --- a/core/src/main/java/org/jivesoftware/smack/AccountManager.java +++ b/core/src/main/java/org/jivesoftware/smack/AccountManager.java @@ -332,4 +332,4 @@ public class AccountManager { info = (Registration)result; } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/Chat.java b/core/src/main/java/org/jivesoftware/smack/Chat.java index 37284e7db..7124bdd1d 100644 --- a/core/src/main/java/org/jivesoftware/smack/Chat.java +++ b/core/src/main/java/org/jivesoftware/smack/Chat.java @@ -186,4 +186,4 @@ public class Chat { && threadID.equals(((Chat)obj).getThreadID()) && participant.equals(((Chat)obj).getParticipant()); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/ConnectionListener.java b/core/src/main/java/org/jivesoftware/smack/ConnectionListener.java index 31369a77f..fa3b45213 100644 --- a/core/src/main/java/org/jivesoftware/smack/ConnectionListener.java +++ b/core/src/main/java/org/jivesoftware/smack/ConnectionListener.java @@ -63,4 +63,4 @@ public interface ConnectionListener { * @param e the exception that caused the reconnection to fail. */ public void reconnectionFailed(Exception e); -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/NonSASLAuthentication.java b/core/src/main/java/org/jivesoftware/smack/NonSASLAuthentication.java index 37d46c1af..0b6aa5ec8 100644 --- a/core/src/main/java/org/jivesoftware/smack/NonSASLAuthentication.java +++ b/core/src/main/java/org/jivesoftware/smack/NonSASLAuthentication.java @@ -1,140 +1,140 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 - * link 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(); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 + * link 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/core/src/main/java/org/jivesoftware/smack/OpenTrustManager.java b/core/src/main/java/org/jivesoftware/smack/OpenTrustManager.java index 3fc3051e7..103a72c25 100644 --- a/core/src/main/java/org/jivesoftware/smack/OpenTrustManager.java +++ b/core/src/main/java/org/jivesoftware/smack/OpenTrustManager.java @@ -1,46 +1,46 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 { + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/PacketInterceptor.java b/core/src/main/java/org/jivesoftware/smack/PacketInterceptor.java index d778d1cd0..e2df41093 100644 --- a/core/src/main/java/org/jivesoftware/smack/PacketInterceptor.java +++ b/core/src/main/java/org/jivesoftware/smack/PacketInterceptor.java @@ -1,47 +1,47 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.

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

- * - * 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); -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.

+ * + * 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.

+ * + * 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/core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java b/core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java index 00526994e..2c9599bf5 100644 --- a/core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java +++ b/core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java @@ -14,215 +14,215 @@ * 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; +package org.jivesoftware.smack; + +import org.jivesoftware.smack.packet.StreamError; +import java.util.Random; import java.util.logging.Logger; -/** - * 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.

- * - * The reconnection mechanism will try to reconnect periodically: - *

    - *
  1. For the first minute it will attempt to connect once every ten seconds. - *
  2. For the next five minutes it will attempt to connect once a minute. - *
  3. If that fails it will indefinitely try to connect once every five minutes. - *
- * - * @author Francisco Vives - */ -public class ReconnectionManager implements ConnectionListener { +/** + * 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.

+ * + * The reconnection mechanism will try to reconnect periodically: + *

    + *
  1. For the first minute it will attempt to connect once every ten seconds. + *
  2. For the next five minutes it will attempt to connect once a minute. + *
  3. If that fails it will indefinitely try to connect once every five minutes. + *
+ * + * @author Francisco Vives + */ +public class ReconnectionManager implements ConnectionListener { private static Logger log = Logger.getLogger(ReconnectionManager.class.getName()); - - // 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() { + + // 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. - *

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

    - *
  1. First it will try 6 times every 10 seconds. - *
  2. Then it will try 10 times every 1 minute. - *
  3. Finally it will try indefinitely every 5 minutes. - *
- */ - 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); - } + } + + /** + * Starts a reconnection mechanism if it was configured to do that. + * The algorithm is been executed when the first connection error is detected. + *

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

    + *
  1. First it will try 6 times every 10 seconds. + *
  2. Then it will try 10 times every 1 minute. + *
  3. Finally it will try indefinitely every 5 minutes. + *
+ */ + 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) { - log.warning("Sleeping thread interrupted"); - // 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) { + log.warning("Sleeping thread interrupted"); + // 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 - } - -} \ No newline at end of file + 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 + } + +} diff --git a/core/src/main/java/org/jivesoftware/smack/RosterEntry.java b/core/src/main/java/org/jivesoftware/smack/RosterEntry.java index 14f901a4a..057f49b8d 100644 --- a/core/src/main/java/org/jivesoftware/smack/RosterEntry.java +++ b/core/src/main/java/org/jivesoftware/smack/RosterEntry.java @@ -238,4 +238,4 @@ public class RosterEntry { return item; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/RosterGroup.java b/core/src/main/java/org/jivesoftware/smack/RosterGroup.java index 7183ac8fe..47c446893 100644 --- a/core/src/main/java/org/jivesoftware/smack/RosterGroup.java +++ b/core/src/main/java/org/jivesoftware/smack/RosterGroup.java @@ -248,4 +248,4 @@ public class RosterGroup { } } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/RosterListener.java b/core/src/main/java/org/jivesoftware/smack/RosterListener.java index 2305efcbe..159274a43 100644 --- a/core/src/main/java/org/jivesoftware/smack/RosterListener.java +++ b/core/src/main/java/org/jivesoftware/smack/RosterListener.java @@ -77,4 +77,4 @@ public interface RosterListener { * @see Roster#getPresence(String) */ public void presenceChanged(Presence presence); -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java index 562532d58..4be0789e4 100644 --- a/core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -1,588 +1,588 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.*; - -/** - *

This class is responsible authenticating the user using SASL, binding the resource - * to the connection and establishing a session with the server.

- * - *

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.

- * - *

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}.

- * - *

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.

- * - *

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.

- * - * @see org.jivesoftware.smack.sasl.SASLMechanism - * - * @author Gaston Dombiak - * @author Jay Kline - */ -public class SASLAuthentication implements UserAuthentication { - - private static Map> implementedMechanisms = new HashMap>(); - private static List mechanismsPreferences = new ArrayList(); - - private Connection connection; - private Collection serverMechanisms = new ArrayList(); - 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 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 index 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> getRegisterSASLMechanisms() { - List> answer = new ArrayList>(); - 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.

- * - * 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 mechanismClass = implementedMechanisms.get(selectedMechanism); - Constructor 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.

- * - * 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 mechanismClass = implementedMechanisms.get(selectedMechanism); - Constructor 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.

- * - * 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 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 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.*; + +/** + *

This class is responsible authenticating the user using SASL, binding the resource + * to the connection and establishing a session with the server.

+ * + *

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.

+ * + *

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}.

+ * + *

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.

+ * + *

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.

+ * + * @see org.jivesoftware.smack.sasl.SASLMechanism + * + * @author Gaston Dombiak + * @author Jay Kline + */ +public class SASLAuthentication implements UserAuthentication { + + private static Map> implementedMechanisms = new HashMap>(); + private static List mechanismsPreferences = new ArrayList(); + + private Connection connection; + private Collection serverMechanisms = new ArrayList(); + 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 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 index 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> getRegisterSASLMechanisms() { + List> answer = new ArrayList>(); + 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.

+ * + * 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 mechanismClass = implementedMechanisms.get(selectedMechanism); + Constructor 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.

+ * + * 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 mechanismClass = implementedMechanisms.get(selectedMechanism); + Constructor 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.

+ * + * 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 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 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; + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/SmackError.java b/core/src/main/java/org/jivesoftware/smack/SmackError.java index 14dfe05cd..95a3871fb 100644 --- a/core/src/main/java/org/jivesoftware/smack/SmackError.java +++ b/core/src/main/java/org/jivesoftware/smack/SmackError.java @@ -1,40 +1,40 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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; - -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; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; + +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/core/src/main/java/org/jivesoftware/smack/UserAuthentication.java b/core/src/main/java/org/jivesoftware/smack/UserAuthentication.java index 2649c0edc..bc6f6369d 100644 --- a/core/src/main/java/org/jivesoftware/smack/UserAuthentication.java +++ b/core/src/main/java/org/jivesoftware/smack/UserAuthentication.java @@ -1,76 +1,76 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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; -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/XMPPException.java b/core/src/main/java/org/jivesoftware/smack/XMPPException.java index 6212cfb4f..11718264d 100644 --- a/core/src/main/java/org/jivesoftware/smack/XMPPException.java +++ b/core/src/main/java/org/jivesoftware/smack/XMPPException.java @@ -235,4 +235,4 @@ public class XMPPException extends Exception { return buf.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java b/core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java index d67919e8d..4b9af81c2 100644 --- a/core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java +++ b/core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java @@ -92,4 +92,4 @@ public interface SmackDebugger { * the GUI */ public abstract PacketListener getWriterListener(); -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/filter/FromContainsFilter.java b/core/src/main/java/org/jivesoftware/smack/filter/FromContainsFilter.java index 5dc96a607..24a8804c3 100644 --- a/core/src/main/java/org/jivesoftware/smack/filter/FromContainsFilter.java +++ b/core/src/main/java/org/jivesoftware/smack/filter/FromContainsFilter.java @@ -48,4 +48,4 @@ public class FromContainsFilter implements PacketFilter { return packet.getFrom().toLowerCase().indexOf(from) != -1; } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java b/core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java index 789d8ab52..a0d5192a6 100644 --- a/core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java +++ b/core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java @@ -1,45 +1,45 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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)); - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java b/core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java index 6ebfc0037..141fcc9ac 100644 --- a/core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java +++ b/core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java @@ -97,4 +97,4 @@ public class OrFilter implements PacketFilter { public String toString() { return filters.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/filter/ToContainsFilter.java b/core/src/main/java/org/jivesoftware/smack/filter/ToContainsFilter.java index 82a5632f4..7694dc080 100644 --- a/core/src/main/java/org/jivesoftware/smack/filter/ToContainsFilter.java +++ b/core/src/main/java/org/jivesoftware/smack/filter/ToContainsFilter.java @@ -49,4 +49,4 @@ public class ToContainsFilter implements PacketFilter { return packet.getTo().toLowerCase().indexOf(to) != -1; } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java index fa4bd05d1..cb8560222 100644 --- a/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/LoggingInitializer.java @@ -1,55 +1,55 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -import org.jivesoftware.smack.util.FileUtils; - -/** - * Initializes the Java logging system. - * - * @author Robin Collier - * - */ -public class LoggingInitializer implements SmackInitializer { - - private static Logger log = Logger.getLogger(LoggingInitializer.class.getName()); - - private List exceptions = new LinkedList(); - - @Override - public void initialize() { - try { - LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:org.jivesofware.smack/jul.properties", null)); - } - catch (Exception e) { - log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e); - exceptions.add(e); - } - } - - @Override - public List getExceptions() { - return Collections.unmodifiableList(exceptions); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import org.jivesoftware.smack.util.FileUtils; + +/** + * Initializes the Java logging system. + * + * @author Robin Collier + * + */ +public class LoggingInitializer implements SmackInitializer { + + private static Logger log = Logger.getLogger(LoggingInitializer.class.getName()); + + private List exceptions = new LinkedList(); + + @Override + public void initialize() { + try { + LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:org.jivesofware.smack/jul.properties", null)); + } + catch (Exception e) { + log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e); + exceptions.add(e); + } + } + + @Override + public List getExceptions() { + return Collections.unmodifiableList(exceptions); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java index fe06da0cf..1d8a22d15 100644 --- a/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/SmackInitializer.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; - -import java.util.List; - -import org.jivesoftware.smack.SmackConfiguration; - -/** - * Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization. - * - *

- * Any implementation of this class MUST have a default constructor. - * - * @author Robin Collier - * - */ -public interface SmackInitializer { - void initialize(); - List getExceptions(); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; + +import java.util.List; + +import org.jivesoftware.smack.SmackConfiguration; + +/** + * Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization. + * + *

+ * Any implementation of this class MUST have a default constructor. + * + * @author Robin Collier + * + */ +public interface SmackInitializer { + void initialize(); + List getExceptions(); +} diff --git a/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java index fac93327b..a992f7feb 100644 --- a/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/UrlProviderFileInitializer.java @@ -1,81 +1,81 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jivesoftware.smack.provider.ProviderFileLoader; -import org.jivesoftware.smack.provider.ProviderManager; -import org.jivesoftware.smack.util.FileUtils; - -/** - * Loads the provider file defined by the URL returned by {@link #getFilePath()}. This file will be loaded on Smack initialization. - * - * @author Robin Collier - * - */ -public abstract class UrlProviderFileInitializer implements SmackInitializer { - private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName()); - - private List exceptions = new LinkedList(); - - @Override - public void initialize() { - String filePath = getFilePath(); - - try { - InputStream is = FileUtils.getStreamForUrl(filePath, getClassLoader()); - - if (is != null) { - log.log(Level.INFO, "Loading providers for file [" + filePath + "]"); - ProviderFileLoader pfl = new ProviderFileLoader(is); - ProviderManager.getInstance().addLoader(pfl); - exceptions.addAll(pfl.getLoadingExceptions()); - } - else { - log.log(Level.WARNING, "No input stream created for " + filePath); - exceptions.add(new IOException("No input stream created for " + filePath)); - } - } - catch (Exception e) { - log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e); - exceptions.add(e); - } - } - - @Override - public List getExceptions() { - return Collections.unmodifiableList(exceptions); - } - - protected abstract String getFilePath(); - - /** - * Returns an array of class loaders to load resources from. - * - * @return an array of ClassLoader instances. - */ - protected ClassLoader getClassLoader() { - return null; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.provider.ProviderFileLoader; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.util.FileUtils; + +/** + * Loads the provider file defined by the URL returned by {@link #getFilePath()}. This file will be loaded on Smack initialization. + * + * @author Robin Collier + * + */ +public abstract class UrlProviderFileInitializer implements SmackInitializer { + private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName()); + + private List exceptions = new LinkedList(); + + @Override + public void initialize() { + String filePath = getFilePath(); + + try { + InputStream is = FileUtils.getStreamForUrl(filePath, getClassLoader()); + + if (is != null) { + log.log(Level.INFO, "Loading providers for file [" + filePath + "]"); + ProviderFileLoader pfl = new ProviderFileLoader(is); + ProviderManager.getInstance().addLoader(pfl); + exceptions.addAll(pfl.getLoadingExceptions()); + } + else { + log.log(Level.WARNING, "No input stream created for " + filePath); + exceptions.add(new IOException("No input stream created for " + filePath)); + } + } + catch (Exception e) { + log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e); + exceptions.add(e); + } + } + + @Override + public List getExceptions() { + return Collections.unmodifiableList(exceptions); + } + + protected abstract String getFilePath(); + + /** + * Returns an array of class loaders to load resources from. + * + * @return an array of ClassLoader instances. + */ + protected ClassLoader getClassLoader() { + return null; + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java b/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java index 917ac6285..d84ffa0ab 100644 --- a/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java +++ b/core/src/main/java/org/jivesoftware/smack/initializer/VmArgInitializer.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; - -import org.jivesoftware.smack.provider.ProviderManager; - - -/** - * Looks for a provider file location based on the VM argument smack.provider.file. If it is supplied, its value will - * be used as a file location for a providers file and loaded into the {@link ProviderManager} on Smack initialization. - * - * @author Robin Collier - * - */ -public class VmArgInitializer extends UrlProviderFileInitializer { - - protected String getFilePath() { - return System.getProperty("smack.provider.file"); - } - - @Override - public void initialize() { - if (getFilePath() != null) { - super.initialize(); - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.initializer; + +import org.jivesoftware.smack.provider.ProviderManager; + + +/** + * Looks for a provider file location based on the VM argument smack.provider.file. If it is supplied, its value will + * be used as a file location for a providers file and loaded into the {@link ProviderManager} on Smack initialization. + * + * @author Robin Collier + * + */ +public class VmArgInitializer extends UrlProviderFileInitializer { + + protected String getFilePath() { + return System.getProperty("smack.provider.file"); + } + + @Override + public void initialize() { + if (getFilePath() != null) { + super.initialize(); + } + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Bind.java b/core/src/main/java/org/jivesoftware/smack/packet/Bind.java index 95a6201fc..a90afb516 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Bind.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Bind.java @@ -1,68 +1,68 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.

- * - * For more information refer to the following - * link. - * - * @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(""); - if (resource != null) { - buf.append("").append(resource).append(""); - } - if (jid != null) { - buf.append("").append(jid).append(""); - } - buf.append(""); - return buf.toString(); - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.

+ * + * For more information refer to the following + * link. + * + * @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(""); + if (resource != null) { + buf.append("").append(resource).append(""); + } + if (jid != null) { + buf.append("").append(jid).append(""); + } + buf.append(""); + return buf.toString(); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/DefaultPacketExtension.java b/core/src/main/java/org/jivesoftware/smack/packet/DefaultPacketExtension.java index 95cc803f0..e238ae1ed 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/DefaultPacketExtension.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/DefaultPacketExtension.java @@ -127,4 +127,4 @@ public class DefaultPacketExtension implements PacketExtension { } map.put(name, value); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Message.java b/core/src/main/java/org/jivesoftware/smack/packet/Message.java index 8f76a8373..1a87eb2e3 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -669,4 +669,4 @@ public class Message extends Packet { } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Presence.java b/core/src/main/java/org/jivesoftware/smack/packet/Presence.java index 2a5446773..eb5d5fe6f 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Presence.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Presence.java @@ -352,4 +352,4 @@ public class Presence extends Packet { */ dnd } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Registration.java b/core/src/main/java/org/jivesoftware/smack/packet/Registration.java index 6a4044362..1b81c9520 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Registration.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Registration.java @@ -104,4 +104,4 @@ public class Registration extends IQ { buf.append(""); return buf.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Session.java b/core/src/main/java/org/jivesoftware/smack/packet/Session.java index 38d61a910..a835a2e06 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Session.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Session.java @@ -1,42 +1,42 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.

- * - * If a server supports sessions, it MUST include a session 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.

- * - * For more information refer to the following - * link. - * - * @author Gaston Dombiak - */ -public class Session extends IQ { - - public Session() { - setType(IQ.Type.SET); - } - - public String getChildElementXML() { - return ""; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.

+ * + * If a server supports sessions, it MUST include a session 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.

+ * + * For more information refer to the following + * link. + * + * @author Gaston Dombiak + */ +public class Session extends IQ { + + public Session() { + setType(IQ.Type.SET); + } + + public String getChildElementXML() { + return ""; + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/packet/StreamError.java b/core/src/main/java/org/jivesoftware/smack/packet/StreamError.java index 9391299d5..1816dd95e 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/StreamError.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/StreamError.java @@ -1,120 +1,120 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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:

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
CodeDescription
bad-format the entity has sent XML that cannot be processed
unsupported-encoding the entity has sent a namespace prefix that is - * unsupported
bad-namespace-prefix Remote Server Timeout
conflict the server is closing the active stream for this entity - * because a new stream has been initiated that conflicts with the existing - * stream.
connection-timeout the entity has not generated any traffic over - * the stream for some period of time.
host-gone 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.
host-unknown 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.
improper-addressing a stanza sent between two servers lacks a 'to' - * or 'from' attribute
internal-server-error the server has experienced a - * misconfiguration.
invalid-from the JID or hostname provided in a 'from' address does - * not match an authorized JID.
invalid-namespace the streams namespace name is invalid.
invalid-xml the entity has sent invalid XML over the stream.
not-authorized the entity has attempted to send data before the - * stream has been authenticated
policy-violation the entity has violated some local service - * policy.
remote-connection-failed Rthe server is unable to properly connect - * to a remote entity.
resource-constraint Rthe server lacks the system resources necessary - * to service the stream.
restricted-xml the entity has attempted to send restricted XML - * features.
see-other-host the server will not provide service to the initiating - * entity but is redirecting traffic to another host.
system-shutdown the server is being shut down and all active streams - * are being closed.
undefined-condition the error condition is not one of those defined - * by the other conditions in this list.
unsupported-encoding the initiating entity has encoded the stream in - * an encoding that is not supported.
unsupported-stanza-type the initiating entity has sent a first-level - * child of the stream that is not supported.
unsupported-version the value of the 'version' attribute provided by - * the initiating entity in the stream header specifies a version of XMPP that is not - * supported.
not-well-formed the initiating entity has sent XML that is not - * well-formed.
- * - * @author Gaston Dombiak - */ -public class StreamError { - - public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams"; - - private String code; - private String text; - - public StreamError(String code) { - super(); - this.code = code; - } - - public StreamError(String code, String text) { - this(code); - this.text = text; - } - - /** - * Returns the error code. - * - * @return the error code. - */ - public String getCode() { - return code; - } - - /** - * Returns the error text, which may be null. - * - * @return the error text. - */ - public String getText() { - return text; - } - - public String toString() { - StringBuilder txt = new StringBuilder(); - txt.append("stream:error (").append(code).append(")"); - if (text != null) txt.append(" text: ").append(text); - return txt.toString(); - } -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
CodeDescription
bad-format the entity has sent XML that cannot be processed
unsupported-encoding the entity has sent a namespace prefix that is + * unsupported
bad-namespace-prefix Remote Server Timeout
conflict the server is closing the active stream for this entity + * because a new stream has been initiated that conflicts with the existing + * stream.
connection-timeout the entity has not generated any traffic over + * the stream for some period of time.
host-gone 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.
host-unknown 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.
improper-addressing a stanza sent between two servers lacks a 'to' + * or 'from' attribute
internal-server-error the server has experienced a + * misconfiguration.
invalid-from the JID or hostname provided in a 'from' address does + * not match an authorized JID.
invalid-namespace the streams namespace name is invalid.
invalid-xml the entity has sent invalid XML over the stream.
not-authorized the entity has attempted to send data before the + * stream has been authenticated
policy-violation the entity has violated some local service + * policy.
remote-connection-failed Rthe server is unable to properly connect + * to a remote entity.
resource-constraint Rthe server lacks the system resources necessary + * to service the stream.
restricted-xml the entity has attempted to send restricted XML + * features.
see-other-host the server will not provide service to the initiating + * entity but is redirecting traffic to another host.
system-shutdown the server is being shut down and all active streams + * are being closed.
undefined-condition the error condition is not one of those defined + * by the other conditions in this list.
unsupported-encoding the initiating entity has encoded the stream in + * an encoding that is not supported.
unsupported-stanza-type the initiating entity has sent a first-level + * child of the stream that is not supported.
unsupported-version the value of the 'version' attribute provided by + * the initiating entity in the stream header specifies a version of XMPP that is not + * supported.
not-well-formed the initiating entity has sent XML that is not + * well-formed.
+ * + * @author Gaston Dombiak + */ +public class StreamError { + + public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams"; + + private String code; + private String text; + + public StreamError(String code) { + super(); + this.code = code; + } + + public StreamError(String code, String text) { + this(code); + this.text = text; + } + + /** + * Returns the error code. + * + * @return the error code. + */ + public String getCode() { + return code; + } + + /** + * Returns the error text, which may be null. + * + * @return the error text. + */ + public String getText() { + return text; + } + + public String toString() { + StringBuilder txt = new StringBuilder(); + txt.append("stream:error (").append(code).append(")"); + if (text != null) txt.append(" text: ").append(text); + return txt.toString(); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/AbstractProviderInfo.java b/core/src/main/java/org/jivesoftware/smack/provider/AbstractProviderInfo.java index c783bb6ac..7aa26172f 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/AbstractProviderInfo.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/AbstractProviderInfo.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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; - -abstract class AbstractProviderInfo { - private String element; - private String ns; - private Object provider; - - AbstractProviderInfo(String elementName, String namespace, Object iqOrExtProvider) { - element = elementName; - ns = namespace; - provider = iqOrExtProvider; - } - - public String getElementName() { - return element; - } - - public String getNamespace() { - return ns; - } - - Object getProvider() { - return provider; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; + +abstract class AbstractProviderInfo { + private String element; + private String ns; + private Object provider; + + AbstractProviderInfo(String elementName, String namespace, Object iqOrExtProvider) { + element = elementName; + ns = namespace; + provider = iqOrExtProvider; + } + + public String getElementName() { + return element; + } + + public String getNamespace() { + return ns; + } + + Object getProvider() { + return provider; + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java b/core/src/main/java/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java index e8ee87476..7e7e63fcf 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/EmbeddedExtensionProvider.java @@ -1,109 +1,109 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.xmlpull.v1.XmlPullParser; - -/** - * - * This class simplifies parsing of embedded elements by using the - * Template Method Pattern. - * 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. - * - *

To use this class, you simply register your subclasses as extension providers in the - * smack.properties file. Then they will be automatically picked up and used to parse - * any child elements. - * - *

- * For example, given the following message
- * 
- * <message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo>
- *    <event xmlns='http://jabber.org/protocol/pubsub#event>
- *       <items node='princely_musings'>
- *          <item id='asdjkwei3i34234n356'>
- *             <entry xmlns='http://www.w3.org/2005/Atom'>
- *                <title>Soliloquy</title>
- *                <link rel='alternative' type='text/html'/>
- *                <id>tag:denmark.lit,2003:entry-32397</id>
- *             </entry>
- *          </item>
- *       </items>
- *    </event>
- * </message>
- * 
- * 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.
- * 
- *   <extensionProvider>
- *      <elementName>items</elementName>
- *      <namespace>http://jabber.org/protocol/pubsub#event</namespace>
- *      <className>org.jivesoftware.smackx.provider.ItemsEventProvider</className>
- *   </extensionProvider>
- *   <extensionProvider>
- *       <elementName>item</elementName>
- *       <namespace>http://jabber.org/protocol/pubsub#event</namespace>
- *       <className>org.jivesoftware.smackx.provider.ItemProvider</className>
- *   </extensionProvider>
- * 
- * 
- * - * @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 attMap = new HashMap(); - - for(int i=0; i extensions = new ArrayList(); - - 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 attributeMap, List content); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.xmlpull.v1.XmlPullParser; + +/** + * + * This class simplifies parsing of embedded elements by using the + * Template Method Pattern. + * 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. + * + *

To use this class, you simply register your subclasses as extension providers in the + * smack.properties file. Then they will be automatically picked up and used to parse + * any child elements. + * + *

+ * For example, given the following message
+ * 
+ * <message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo>
+ *    <event xmlns='http://jabber.org/protocol/pubsub#event>
+ *       <items node='princely_musings'>
+ *          <item id='asdjkwei3i34234n356'>
+ *             <entry xmlns='http://www.w3.org/2005/Atom'>
+ *                <title>Soliloquy</title>
+ *                <link rel='alternative' type='text/html'/>
+ *                <id>tag:denmark.lit,2003:entry-32397</id>
+ *             </entry>
+ *          </item>
+ *       </items>
+ *    </event>
+ * </message>
+ * 
+ * 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.
+ * 
+ *   <extensionProvider>
+ *      <elementName>items</elementName>
+ *      <namespace>http://jabber.org/protocol/pubsub#event</namespace>
+ *      <className>org.jivesoftware.smackx.provider.ItemsEventProvider</className>
+ *   </extensionProvider>
+ *   <extensionProvider>
+ *       <elementName>item</elementName>
+ *       <namespace>http://jabber.org/protocol/pubsub#event</namespace>
+ *       <className>org.jivesoftware.smackx.provider.ItemProvider</className>
+ *   </extensionProvider>
+ * 
+ * 
+ * + * @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 attMap = new HashMap(); + + for(int i=0; i extensions = new ArrayList(); + + 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 attributeMap, List content); +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ExtensionProviderInfo.java b/core/src/main/java/org/jivesoftware/smack/provider/ExtensionProviderInfo.java index a00a4e0fc..c062664b3 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ExtensionProviderInfo.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ExtensionProviderInfo.java @@ -1,49 +1,49 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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; - -/** - * Defines the information required to register a packet extension Provider with the {@link ProviderManager} when using the - * {@link ProviderLoader}. - * - * @author Robin Collier - * - */ -public final class ExtensionProviderInfo extends AbstractProviderInfo { - - /** - * Defines an extension provider which implements the PacketExtensionProvider interface. - * - * @param elementName Element that provider parses. - * @param namespace Namespace that provider parses. - * @param extProvider The provider implementation. - */ - public ExtensionProviderInfo(String elementName, String namespace, PacketExtensionProvider extProvider) { - super(elementName, namespace, extProvider); - } - - /** - * Defines an extension provider which is adheres to the JavaBean spec for parsing the extension. - * - * @param elementName Element that provider parses. - * @param namespace Namespace that provider parses. - * @param beanClass The provider bean class. - */ - public ExtensionProviderInfo(String elementName, String namespace, Class beanClass) { - super(elementName, namespace, beanClass); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; + +/** + * Defines the information required to register a packet extension Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class ExtensionProviderInfo extends AbstractProviderInfo { + + /** + * Defines an extension provider which implements the PacketExtensionProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param extProvider The provider implementation. + */ + public ExtensionProviderInfo(String elementName, String namespace, PacketExtensionProvider extProvider) { + super(elementName, namespace, extProvider); + } + + /** + * Defines an extension provider which is adheres to the JavaBean spec for parsing the extension. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param beanClass The provider bean class. + */ + public ExtensionProviderInfo(String elementName, String namespace, Class beanClass) { + super(elementName, namespace, beanClass); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java b/core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java index f2ac95f41..aaa099b91 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/IQProvider.java @@ -41,4 +41,4 @@ public interface IQProvider { * @throws Exception if an error occurs parsing the XML. */ public IQ parseIQ(XmlPullParser parser) throws Exception; -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/IQProviderInfo.java b/core/src/main/java/org/jivesoftware/smack/provider/IQProviderInfo.java index 264618596..9265c32e2 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/IQProviderInfo.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/IQProviderInfo.java @@ -1,51 +1,51 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 org.jivesoftware.smack.packet.IQ; - -/** - * Defines the information required to register an IQ Provider with the {@link ProviderManager} when using the - * {@link ProviderLoader}. - * - * @author Robin Collier - * - */ -public final class IQProviderInfo extends AbstractProviderInfo { - - /** - * Defines an IQ provider which implements the IQProvider interface. - * - * @param elementName Element that provider parses. - * @param namespace Namespace that provider parses. - * @param iqProvider The provider implementation. - */ - public IQProviderInfo(String elementName, String namespace, IQProvider iqProvider) { - super(elementName, namespace, iqProvider); - } - - /** - * Defines an IQ class which can be used as a provider via introspection. - * - * @param elementName Element that provider parses. - * @param namespace Namespace that provider parses. - * @param iqProviderClass The IQ class being parsed. - */ - public IQProviderInfo(String elementName, String namespace, Class iqProviderClass) { - super(elementName, namespace, iqProviderClass); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 org.jivesoftware.smack.packet.IQ; + +/** + * Defines the information required to register an IQ Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class IQProviderInfo extends AbstractProviderInfo { + + /** + * Defines an IQ provider which implements the IQProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProvider The provider implementation. + */ + public IQProviderInfo(String elementName, String namespace, IQProvider iqProvider) { + super(elementName, namespace, iqProvider); + } + + /** + * Defines an IQ class which can be used as a provider via introspection. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProviderClass The IQ class being parsed. + */ + public IQProviderInfo(String elementName, String namespace, Class iqProviderClass) { + super(elementName, namespace, iqProviderClass); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java b/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java index 8798948b8..d8b9e6109 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java @@ -1,172 +1,172 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.PacketExtension; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; - -/** - * Loads the {@link IQProvider} and {@link PacketExtensionProvider} information from a standard provider file in preparation - * for loading into the {@link ProviderManager}. - * - * @author Robin Collier - * - */ -public class ProviderFileLoader implements ProviderLoader { - private final static Logger log = Logger.getLogger(ProviderFileLoader.class.getName()); - - private Collection iqProviders; - private Collection extProviders; - private InputStream providerStream; - private List exceptions = new LinkedList(); - - public ProviderFileLoader(InputStream providerFileInputStream) { - setInputStream(providerFileInputStream); - } - - public ProviderFileLoader() { - } - - @Override - public Collection getIQProviderInfo() { - initialize(); - return iqProviders; - } - - @Override - public Collection getExtensionProviderInfo() { - initialize(); - return extProviders; - } - - public List getLoadingExceptions() { - return Collections.unmodifiableList(exceptions); - } - - @SuppressWarnings("unchecked") - private synchronized void initialize() { - // Check to see if already initialized - if (iqProviders != null) { - return; - } - - if (providerStream == null) { - throw new IllegalArgumentException("No input stream set for loader"); - } - iqProviders = new ArrayList(); - extProviders = new ArrayList(); - - // Load processing providers. - try { - XmlPullParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(providerStream, "UTF-8"); - int eventType = parser.getEventType(); - do { - if (eventType == XmlPullParser.START_TAG) { - String typeName = parser.getName(); - - try { - if (!"smackProviders".equals(typeName)) { - parser.next(); - parser.next(); - String elementName = parser.nextText(); - parser.next(); - parser.next(); - String namespace = parser.nextText(); - parser.next(); - parser.next(); - String className = parser.nextText(); - - try { - // Attempt to load the provider class and then create - // a new instance if it's an IQProvider. Otherwise, if it's - // an IQ class, add the class object itself, then we'll use - // reflection later to create instances of the class. - if ("iqProvider".equals(typeName)) { - // Add the provider to the map. - Class provider = Class.forName(className); - - if (IQProvider.class.isAssignableFrom(provider)) { - iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider) provider.newInstance())); - } - else if (IQ.class.isAssignableFrom(provider)) { - iqProviders.add(new IQProviderInfo(elementName, namespace, (Class)provider)); - } - } - else { - // Attempt to load the provider class and then create - // a new instance if it's an ExtensionProvider. Otherwise, if it's - // a PacketExtension, add the class object itself and - // then we'll use reflection later to create instances - // of the class. - Class provider = Class.forName(className); - if (PacketExtensionProvider.class.isAssignableFrom(provider)) { - extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider) provider.newInstance())); - } - else if (PacketExtension.class.isAssignableFrom(provider)) { - extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider)); - } - } - } - catch (ClassNotFoundException cnfe) { - log.log(Level.SEVERE, "Could not find provider class", cnfe); - exceptions.add(cnfe); - } - } - } - catch (IllegalArgumentException illExc) { - log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc); - exceptions.add(illExc); - } - } - eventType = parser.next(); - } - while (eventType != XmlPullParser.END_DOCUMENT); - } - catch (Exception e){ - log.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e); - } - finally { - try { - providerStream.close(); - } - catch (Exception e) { - // Ignore. - } - } - } - - public void setInputStream(InputStream providerFileInput) { - if (providerFileInput == null) { - throw new IllegalArgumentException("InputStream cannot be null"); - } - providerStream = providerFileInput; - initialize(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.PacketExtension; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; + +/** + * Loads the {@link IQProvider} and {@link PacketExtensionProvider} information from a standard provider file in preparation + * for loading into the {@link ProviderManager}. + * + * @author Robin Collier + * + */ +public class ProviderFileLoader implements ProviderLoader { + private final static Logger log = Logger.getLogger(ProviderFileLoader.class.getName()); + + private Collection iqProviders; + private Collection extProviders; + private InputStream providerStream; + private List exceptions = new LinkedList(); + + public ProviderFileLoader(InputStream providerFileInputStream) { + setInputStream(providerFileInputStream); + } + + public ProviderFileLoader() { + } + + @Override + public Collection getIQProviderInfo() { + initialize(); + return iqProviders; + } + + @Override + public Collection getExtensionProviderInfo() { + initialize(); + return extProviders; + } + + public List getLoadingExceptions() { + return Collections.unmodifiableList(exceptions); + } + + @SuppressWarnings("unchecked") + private synchronized void initialize() { + // Check to see if already initialized + if (iqProviders != null) { + return; + } + + if (providerStream == null) { + throw new IllegalArgumentException("No input stream set for loader"); + } + iqProviders = new ArrayList(); + extProviders = new ArrayList(); + + // Load processing providers. + try { + XmlPullParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(providerStream, "UTF-8"); + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + String typeName = parser.getName(); + + try { + if (!"smackProviders".equals(typeName)) { + parser.next(); + parser.next(); + String elementName = parser.nextText(); + parser.next(); + parser.next(); + String namespace = parser.nextText(); + parser.next(); + parser.next(); + String className = parser.nextText(); + + try { + // Attempt to load the provider class and then create + // a new instance if it's an IQProvider. Otherwise, if it's + // an IQ class, add the class object itself, then we'll use + // reflection later to create instances of the class. + if ("iqProvider".equals(typeName)) { + // Add the provider to the map. + Class provider = Class.forName(className); + + if (IQProvider.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider) provider.newInstance())); + } + else if (IQ.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (Class)provider)); + } + } + else { + // Attempt to load the provider class and then create + // a new instance if it's an ExtensionProvider. Otherwise, if it's + // a PacketExtension, add the class object itself and + // then we'll use reflection later to create instances + // of the class. + Class provider = Class.forName(className); + if (PacketExtensionProvider.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider) provider.newInstance())); + } + else if (PacketExtension.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider)); + } + } + } + catch (ClassNotFoundException cnfe) { + log.log(Level.SEVERE, "Could not find provider class", cnfe); + exceptions.add(cnfe); + } + } + } + catch (IllegalArgumentException illExc) { + log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc); + exceptions.add(illExc); + } + } + eventType = parser.next(); + } + while (eventType != XmlPullParser.END_DOCUMENT); + } + catch (Exception e){ + log.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e); + } + finally { + try { + providerStream.close(); + } + catch (Exception e) { + // Ignore. + } + } + } + + public void setInputStream(InputStream providerFileInput) { + if (providerFileInput == null) { + throw new IllegalArgumentException("InputStream cannot be null"); + } + providerStream = providerFileInput; + initialize(); + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ProviderLoader.java b/core/src/main/java/org/jivesoftware/smack/provider/ProviderLoader.java index 5df15ff87..6b66edfba 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ProviderLoader.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ProviderLoader.java @@ -1,39 +1,39 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.Collection; - -/** - * Used to load providers into the {@link ProviderManager}. - * - * @author Robin Collier - */ -public interface ProviderLoader { - - /** - * Provides the IQ provider info for the creation of IQ providers to be added to the ProviderManager. - * @return The IQ provider info to load. - */ - Collection getIQProviderInfo(); - - /** - * Provides the extension providers for the creation of extension providers to be added to the ProviderManager. - * @return The extension provider info to load. - */ - Collection getExtensionProviderInfo(); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.Collection; + +/** + * Used to load providers into the {@link ProviderManager}. + * + * @author Robin Collier + */ +public interface ProviderLoader { + + /** + * Provides the IQ provider info for the creation of IQ providers to be added to the ProviderManager. + * @return The IQ provider info to load. + */ + Collection getIQProviderInfo(); + + /** + * Provides the extension providers for the creation of extension providers to be added to the ProviderManager. + * @return The extension provider info to load. + */ + Collection getExtensionProviderInfo(); +} diff --git a/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java b/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java index fe0d28e6d..c70076e56 100644 --- a/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java +++ b/core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java @@ -295,4 +295,4 @@ public final class ProviderManager { buf.append("<").append(elementName).append("/><").append(namespace).append("/>"); return buf.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java index 5f1cae8d6..90669556b 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java @@ -1,59 +1,59 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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()); - } - - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java index 5fe23fe9b..1e75be788 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java index 051de4546..9c7bb115a 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/sasl/SASLExternalMechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLExternalMechanism.java index ff80ee8d3..f45ce5203 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLExternalMechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLExternalMechanism.java @@ -1,56 +1,56 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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: - *
    - *
  • javax.net.ssl.keyStore to the location of the keyStore - *
  • javax.net.ssl.keyStorePassword to the password of the keyStore - *
  • javax.net.ssl.trustStore to the location of the trustStore - *
  • javax.net.ssl.trustStorePassword to the the password of the trustStore - *
- * - * 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"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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: + *
    + *
  • javax.net.ssl.keyStore to the location of the keyStore + *
  • javax.net.ssl.keyStorePassword to the password of the keyStore + *
  • javax.net.ssl.trustStore to the location of the trustStore + *
  • javax.net.ssl.trustStorePassword to the the password of the trustStore + *
+ * + * 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/core/src/main/java/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java index c92d95dbf..78f0fac10 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java @@ -1,86 +1,86 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 auth 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 props = new HashMap(); - props.put(Sasl.SERVER_AUTH,"TRUE"); - sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh); - authenticate(); - } - - /** - * Builds and sends the auth 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 props = new HashMap(); - props.put(Sasl.SERVER_AUTH,"TRUE"); - sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this); - authenticate(); - } - - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 auth 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 props = new HashMap(); + props.put(Sasl.SERVER_AUTH,"TRUE"); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh); + authenticate(); + } + + /** + * Builds and sends the auth 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 props = new HashMap(); + props.put(Sasl.SERVER_AUTH,"TRUE"); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this); + authenticate(); + } + + +} diff --git a/core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java index 77841459a..527562461 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -1,400 +1,400 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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: - *
    - *
  • {@link #getName()} -- returns the common name of the SASL mechanism.
  • - *
- * Subclasses will likely want to implement their own versions of these mthods: - *
  • {@link #authenticate(String, String, String)} -- Initiate authentication stanza using the - * deprecated method.
  • - *
  • {@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza - * using the CallbackHandler method.
  • - *
  • {@link #challengeReceived(String)} -- Handle a challenge from the server.
  • - * - * - * Basic XMPP SASL authentication steps: - * 1. Client authentication initialization, stanza sent to the server (Base64 encoded): - * - * 2. Server sends back to the client the challenge response (Base64 encoded) - * sample: - * realm=,nonce="OA6MG9tEQGm2hh",qop="auth",charset=utf-8,algorithm=md5-sess - * 3. The client responds back to the server (Base 64 encoded): - * sample: - * username=,realm=,nonce="OA6MG9tEQGm2hh", - * cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth, - * digest-uri=, - * response=d388dad90d4bbd760a152321f2143af7, - * charset=utf-8, - * authzid= - * 4. The server evaluates if the user is present and contained in the REALM - * if successful it sends: (Base64 encoded) - * if not successful it sends: - * sample: - * - * cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA== - * - * - - * - * @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 auth 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 props = new HashMap(); - 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. - *

    - * 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 auth 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 props = new HashMap(); - 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(""); - if (authenticationText != null && - authenticationText.trim().length() > 0) { - stanza.append(authenticationText); - } - stanza.append(""); - 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(""); - if (data != null && - data.trim().length() > 0) { - stanza.append(data); - } - stanza.append(""); - 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(""); - if (authenticationText != null) { - stanza.append(authenticationText); - } - stanza.append(""); - 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(""); - if (data != null && - data.trim().length() > 0) { - stanza.append(data); - } - stanza.append(""); - 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(""); - if (condition != null && - condition.trim().length() > 0) { - stanza.append("<").append(condition).append("/>"); - } - stanza.append(""); - return stanza.toString(); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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: + *

      + *
    • {@link #getName()} -- returns the common name of the SASL mechanism.
    • + *
    + * Subclasses will likely want to implement their own versions of these mthods: + *
  • {@link #authenticate(String, String, String)} -- Initiate authentication stanza using the + * deprecated method.
  • + *
  • {@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza + * using the CallbackHandler method.
  • + *
  • {@link #challengeReceived(String)} -- Handle a challenge from the server.
  • + * + * + * Basic XMPP SASL authentication steps: + * 1. Client authentication initialization, stanza sent to the server (Base64 encoded): + * + * 2. Server sends back to the client the challenge response (Base64 encoded) + * sample: + * realm=,nonce="OA6MG9tEQGm2hh",qop="auth",charset=utf-8,algorithm=md5-sess + * 3. The client responds back to the server (Base 64 encoded): + * sample: + * username=,realm=,nonce="OA6MG9tEQGm2hh", + * cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth, + * digest-uri=, + * response=d388dad90d4bbd760a152321f2143af7, + * charset=utf-8, + * authzid= + * 4. The server evaluates if the user is present and contained in the REALM + * if successful it sends: (Base64 encoded) + * if not successful it sends: + * sample: + * + * cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA== + * + * + + * + * @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 auth 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 props = new HashMap(); + 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. + *

    + * 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 auth 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 props = new HashMap(); + 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(""); + if (authenticationText != null && + authenticationText.trim().length() > 0) { + stanza.append(authenticationText); + } + stanza.append(""); + 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(""); + if (data != null && + data.trim().length() > 0) { + stanza.append(data); + } + stanza.append(""); + 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(""); + if (authenticationText != null) { + stanza.append(authenticationText); + } + stanza.append(""); + 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(""); + if (data != null && + data.trim().length() > 0) { + stanza.append(data); + } + stanza.append(""); + 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(""); + if (condition != null && + condition.trim().length() > 0) { + stanza.append("<").append(condition).append("/>"); + } + stanza.append(""); + return stanza.toString(); + } + } +} diff --git a/core/src/main/java/org/jivesoftware/smack/sasl/SASLPlainMechanism.java b/core/src/main/java/org/jivesoftware/smack/sasl/SASLPlainMechanism.java index 9d2284594..e80f2069f 100644 --- a/core/src/main/java/org/jivesoftware/smack/sasl/SASLPlainMechanism.java +++ b/core/src/main/java/org/jivesoftware/smack/sasl/SASLPlainMechanism.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/java/org/jivesoftware/smack/util/Base64.java b/core/src/main/java/org/jivesoftware/smack/util/Base64.java index 498e96460..dc839c639 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/Base64.java +++ b/core/src/main/java/org/jivesoftware/smack/util/Base64.java @@ -14,1673 +14,1673 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.util; +package org.jivesoftware.smack.util; import java.util.logging.Level; import java.util.logging.Logger; - -/** - *

    Encodes and decodes to and from Base64 notation.

    - * This code was obtained from http://iharder.net/base64

    - * - * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.2.1 - */ -public class Base64 -{ - private static Logger log = Logger.getLogger(Base64.class.getName()); - /* ******** 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: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not 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: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - 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: - * http://www.faqs.org/rfcs/rfc3548.html. - * 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: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - 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(){} - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @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 - - - /** - *

    Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

    - *

    This is the lowest level of the encoding methods with - * all possible parameters.

    - * - * @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 destination 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 null. - * 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 null. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeObject( myObj, Base64.GZIP ) or - *

    - * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @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); - - 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 ) - { - log.log(Level.SEVERE, "Error encoding object", e); - 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. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @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. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @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 ) - { +/** + *

    Encodes and decodes to and from Base64 notation.

    + * This code was obtained from http://iharder.net/base64

    + * + * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.2.1 + */ +public class Base64 +{ + private static Logger log = Logger.getLogger(Base64.class.getName()); + + /* ******** 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: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not 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: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + 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: + * http://www.faqs.org/rfcs/rfc3548.html. + * 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: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + 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(){} + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @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 + + + /** + *

    Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

    + *

    This is the lowest level of the encoding methods with + * all possible parameters.

    + * + * @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 destination 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 null. + * 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 null. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeObject( myObj, Base64.GZIP ) or + *

    + * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @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); + + 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 ) + { + log.log(Level.SEVERE, "Error encoding object", e); + 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. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @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. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @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 ) + { log.log(Level.SEVERE, "Error encoding bytes", e); - 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 source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

    This is the lowest level of the decoding methods with - * all possible parameters.

    - * - * - * @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; + 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 source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

    This is the lowest level of the decoding methods with + * all possible parameters.

    + * + * + * @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){ - log.log(Level.SEVERE, e.getMessage(), e); - log.severe(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - log.severe(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - log.severe(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - log.severe(""+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 - { - log.warning("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 - // - - // 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 null 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 ) - { - log.log(Level.SEVERE, "Error reading object", e); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { + log.log(Level.SEVERE, e.getMessage(), e); + log.severe(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + log.severe(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + log.severe(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + log.severe(""+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 + { + log.warning("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 + // + + // 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 null 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 ) + { + log.log(Level.SEVERE, "Error reading object", e); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { log.log(Level.SEVERE, "Class not found for encoded object", e); - 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 true if successful, false 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 true if successful, false 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 ) - { - log.warning("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 ) - { - log.log(Level.SEVERE, "Error decoding from file " + filename, e); - } // 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 ) - { - log.log(Level.SEVERE, "Error encoding from file " + filename, e); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - /** - * Reads infile and encodes it to outfile. - * - * @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 ) { + 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 true if successful, false 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 true if successful, false 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 ) + { + log.warning("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 ) + { + log.log(Level.SEVERE, "Error decoding from file " + filename, e); + } // 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 ) + { + log.log(Level.SEVERE, "Error encoding from file " + filename, e); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @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 ) { log.log(Level.SEVERE, "Error encoding file " + infile, ex); - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end encodeFileToFile - - - /** - * Reads infile and decodes it to outfile. - * - * @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 ) { + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @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 ) { log.log(Level.SEVERE, "Error decoding file " + infile, ex); - } // 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 - * java.io.InputStream, 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 + } // 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 + * java.io.InputStream, 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[] decodabet; // Local copies to avoid extra method calls - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream 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. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream 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; + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream 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. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream 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.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 len 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 - * java.io.OutputStream, 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; + 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 len 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 + * java.io.OutputStream, 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[] decodabet; // Local copies to avoid extra method calls - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream 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. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream 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]; + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream 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. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream 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.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 len - * 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.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 len + * 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/core/src/main/java/org/jivesoftware/smack/util/Cache.java b/core/src/main/java/org/jivesoftware/smack/util/Cache.java index 0b992ab2b..98bdaad20 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/Cache.java +++ b/core/src/main/java/org/jivesoftware/smack/util/Cache.java @@ -675,4 +675,4 @@ public class Cache implements Map { return object.toString(); } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/util/DNSUtil.java b/core/src/main/java/org/jivesoftware/smack/util/DNSUtil.java index 7fab52512..9fd15bced 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/DNSUtil.java +++ b/core/src/main/java/org/jivesoftware/smack/util/DNSUtil.java @@ -230,4 +230,4 @@ public class DNSUtil { } return pos; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jivesoftware/smack/util/FileUtils.java b/core/src/main/java/org/jivesoftware/smack/util/FileUtils.java index fb7f969b5..dae5609a3 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/FileUtils.java +++ b/core/src/main/java/org/jivesoftware/smack/util/FileUtils.java @@ -1,75 +1,75 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -public final class FileUtils { - - private FileUtils() { - } - - public static InputStream getStreamForUrl(String url, ClassLoader loader) throws MalformedURLException, IOException { - URI fileUri = URI.create(url); - - if (fileUri.getScheme() == null) { - throw new MalformedURLException("No protocol found in file URL: " + url); - } - - if (fileUri.getScheme().equals("classpath")) { - // Get an array of class loaders to try loading the providers files from. - ClassLoader[] classLoaders = getClassLoaders(); - for (ClassLoader classLoader : classLoaders) { - InputStream is = classLoader.getResourceAsStream(fileUri.getSchemeSpecificPart()); - - if (is != null) { - return is; - } - } - } - else { - return fileUri.toURL().openStream(); - } - return null; - } - - /** - * Returns default classloaders. - * - * @return an array of ClassLoader instances. - */ - public static ClassLoader[] getClassLoaders() { - ClassLoader[] classLoaders = new ClassLoader[2]; - classLoaders[0] = FileUtils.class.getClassLoader(); - classLoaders[1] = Thread.currentThread().getContextClassLoader(); - // Clean up possible null values. Note that #getClassLoader may return a null value. - List loaders = new ArrayList(); - - for (ClassLoader classLoader : classLoaders) { - if (classLoader != null) { - loaders.add(classLoader); - } - } - return loaders.toArray(new ClassLoader[loaders.size()]); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class FileUtils { + + private FileUtils() { + } + + public static InputStream getStreamForUrl(String url, ClassLoader loader) throws MalformedURLException, IOException { + URI fileUri = URI.create(url); + + if (fileUri.getScheme() == null) { + throw new MalformedURLException("No protocol found in file URL: " + url); + } + + if (fileUri.getScheme().equals("classpath")) { + // Get an array of class loaders to try loading the providers files from. + ClassLoader[] classLoaders = getClassLoaders(); + for (ClassLoader classLoader : classLoaders) { + InputStream is = classLoader.getResourceAsStream(fileUri.getSchemeSpecificPart()); + + if (is != null) { + return is; + } + } + } + else { + return fileUri.toURL().openStream(); + } + return null; + } + + /** + * Returns default classloaders. + * + * @return an array of ClassLoader instances. + */ + public static ClassLoader[] getClassLoaders() { + ClassLoader[] classLoaders = new ClassLoader[2]; + classLoaders[0] = FileUtils.class.getClassLoader(); + classLoaders[1] = Thread.currentThread().getContextClassLoader(); + // Clean up possible null values. Note that #getClassLoader may return a null value. + List loaders = new ArrayList(); + + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + loaders.add(classLoader); + } + } + return loaders.toArray(new ClassLoader[loaders.size()]); + } + +} diff --git a/core/src/main/java/org/jivesoftware/smack/util/SyncPacketSend.java b/core/src/main/java/org/jivesoftware/smack/util/SyncPacketSend.java index 2933def59..386ddb6d8 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/SyncPacketSend.java +++ b/core/src/main/java/org/jivesoftware/smack/util/SyncPacketSend.java @@ -1,67 +1,67 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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()); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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/core/src/main/resources/org.jivesoftware.smack/jul.properties b/core/src/main/resources/org.jivesoftware.smack/jul.properties index fe90575ec..ec7c39aa2 100644 --- a/core/src/main/resources/org.jivesoftware.smack/jul.properties +++ b/core/src/main/resources/org.jivesoftware.smack/jul.properties @@ -1,3 +1,3 @@ -# Java Util Logging configuration for Smack. -handlers = java.util.logging.ConsoleHandler +# Java Util Logging configuration for Smack. +handlers = java.util.logging.ConsoleHandler .level = WARNING \ No newline at end of file diff --git a/core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java b/core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java index e6c394508..ea31c7701 100644 --- a/core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java +++ b/core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java @@ -1,440 +1,440 @@ -/** - * - * Copyright 2010 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import org.jivesoftware.smack.ChatManager.MatchMode; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Message.Type; -import org.jivesoftware.smack.packet.Packet; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -public class ChatConnectionTest { - - private DummyConnection connection; - - @Before - public void setUp() throws Exception { - connection = getConnection(); - } - - @After - public void tearDown() throws Exception { - if (connection != null) - connection.disconnect(); - } - - @Test - public void validateDefaultSetNormalIncluded() { - ChatManager.setDefaultIsNormalIncluded(false); - assertFalse(getConnection().getChatManager().isNormalIncluded()); - - ChatManager.setDefaultIsNormalIncluded(true); - assertTrue(getConnection().getChatManager().isNormalIncluded()); - } - - @Test - public void validateDefaultSetMatchMode() { - ChatManager.setDefaultMatchMode(MatchMode.NONE); - assertEquals(MatchMode.NONE, getConnection().getChatManager().getMatchMode()); - - ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); - assertEquals(MatchMode.BARE_JID, getConnection().getChatManager().getMatchMode()); - } - - @Ignore - @Test - public void validateMessageTypeWithDefaults() { - DummyConnection dc = getConnection(); - ChatManager cm = dc.getChatManager(); - TestChatManagerListener listener = new TestChatManagerListener(); - cm.addChatListener(listener); - Message incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.chat); - processServerMessage(incomingChat, dc); - assertNotNull(listener.getNewChat()); - - dc = getConnection(); - cm = dc.getChatManager(); - listener = new TestChatManagerListener(); - cm.addChatListener(listener); - incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.normal); - processServerMessage(incomingChat, dc); - assertNotNull(listener.getNewChat()); - - dc = getConnection(); - cm = dc.getChatManager(); - listener = new TestChatManagerListener(); - cm.addChatListener(listener); - incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.groupchat); - processServerMessage(incomingChat, dc); - assertNull(listener.getNewChat()); - - dc = getConnection(); - cm = dc.getChatManager(); - listener = new TestChatManagerListener(); - cm.addChatListener(listener); - incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.headline); - processServerMessage(incomingChat, dc); - assertNull(listener.getNewChat()); - } - - @Test - public void validateMessageTypeWithNoNormal() { - ChatManager.setDefaultIsNormalIncluded(false); - DummyConnection dc = getConnection(); - ChatManager cm = dc.getChatManager(); - TestChatManagerListener listener = new TestChatManagerListener(); - cm.addChatListener(listener); - Message incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.chat); - processServerMessage(incomingChat, dc); - assertNotNull(listener.getNewChat()); - - dc = getConnection(); - cm = dc.getChatManager(); - listener = new TestChatManagerListener(); - cm.addChatListener(listener); - incomingChat = createChatPacket("134", true); - incomingChat.setType(Type.normal); - processServerMessage(incomingChat, dc); - assertNull(listener.getNewChat()); - } - - // No thread behaviour - @Test - public void chatMatchedOnJIDWhenNoThreadBareMode() { - // MatchMode.BARE_JID is the default, so setting required. - DummyConnection con = getConnection(); - TestMessageListener msgListener = new TestMessageListener(); - TestChatManagerListener listener = new TestChatManagerListener(msgListener); - con.getChatManager().addChatListener(listener); - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - - // Should match on chat with full jid - incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - assertEquals(2, msgListener.getNumMessages()); - - // Should match on chat with bare jid - incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat, con); - assertEquals(3, msgListener.getNumMessages()); - } - - @Test - public void chatMatchedOnJIDWhenNoThreadJidMode() { - DummyConnection con = getConnection(); - TestMessageListener msgListener = new TestMessageListener(); - TestChatManagerListener listener = new TestChatManagerListener(msgListener); - ChatManager cm = con.getChatManager(); - cm.setMatchMode(MatchMode.SUPPLIED_JID); - cm.addChatListener(listener); - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - cm.removeChatListener(listener); - - // Should match on chat with full jid - incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - assertEquals(2, msgListener.getNumMessages()); - - // Should not match on chat with bare jid - TestChatManagerListener listener2 = new TestChatManagerListener(); - cm.addChatListener(listener2); - incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat, con); - assertEquals(2, msgListener.getNumMessages()); - assertNotNull(listener2.getNewChat()); - } - - @Test - public void chatMatchedOnJIDWhenNoThreadNoneMode() { - DummyConnection con = getConnection(); - TestMessageListener msgListener = new TestMessageListener(); - TestChatManagerListener listener = new TestChatManagerListener(msgListener); - ChatManager cm = con.getChatManager(); - cm.setMatchMode(MatchMode.NONE); - cm.addChatListener(listener); - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertEquals(1, msgListener.getNumMessages()); - cm.removeChatListener(listener); - - // Should not match on chat with full jid - TestChatManagerListener listener2 = new TestChatManagerListener(); - cm.addChatListener(listener2); - incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat, con); - assertEquals(1, msgListener.getNumMessages()); - assertNotNull(newChat); - cm.removeChatListener(listener2); - - // Should not match on chat with bare jid - TestChatManagerListener listener3 = new TestChatManagerListener(); - cm.addChatListener(listener3); - incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat, con); - assertEquals(1, msgListener.getNumMessages()); - assertNotNull(listener3.getNewChat()); - } - - /** - * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread - * id and the user is a full jid. - */ - @Test - public void chatFoundWhenNoThreadFullJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); - } - - /** - * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread - * id and the user is a base jid. - */ - @Test - public void chatFoundWhenNoThreadBaseJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); - } - - /** - * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id - * and the user is a full jid. - */ - @Test - public void chatFoundWithSameThreadFullJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(outgoing.getThreadID(), true); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); - } - - /** - * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id - * and the user is a base jid. - */ - @Test - public void chatFoundWithSameThreadBaseJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(outgoing.getThreadID(), false); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); - } - - /** - * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a - * different id and the same user as a base jid. - */ - @Test - public void chatNotFoundWithDiffThreadBaseJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", false); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertFalse(newChat == outgoing); - } - - /** - * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a - * different id and the same base jid. - */ - @Test - public void chatNotFoundWithDiffThreadFullJid() { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - - Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", true); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertFalse(newChat == outgoing); - } - - @Test - public void chatNotMatchedWithTypeNormal() { - TestChatManagerListener listener = new TestChatManagerListener(); - DummyConnection con = getConnection(); - ChatManager cm = con.getChatManager(); - cm.setNormalIncluded(false); - cm.addChatListener(listener); - - Message incomingChat = createChatPacket(null, false); - incomingChat.setType(Type.normal); - processServerMessage(incomingChat); - - assertNull(listener.getNewChat()); - } - - @SuppressWarnings("unused") - private ChatManager getChatManager(boolean includeNormal, MatchMode mode) { - ChatManager cm = getConnection().getChatManager(); - cm.setMatchMode(mode); - cm.setNormalIncluded(includeNormal); - return cm; - } - - private DummyConnection getConnection() { - DummyConnection con = new DummyConnection(); - - try { - con.connect(); - con.login("me", "secret"); - } catch (XMPPException e) { - // No need for handling in a dummy connection. - } - return con; - } - private Message createChatPacket(final String threadId, final boolean isFullJid) { - Message chatMsg = new Message("me@testserver", Message.Type.chat); - chatMsg.setBody("the body message - " + System.currentTimeMillis()); - chatMsg.setFrom("you@testserver" + (isFullJid ? "/resource" : "")); - - if (threadId != null) - chatMsg.setThread(threadId); - return chatMsg; - } - - private void processServerMessage(Packet incomingChat) { - processServerMessage(incomingChat, connection); - } - - private void processServerMessage(Packet incomingChat, DummyConnection con) { - TestChatServer chatServer = new TestChatServer(incomingChat, con); - chatServer.start(); - try { - chatServer.join(); - } catch (InterruptedException e) { - fail(); - } - } - - class TestChatManagerListener implements ChatManagerListener { - private Chat newChat; - private MessageListener listener; - - public TestChatManagerListener(TestMessageListener msgListener) { - listener = msgListener; - } - - public TestChatManagerListener() { - } - - @Override - public void chatCreated(Chat chat, boolean createdLocally) { - newChat = chat; - - if (listener != null) - newChat.addMessageListener(listener); - } - - public Chat getNewChat() { - return newChat; - } - } - - private class TestChatServer extends Thread { - private Packet chatPacket; - private DummyConnection con; - - TestChatServer(Packet chatMsg, DummyConnection conect) { - chatPacket = chatMsg; - con = conect; - } - - @Override - public void run() { - con.processPacket(chatPacket); - } - } - - private class TestMessageListener implements MessageListener { - private Chat msgChat; - private int counter = 0; - - @Override - public void processMessage(Chat chat, Message message) { - msgChat = chat; - counter++; - } - - @SuppressWarnings("unused") - public Chat getChat() { - return msgChat; - } - - public int getNumMessages() { - return counter; - } - }; -} +/** + * + * Copyright 2010 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.jivesoftware.smack.ChatManager.MatchMode; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smack.packet.Packet; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +public class ChatConnectionTest { + + private DummyConnection connection; + + @Before + public void setUp() throws Exception { + connection = getConnection(); + } + + @After + public void tearDown() throws Exception { + if (connection != null) + connection.disconnect(); + } + + @Test + public void validateDefaultSetNormalIncluded() { + ChatManager.setDefaultIsNormalIncluded(false); + assertFalse(getConnection().getChatManager().isNormalIncluded()); + + ChatManager.setDefaultIsNormalIncluded(true); + assertTrue(getConnection().getChatManager().isNormalIncluded()); + } + + @Test + public void validateDefaultSetMatchMode() { + ChatManager.setDefaultMatchMode(MatchMode.NONE); + assertEquals(MatchMode.NONE, getConnection().getChatManager().getMatchMode()); + + ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); + assertEquals(MatchMode.BARE_JID, getConnection().getChatManager().getMatchMode()); + } + + @Ignore + @Test + public void validateMessageTypeWithDefaults() { + DummyConnection dc = getConnection(); + ChatManager cm = dc.getChatManager(); + TestChatManagerListener listener = new TestChatManagerListener(); + cm.addChatListener(listener); + Message incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.chat); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.groupchat); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.headline); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + } + + @Test + public void validateMessageTypeWithNoNormal() { + ChatManager.setDefaultIsNormalIncluded(false); + DummyConnection dc = getConnection(); + ChatManager cm = dc.getChatManager(); + TestChatManagerListener listener = new TestChatManagerListener(); + cm.addChatListener(listener); + Message incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.chat); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + } + + // No thread behaviour + @Test + public void chatMatchedOnJIDWhenNoThreadBareMode() { + // MatchMode.BARE_JID is the default, so setting required. + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + con.getChatManager().addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + + // Should match on chat with full jid + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + + // Should match on chat with bare jid + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(3, msgListener.getNumMessages()); + } + + @Test + public void chatMatchedOnJIDWhenNoThreadJidMode() { + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + ChatManager cm = con.getChatManager(); + cm.setMatchMode(MatchMode.SUPPLIED_JID); + cm.addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + cm.removeChatListener(listener); + + // Should match on chat with full jid + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + + // Should not match on chat with bare jid + TestChatManagerListener listener2 = new TestChatManagerListener(); + cm.addChatListener(listener2); + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + assertNotNull(listener2.getNewChat()); + } + + @Test + public void chatMatchedOnJIDWhenNoThreadNoneMode() { + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + ChatManager cm = con.getChatManager(); + cm.setMatchMode(MatchMode.NONE); + cm.addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertEquals(1, msgListener.getNumMessages()); + cm.removeChatListener(listener); + + // Should not match on chat with full jid + TestChatManagerListener listener2 = new TestChatManagerListener(); + cm.addChatListener(listener2); + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(1, msgListener.getNumMessages()); + assertNotNull(newChat); + cm.removeChatListener(listener2); + + // Should not match on chat with bare jid + TestChatManagerListener listener3 = new TestChatManagerListener(); + cm.addChatListener(listener3); + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(1, msgListener.getNumMessages()); + assertNotNull(listener3.getNewChat()); + } + + /** + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread + * id and the user is a full jid. + */ + @Test + public void chatFoundWhenNoThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); + } + + /** + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread + * id and the user is a base jid. + */ + @Test + public void chatFoundWhenNoThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); + } + + /** + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id + * and the user is a full jid. + */ + @Test + public void chatFoundWithSameThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(outgoing.getThreadID(), true); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); + } + + /** + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id + * and the user is a base jid. + */ + @Test + public void chatFoundWithSameThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(outgoing.getThreadID(), false); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); + } + + /** + * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a + * different id and the same user as a base jid. + */ + @Test + public void chatNotFoundWithDiffThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", false); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertFalse(newChat == outgoing); + } + + /** + * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a + * different id and the same base jid. + */ + @Test + public void chatNotFoundWithDiffThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + + Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", true); + processServerMessage(incomingChat); + + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertFalse(newChat == outgoing); + } + + @Test + public void chatNotMatchedWithTypeNormal() { + TestChatManagerListener listener = new TestChatManagerListener(); + DummyConnection con = getConnection(); + ChatManager cm = con.getChatManager(); + cm.setNormalIncluded(false); + cm.addChatListener(listener); + + Message incomingChat = createChatPacket(null, false); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat); + + assertNull(listener.getNewChat()); + } + + @SuppressWarnings("unused") + private ChatManager getChatManager(boolean includeNormal, MatchMode mode) { + ChatManager cm = getConnection().getChatManager(); + cm.setMatchMode(mode); + cm.setNormalIncluded(includeNormal); + return cm; + } + + private DummyConnection getConnection() { + DummyConnection con = new DummyConnection(); + + try { + con.connect(); + con.login("me", "secret"); + } catch (XMPPException e) { + // No need for handling in a dummy connection. + } + return con; + } + private Message createChatPacket(final String threadId, final boolean isFullJid) { + Message chatMsg = new Message("me@testserver", Message.Type.chat); + chatMsg.setBody("the body message - " + System.currentTimeMillis()); + chatMsg.setFrom("you@testserver" + (isFullJid ? "/resource" : "")); + + if (threadId != null) + chatMsg.setThread(threadId); + return chatMsg; + } + + private void processServerMessage(Packet incomingChat) { + processServerMessage(incomingChat, connection); + } + + private void processServerMessage(Packet incomingChat, DummyConnection con) { + TestChatServer chatServer = new TestChatServer(incomingChat, con); + chatServer.start(); + try { + chatServer.join(); + } catch (InterruptedException e) { + fail(); + } + } + + class TestChatManagerListener implements ChatManagerListener { + private Chat newChat; + private MessageListener listener; + + public TestChatManagerListener(TestMessageListener msgListener) { + listener = msgListener; + } + + public TestChatManagerListener() { + } + + @Override + public void chatCreated(Chat chat, boolean createdLocally) { + newChat = chat; + + if (listener != null) + newChat.addMessageListener(listener); + } + + public Chat getNewChat() { + return newChat; + } + } + + private class TestChatServer extends Thread { + private Packet chatPacket; + private DummyConnection con; + + TestChatServer(Packet chatMsg, DummyConnection conect) { + chatPacket = chatMsg; + con = conect; + } + + @Override + public void run() { + con.processPacket(chatPacket); + } + } + + private class TestMessageListener implements MessageListener { + private Chat msgChat; + private int counter = 0; + + @Override + public void processMessage(Chat chat, Message message) { + msgChat = chat; + counter++; + } + + @SuppressWarnings("unused") + public Chat getChat() { + return msgChat; + } + + public int getNumMessages() { + return counter; + } + }; +} diff --git a/core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java b/core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java index 7e3d02f6d..a7fe81cd0 100644 --- a/core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java +++ b/core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java @@ -1,214 +1,214 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import org.jivesoftware.smack.filter.PacketFilter; -import org.jivesoftware.smack.packet.Packet; -import org.junit.Test; - -public class PacketCollectorTest -{ - - @Test - public void verifyRollover() - { - TestPacketCollector collector = new TestPacketCollector(null, new OKEverything(), 5); - - for (int i=0; i<6; i++) - { - Packet testPacket = new TestPacket(i); - collector.processPacket(testPacket); - } - - // Assert that '0' has rolled off - assertEquals("1", collector.nextResult().getPacketID()); - assertEquals("2", collector.nextResult().getPacketID()); - assertEquals("3", collector.nextResult().getPacketID()); - assertEquals("4", collector.nextResult().getPacketID()); - assertEquals("5", collector.pollResult().getPacketID()); - assertNull(collector.pollResult()); - - for (int i=10; i<15; i++) - { - Packet testPacket = new TestPacket(i); - collector.processPacket(testPacket); - } - - assertEquals("10", collector.nextResult().getPacketID()); - assertEquals("11", collector.nextResult().getPacketID()); - assertEquals("12", collector.nextResult().getPacketID()); - assertEquals("13", collector.nextResult().getPacketID()); - assertEquals("14", collector.pollResult().getPacketID()); - assertNull(collector.pollResult()); - - assertNull(collector.nextResult(1000)); - } - - /** - * Although this doesn't guarentee anything due to the nature of threading, it can - * potentially catch problems. - */ - @Test - public void verifyThreadSafety() - { - int insertCount = 500; - final TestPacketCollector collector = new TestPacketCollector(null, new OKEverything(), insertCount); - - Thread consumer1 = new Thread(new Runnable() - { - @Override - public void run() - { - try - { - while (true) - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - Packet packet = collector.nextResult(); -// System.out.println(Thread.currentThread().getName() + " packet: " + packet); - } - } - catch (RuntimeException re) - { - if (re.getCause() instanceof InterruptedException) - { -// System.out.println(Thread.currentThread().getName() + " has been interupted"); - } - } - } - }); - consumer1.setName("consumer 1"); - - Thread consumer2 = new Thread(new Runnable() - { - @Override - public void run() - { - Packet p = null; - - do - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - p = collector.nextResult(1); -// System.out.println(Thread.currentThread().getName() + " packet: " + p); - } - while (p != null); - } - }); - consumer2.setName("consumer 2"); - - Thread consumer3 = new Thread(new Runnable() - { - @Override - public void run() - { - Packet p = null; - - do - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - p = collector.pollResult(); -// System.out.println(Thread.currentThread().getName() + " packet: " + p); - } - while (p != null); - } - }); - consumer3.setName("consumer 3"); - - consumer1.start(); - consumer2.start(); - consumer3.start(); - - for(int i=0; i" + getPacketID() + ""; - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.packet.Packet; +import org.junit.Test; + +public class PacketCollectorTest +{ + + @Test + public void verifyRollover() + { + TestPacketCollector collector = new TestPacketCollector(null, new OKEverything(), 5); + + for (int i=0; i<6; i++) + { + Packet testPacket = new TestPacket(i); + collector.processPacket(testPacket); + } + + // Assert that '0' has rolled off + assertEquals("1", collector.nextResult().getPacketID()); + assertEquals("2", collector.nextResult().getPacketID()); + assertEquals("3", collector.nextResult().getPacketID()); + assertEquals("4", collector.nextResult().getPacketID()); + assertEquals("5", collector.pollResult().getPacketID()); + assertNull(collector.pollResult()); + + for (int i=10; i<15; i++) + { + Packet testPacket = new TestPacket(i); + collector.processPacket(testPacket); + } + + assertEquals("10", collector.nextResult().getPacketID()); + assertEquals("11", collector.nextResult().getPacketID()); + assertEquals("12", collector.nextResult().getPacketID()); + assertEquals("13", collector.nextResult().getPacketID()); + assertEquals("14", collector.pollResult().getPacketID()); + assertNull(collector.pollResult()); + + assertNull(collector.nextResult(1000)); + } + + /** + * Although this doesn't guarentee anything due to the nature of threading, it can + * potentially catch problems. + */ + @Test + public void verifyThreadSafety() + { + int insertCount = 500; + final TestPacketCollector collector = new TestPacketCollector(null, new OKEverything(), insertCount); + + Thread consumer1 = new Thread(new Runnable() + { + @Override + public void run() + { + try + { + while (true) + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + Packet packet = collector.nextResult(); +// System.out.println(Thread.currentThread().getName() + " packet: " + packet); + } + } + catch (RuntimeException re) + { + if (re.getCause() instanceof InterruptedException) + { +// System.out.println(Thread.currentThread().getName() + " has been interupted"); + } + } + } + }); + consumer1.setName("consumer 1"); + + Thread consumer2 = new Thread(new Runnable() + { + @Override + public void run() + { + Packet p = null; + + do + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + p = collector.nextResult(1); +// System.out.println(Thread.currentThread().getName() + " packet: " + p); + } + while (p != null); + } + }); + consumer2.setName("consumer 2"); + + Thread consumer3 = new Thread(new Runnable() + { + @Override + public void run() + { + Packet p = null; + + do + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + p = collector.pollResult(); +// System.out.println(Thread.currentThread().getName() + " packet: " + p); + } + while (p != null); + } + }); + consumer3.setName("consumer 3"); + + consumer1.start(); + consumer2.start(); + consumer3.start(); + + for(int i=0; i" + getPacketID() + ""; + } + } +} diff --git a/core/src/test/java/org/jivesoftware/smack/filters/FromMatchesFilterTest.java b/core/src/test/java/org/jivesoftware/smack/filters/FromMatchesFilterTest.java index e913c588c..7e423acf2 100644 --- a/core/src/test/java/org/jivesoftware/smack/filters/FromMatchesFilterTest.java +++ b/core/src/test/java/org/jivesoftware/smack/filters/FromMatchesFilterTest.java @@ -14,100 +14,100 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.filters; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; - -import org.jivesoftware.smack.filter.FromMatchesFilter; -import org.jivesoftware.smack.packet.Packet; -import org.junit.Test; +package org.jivesoftware.smack.filters; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import org.jivesoftware.smack.filter.FromMatchesFilter; +import org.jivesoftware.smack.packet.Packet; +import org.junit.Test; /** * * @author Robin Collier * - */ -public class FromMatchesFilterTest { - private static final String BASE_JID1 = "ss@muc.myserver.com"; - private static final String FULL_JID1_R1 = BASE_JID1 + "/resource"; - private static final String FULL_JID1_R2 = BASE_JID1 + "/resource2"; - private static final String BASE_JID2 = "sss@muc.myserver.com"; - private static final String FULL_JID2 = BASE_JID2 + "/resource"; - - private static final String SERVICE_JID1 = "muc.myserver.com"; - private static final String SERVICE_JID2 = "pubsub.myserver.com"; - - @Test - public void compareMatchingFullJid() - { - FromMatchesFilter filter = new FromMatchesFilter(FULL_JID1_R1); - Packet packet = new Packet() { - @Override - public String toXML() { return null; } - }; - - packet.setFrom(FULL_JID1_R1); - assertTrue(filter.accept(packet)); - - packet.setFrom(BASE_JID1); - assertFalse(filter.accept(packet)); - - packet.setFrom(FULL_JID1_R2); - assertFalse(filter.accept(packet)); - - packet.setFrom(BASE_JID2); - assertFalse(filter.accept(packet)); - - packet.setFrom(FULL_JID2); - assertFalse(filter.accept(packet)); - } - - @Test - public void compareMatchingBaseJid() - { - FromMatchesFilter filter = new FromMatchesFilter(BASE_JID1); - Packet packet = new Packet() { - @Override - public String toXML() { return null; } - }; - - packet.setFrom(BASE_JID1); - assertTrue(filter.accept(packet)); - - packet.setFrom(FULL_JID1_R1); - assertTrue(filter.accept(packet)); - - packet.setFrom(FULL_JID1_R2); - assertTrue(filter.accept(packet)); - - packet.setFrom(BASE_JID2); - assertFalse(filter.accept(packet)); - - packet.setFrom(FULL_JID2); - assertFalse(filter.accept(packet)); - } - - @Test - public void compareMatchingServiceJid() - { - FromMatchesFilter filter = new FromMatchesFilter(SERVICE_JID1); - Packet packet = new Packet() { - @Override - public String toXML() { return null; } - }; - - packet.setFrom(SERVICE_JID1); - assertTrue(filter.accept(packet)); - - packet.setFrom(SERVICE_JID2); - assertFalse(filter.accept(packet)); - - packet.setFrom(BASE_JID1); - assertFalse(filter.accept(packet)); - - packet.setFrom(FULL_JID1_R1); - assertFalse(filter.accept(packet)); - - } -} + */ +public class FromMatchesFilterTest { + private static final String BASE_JID1 = "ss@muc.myserver.com"; + private static final String FULL_JID1_R1 = BASE_JID1 + "/resource"; + private static final String FULL_JID1_R2 = BASE_JID1 + "/resource2"; + private static final String BASE_JID2 = "sss@muc.myserver.com"; + private static final String FULL_JID2 = BASE_JID2 + "/resource"; + + private static final String SERVICE_JID1 = "muc.myserver.com"; + private static final String SERVICE_JID2 = "pubsub.myserver.com"; + + @Test + public void compareMatchingFullJid() + { + FromMatchesFilter filter = new FromMatchesFilter(FULL_JID1_R1); + Packet packet = new Packet() { + @Override + public String toXML() { return null; } + }; + + packet.setFrom(FULL_JID1_R1); + assertTrue(filter.accept(packet)); + + packet.setFrom(BASE_JID1); + assertFalse(filter.accept(packet)); + + packet.setFrom(FULL_JID1_R2); + assertFalse(filter.accept(packet)); + + packet.setFrom(BASE_JID2); + assertFalse(filter.accept(packet)); + + packet.setFrom(FULL_JID2); + assertFalse(filter.accept(packet)); + } + + @Test + public void compareMatchingBaseJid() + { + FromMatchesFilter filter = new FromMatchesFilter(BASE_JID1); + Packet packet = new Packet() { + @Override + public String toXML() { return null; } + }; + + packet.setFrom(BASE_JID1); + assertTrue(filter.accept(packet)); + + packet.setFrom(FULL_JID1_R1); + assertTrue(filter.accept(packet)); + + packet.setFrom(FULL_JID1_R2); + assertTrue(filter.accept(packet)); + + packet.setFrom(BASE_JID2); + assertFalse(filter.accept(packet)); + + packet.setFrom(FULL_JID2); + assertFalse(filter.accept(packet)); + } + + @Test + public void compareMatchingServiceJid() + { + FromMatchesFilter filter = new FromMatchesFilter(SERVICE_JID1); + Packet packet = new Packet() { + @Override + public String toXML() { return null; } + }; + + packet.setFrom(SERVICE_JID1); + assertTrue(filter.accept(packet)); + + packet.setFrom(SERVICE_JID2); + assertFalse(filter.accept(packet)); + + packet.setFrom(BASE_JID1); + assertFalse(filter.accept(packet)); + + packet.setFrom(FULL_JID1_R1); + assertFalse(filter.accept(packet)); + + } +} diff --git a/core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java b/core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java index eaaae3bda..7858d32e8 100644 --- a/core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java +++ b/core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java @@ -1,197 +1,197 @@ -/** - * - * Copyright (C) 2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.packet; - -import org.junit.Test; -import static org.junit.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; -import org.xml.sax.SAXException; -import static junit.framework.Assert.assertEquals; - -import java.io.IOException; - -import javax.xml.parsers.ParserConfigurationException; - -/** - * - */ -public class PresenceTest { - @Test - public void setPresenceTypeTest() throws IOException, SAXException, ParserConfigurationException { - Presence.Type type = Presence.Type.unavailable; - Presence.Type type2 = Presence.Type.subscribe; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append(""); - String control = controlBuilder.toString(); - - Presence presenceTypeInConstructor = new Presence(type); - presenceTypeInConstructor.setPacketID(Packet.ID_NOT_AVAILABLE); - assertEquals(type, presenceTypeInConstructor.getType()); - assertXMLEqual(control, presenceTypeInConstructor.toXML()); - - controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append(""); - control = controlBuilder.toString(); - - Presence presenceTypeSet = getNewPresence(); - presenceTypeSet.setType(type2); - assertEquals(type2, presenceTypeSet.getType()); - assertXMLEqual(control, presenceTypeSet.toXML()); - } - - @Test(expected=NullPointerException.class) - public void setNullPresenceTypeTest() { - getNewPresence().setType(null); - } - - @Test - public void isPresenceAvailableTest() { - Presence presence = getNewPresence(); - presence.setType(Presence.Type.available); - assertTrue(presence.isAvailable()); - - presence.setType(Presence.Type.unavailable); - assertFalse(presence.isAvailable()); - } - - @Test - public void setPresenceStatusTest() throws IOException, SAXException, ParserConfigurationException { - final String status = "This is a test of the emergency broadcast system."; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append("") - .append(status) - .append("") - .append(""); - String control = controlBuilder.toString(); - - Presence presence = getNewPresence(); - presence.setStatus(status); - - assertEquals(status, presence.getStatus()); - assertXMLEqual(control, presence.toXML()); - } - - @Test - public void setPresencePriorityTest() throws IOException, SAXException, ParserConfigurationException { - final int priority = 10; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append("") - .append(priority) - .append("") - .append(""); - String control = controlBuilder.toString(); - - Presence presence = getNewPresence(); - presence.setPriority(priority); - - assertEquals(priority, presence.getPriority()); - assertXMLEqual(control, presence.toXML()); - } - - @Test(expected=IllegalArgumentException.class) - public void setIllegalPriorityTest() { - getNewPresence().setPriority(Integer.MIN_VALUE); - } - - @Test - public void setPresenceModeTest() throws IOException, SAXException, ParserConfigurationException { - Presence.Mode mode1 = Presence.Mode.dnd; - final int priority = 10; - final String status = "This is a test of the emergency broadcast system."; - Presence.Mode mode2 = Presence.Mode.chat; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append("") - .append(status) - .append("") - .append("") - .append(priority) - .append("") - .append("") - .append(mode1) - .append("") - .append(""); - String control = controlBuilder.toString(); - - Presence presenceModeInConstructor = new Presence(Presence.Type.available, status, priority, - mode1); - presenceModeInConstructor.setPacketID(Packet.ID_NOT_AVAILABLE); - assertEquals(mode1, presenceModeInConstructor.getMode()); - assertXMLEqual(control, presenceModeInConstructor.toXML()); - - controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append("") - .append(mode2) - .append("") - .append(""); - control = controlBuilder.toString(); - - Presence presenceModeSet = getNewPresence(); - presenceModeSet.setMode(mode2); - assertEquals(mode2, presenceModeSet.getMode()); - assertXMLEqual(control, presenceModeSet.toXML()); - } - - @Test - public void isModeAwayTest() { - Presence presence = getNewPresence(); - presence.setMode(Presence.Mode.away); - assertTrue(presence.isAway()); - - presence.setMode(Presence.Mode.chat); - assertFalse(presence.isAway()); - } - - @Test - public void presenceXmlLangTest() throws IOException, SAXException, ParserConfigurationException { - final String lang = "sp"; - - StringBuilder controlBuilder = new StringBuilder(); - controlBuilder.append("") - .append(""); - String control = controlBuilder.toString(); - - Presence presence = getNewPresence(); - presence.setLanguage(lang); - - assertXMLEqual(control, presence.toXML()); - } - - private static Presence getNewPresence() { - Presence presence = new Presence(Presence.Type.available); - presence.setPacketID(Packet.ID_NOT_AVAILABLE); - return presence; - } -} +/** + * + * Copyright (C) 2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.packet; + +import org.junit.Test; +import static org.junit.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.xml.sax.SAXException; +import static junit.framework.Assert.assertEquals; + +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; + +/** + * + */ +public class PresenceTest { + @Test + public void setPresenceTypeTest() throws IOException, SAXException, ParserConfigurationException { + Presence.Type type = Presence.Type.unavailable; + Presence.Type type2 = Presence.Type.subscribe; + + StringBuilder controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append(""); + String control = controlBuilder.toString(); + + Presence presenceTypeInConstructor = new Presence(type); + presenceTypeInConstructor.setPacketID(Packet.ID_NOT_AVAILABLE); + assertEquals(type, presenceTypeInConstructor.getType()); + assertXMLEqual(control, presenceTypeInConstructor.toXML()); + + controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append(""); + control = controlBuilder.toString(); + + Presence presenceTypeSet = getNewPresence(); + presenceTypeSet.setType(type2); + assertEquals(type2, presenceTypeSet.getType()); + assertXMLEqual(control, presenceTypeSet.toXML()); + } + + @Test(expected=NullPointerException.class) + public void setNullPresenceTypeTest() { + getNewPresence().setType(null); + } + + @Test + public void isPresenceAvailableTest() { + Presence presence = getNewPresence(); + presence.setType(Presence.Type.available); + assertTrue(presence.isAvailable()); + + presence.setType(Presence.Type.unavailable); + assertFalse(presence.isAvailable()); + } + + @Test + public void setPresenceStatusTest() throws IOException, SAXException, ParserConfigurationException { + final String status = "This is a test of the emergency broadcast system."; + + StringBuilder controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append("") + .append(status) + .append("") + .append(""); + String control = controlBuilder.toString(); + + Presence presence = getNewPresence(); + presence.setStatus(status); + + assertEquals(status, presence.getStatus()); + assertXMLEqual(control, presence.toXML()); + } + + @Test + public void setPresencePriorityTest() throws IOException, SAXException, ParserConfigurationException { + final int priority = 10; + + StringBuilder controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append("") + .append(priority) + .append("") + .append(""); + String control = controlBuilder.toString(); + + Presence presence = getNewPresence(); + presence.setPriority(priority); + + assertEquals(priority, presence.getPriority()); + assertXMLEqual(control, presence.toXML()); + } + + @Test(expected=IllegalArgumentException.class) + public void setIllegalPriorityTest() { + getNewPresence().setPriority(Integer.MIN_VALUE); + } + + @Test + public void setPresenceModeTest() throws IOException, SAXException, ParserConfigurationException { + Presence.Mode mode1 = Presence.Mode.dnd; + final int priority = 10; + final String status = "This is a test of the emergency broadcast system."; + Presence.Mode mode2 = Presence.Mode.chat; + + StringBuilder controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append("") + .append(status) + .append("") + .append("") + .append(priority) + .append("") + .append("") + .append(mode1) + .append("") + .append(""); + String control = controlBuilder.toString(); + + Presence presenceModeInConstructor = new Presence(Presence.Type.available, status, priority, + mode1); + presenceModeInConstructor.setPacketID(Packet.ID_NOT_AVAILABLE); + assertEquals(mode1, presenceModeInConstructor.getMode()); + assertXMLEqual(control, presenceModeInConstructor.toXML()); + + controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append("") + .append(mode2) + .append("") + .append(""); + control = controlBuilder.toString(); + + Presence presenceModeSet = getNewPresence(); + presenceModeSet.setMode(mode2); + assertEquals(mode2, presenceModeSet.getMode()); + assertXMLEqual(control, presenceModeSet.toXML()); + } + + @Test + public void isModeAwayTest() { + Presence presence = getNewPresence(); + presence.setMode(Presence.Mode.away); + assertTrue(presence.isAway()); + + presence.setMode(Presence.Mode.chat); + assertFalse(presence.isAway()); + } + + @Test + public void presenceXmlLangTest() throws IOException, SAXException, ParserConfigurationException { + final String lang = "sp"; + + StringBuilder controlBuilder = new StringBuilder(); + controlBuilder.append("") + .append(""); + String control = controlBuilder.toString(); + + Presence presence = getNewPresence(); + presence.setLanguage(lang); + + assertXMLEqual(control, presence.toXML()); + } + + private static Presence getNewPresence() { + Presence presence = new Presence(Presence.Type.available); + presence.setPacketID(Packet.ID_NOT_AVAILABLE); + return presence; + } +} diff --git a/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java b/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java index b654b380f..c71b7752f 100644 --- a/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java +++ b/experimental/src/main/java/org/jivesoftware/smackx/ExperimentalProviderInitializer.java @@ -1,33 +1,33 @@ -/** - * - * Copyright 2013 Robin Collier - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; - -/** - * Initializes the providers in the experimental code stream. - * - * @author Robin Collier - * - */ -public class ExperimentalProviderInitializer extends UrlProviderFileInitializer { - - @Override - protected String getFilePath() { - return "classpath:org.jivesoftware.smackx/experimental.providers"; - } -} +/** + * + * Copyright 2013 Robin Collier + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; + +/** + * Initializes the providers in the experimental code stream. + * + * @author Robin Collier + * + */ +public class ExperimentalProviderInitializer extends UrlProviderFileInitializer { + + @Override + protected String getFilePath() { + return "classpath:org.jivesoftware.smackx/experimental.providers"; + } +} diff --git a/experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java b/experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java index bd704c881..932555d4b 100644 --- a/experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java +++ b/experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java @@ -1,218 +1,218 @@ -/** - * - * Copyright 2013 Georg Lukas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.carbons; - -import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.PacketCollector; -import org.jivesoftware.smack.PacketListener; -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.smack.packet.Message; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smackx.carbons.packet.CarbonExtension; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; - -/** - * Packet extension for XEP-0280: Message Carbons. This class implements - * the manager for registering {@link CarbonExtension} support, enabling and disabling - * message carbons. - * - * You should call enableCarbons() before sending your first undirected - * presence. - * - * @author Georg Lukas - */ -public class CarbonManager { - - private static Map instances = - Collections.synchronizedMap(new WeakHashMap()); - - static { - Connection.addConnectionCreationListener(new ConnectionCreationListener() { - public void connectionCreated(Connection connection) { - getInstanceFor(connection); - } - }); - } - - private WeakReference weakRefConnection; - private volatile boolean enabled_state = false; - - private CarbonManager(Connection connection) { - ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); - sdm.addFeature(CarbonExtension.NAMESPACE); - weakRefConnection = new WeakReference(connection); - instances.put(connection, this); - } - - /** - * Obtain the CarbonManager responsible for a connection. - * - * @param connection the connection object. - * - * @return a CarbonManager instance - */ - public static synchronized CarbonManager getInstanceFor(Connection connection) { - CarbonManager carbonManager = instances.get(connection); - - if (carbonManager == null) { - carbonManager = new CarbonManager(connection); - } - - return carbonManager; - } - - private IQ carbonsEnabledIQ(final boolean new_state) { - IQ setIQ = new IQ() { - public String getChildElementXML() { - return "<" + (new_state? "enable" : "disable") + " xmlns='" + CarbonExtension.NAMESPACE + "'/>"; - } - }; - setIQ.setType(IQ.Type.SET); - return setIQ; - } - - /** - * Returns true if XMPP Carbons are supported by the server. - * - * @return true if supported - */ - public boolean isSupportedByServer() { - Connection connection = weakRefConnection.get(); - try { - DiscoverInfo result = ServiceDiscoveryManager - .getInstanceFor(connection).discoverInfo(connection.getServiceName()); - return result.containsFeature(CarbonExtension.NAMESPACE); - } - catch (XMPPException e) { - return false; - } - } - - /** - * Notify server to change the carbons state. This method returns - * immediately and changes the variable when the reply arrives. - * - * You should first check for support using isSupportedByServer(). - * - * @param new_state whether carbons should be enabled or disabled - */ - public void sendCarbonsEnabled(final boolean new_state) { - final Connection connection = weakRefConnection.get(); - IQ setIQ = carbonsEnabledIQ(new_state); - - connection.addPacketListener(new PacketListener() { - public void processPacket(Packet packet) { - IQ result = (IQ)packet; - if (result.getType() == IQ.Type.RESULT) { - enabled_state = new_state; - } - connection.removePacketListener(this); - } - }, new PacketIDFilter(setIQ.getPacketID())); - - connection.sendPacket(setIQ); - } - - /** - * Notify server to change the carbons state. This method blocks - * some time until the server replies to the IQ and returns true on - * success. - * - * You should first check for support using isSupportedByServer(). - * - * @param new_state whether carbons should be enabled or disabled - * - * @return true if the operation was successful - */ - public boolean setCarbonsEnabled(final boolean new_state) { - if (enabled_state == new_state) - return true; - - Connection connection = weakRefConnection.get(); - IQ setIQ = carbonsEnabledIQ(new_state); - - PacketCollector collector = - connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID())); - connection.sendPacket(setIQ); - IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); - collector.cancel(); - - if (result != null && result.getType() == IQ.Type.RESULT) { - enabled_state = new_state; - return true; - } - return false; - } - - /** - * Helper method to enable carbons. - * - * @return true if the operation was successful - */ - public boolean enableCarbons() { - return setCarbonsEnabled(true); - } - - /** - * Helper method to disable carbons. - * - * @return true if the operation was successful - */ - public boolean disableCarbons() { - return setCarbonsEnabled(false); - } - - /** - * Check if carbons are enabled on this connection. - */ - public boolean getCarbonsEnabled() { - return this.enabled_state; - } - - /** - * Obtain a Carbon from a message, if available. - * - * @param msg Message object to check for carbons - * - * @return a Carbon if available, null otherwise. - */ - public static CarbonExtension getCarbon(Message msg) { - CarbonExtension cc = (CarbonExtension)msg.getExtension("received", CarbonExtension.NAMESPACE); - if (cc == null) - cc = (CarbonExtension)msg.getExtension("sent", CarbonExtension.NAMESPACE); - return cc; - } - - /** - * Mark a message as "private", so it will not be carbon-copied. - * - * @param msg Message object to mark private - */ - public static void disableCarbons(Message msg) { - msg.addExtension(new CarbonExtension.Private()); - } -} +/** + * + * Copyright 2013 Georg Lukas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.carbons; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +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.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the manager for registering {@link CarbonExtension} support, enabling and disabling + * message carbons. + * + * You should call enableCarbons() before sending your first undirected + * presence. + * + * @author Georg Lukas + */ +public class CarbonManager { + + private static Map instances = + Collections.synchronizedMap(new WeakHashMap()); + + static { + Connection.addConnectionCreationListener(new ConnectionCreationListener() { + public void connectionCreated(Connection connection) { + getInstanceFor(connection); + } + }); + } + + private WeakReference weakRefConnection; + private volatile boolean enabled_state = false; + + private CarbonManager(Connection connection) { + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + sdm.addFeature(CarbonExtension.NAMESPACE); + weakRefConnection = new WeakReference(connection); + instances.put(connection, this); + } + + /** + * Obtain the CarbonManager responsible for a connection. + * + * @param connection the connection object. + * + * @return a CarbonManager instance + */ + public static synchronized CarbonManager getInstanceFor(Connection connection) { + CarbonManager carbonManager = instances.get(connection); + + if (carbonManager == null) { + carbonManager = new CarbonManager(connection); + } + + return carbonManager; + } + + private IQ carbonsEnabledIQ(final boolean new_state) { + IQ setIQ = new IQ() { + public String getChildElementXML() { + return "<" + (new_state? "enable" : "disable") + " xmlns='" + CarbonExtension.NAMESPACE + "'/>"; + } + }; + setIQ.setType(IQ.Type.SET); + return setIQ; + } + + /** + * Returns true if XMPP Carbons are supported by the server. + * + * @return true if supported + */ + public boolean isSupportedByServer() { + Connection connection = weakRefConnection.get(); + try { + DiscoverInfo result = ServiceDiscoveryManager + .getInstanceFor(connection).discoverInfo(connection.getServiceName()); + return result.containsFeature(CarbonExtension.NAMESPACE); + } + catch (XMPPException e) { + return false; + } + } + + /** + * Notify server to change the carbons state. This method returns + * immediately and changes the variable when the reply arrives. + * + * You should first check for support using isSupportedByServer(). + * + * @param new_state whether carbons should be enabled or disabled + */ + public void sendCarbonsEnabled(final boolean new_state) { + final Connection connection = weakRefConnection.get(); + IQ setIQ = carbonsEnabledIQ(new_state); + + connection.addPacketListener(new PacketListener() { + public void processPacket(Packet packet) { + IQ result = (IQ)packet; + if (result.getType() == IQ.Type.RESULT) { + enabled_state = new_state; + } + connection.removePacketListener(this); + } + }, new PacketIDFilter(setIQ.getPacketID())); + + connection.sendPacket(setIQ); + } + + /** + * Notify server to change the carbons state. This method blocks + * some time until the server replies to the IQ and returns true on + * success. + * + * You should first check for support using isSupportedByServer(). + * + * @param new_state whether carbons should be enabled or disabled + * + * @return true if the operation was successful + */ + public boolean setCarbonsEnabled(final boolean new_state) { + if (enabled_state == new_state) + return true; + + Connection connection = weakRefConnection.get(); + IQ setIQ = carbonsEnabledIQ(new_state); + + PacketCollector collector = + connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID())); + connection.sendPacket(setIQ); + IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + collector.cancel(); + + if (result != null && result.getType() == IQ.Type.RESULT) { + enabled_state = new_state; + return true; + } + return false; + } + + /** + * Helper method to enable carbons. + * + * @return true if the operation was successful + */ + public boolean enableCarbons() { + return setCarbonsEnabled(true); + } + + /** + * Helper method to disable carbons. + * + * @return true if the operation was successful + */ + public boolean disableCarbons() { + return setCarbonsEnabled(false); + } + + /** + * Check if carbons are enabled on this connection. + */ + public boolean getCarbonsEnabled() { + return this.enabled_state; + } + + /** + * Obtain a Carbon from a message, if available. + * + * @param msg Message object to check for carbons + * + * @return a Carbon if available, null otherwise. + */ + public static CarbonExtension getCarbon(Message msg) { + CarbonExtension cc = (CarbonExtension)msg.getExtension("received", CarbonExtension.NAMESPACE); + if (cc == null) + cc = (CarbonExtension)msg.getExtension("sent", CarbonExtension.NAMESPACE); + return cc; + } + + /** + * Mark a message as "private", so it will not be carbon-copied. + * + * @param msg Message object to mark private + */ + public static void disableCarbons(Message msg) { + msg.addExtension(new CarbonExtension.Private()); + } +} diff --git a/experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java b/experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java index fc4c099e0..c20fe4ad8 100644 --- a/experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java +++ b/experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java @@ -1,117 +1,117 @@ -/** - * - * Copyright 2013 Georg Lukas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.carbons.packet; - -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smackx.forward.Forwarded; - -/** - * Packet extension for XEP-0280: Message Carbons. The extension - * XEP-0280 is - * meant to synchronize a message flow to multiple presences of a user. - * - *

    - * It accomplishes this by wrapping a {@link Forwarded} packet in a sent - * or received element - * - * @author Georg Lukas - */ -public class CarbonExtension implements PacketExtension { - public static final String NAMESPACE = "urn:xmpp:carbons:2"; - - private Direction dir; - private Forwarded fwd; - - /** - * Construct a Carbon message extension. - * - * @param dir Determines if the carbon is being sent/received - * @param fwd The forwarded message. - */ - public CarbonExtension(Direction dir, Forwarded fwd) { - this.dir = dir; - this.fwd = fwd; - } - - /** - * Get the direction (sent or received) of the carbon. - * - * @return the {@link Direction} of the carbon. - */ - public Direction getDirection() { - return dir; - } - - /** - * Get the forwarded packet. - * - * @return the {@link Forwarded} message contained in this Carbon. - */ - public Forwarded getForwarded() { - return fwd; - } - - @Override - public String getElementName() { - return dir.toString(); - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - @Override - public String toXML() { - StringBuilder buf = new StringBuilder(); - buf.append("<").append(getElementName()).append(" xmlns=\"") - .append(getNamespace()).append("\">"); - - buf.append(fwd.toXML()); - - buf.append(""); - return buf.toString(); - } - - /** - * Defines the direction of a {@link CarbonExtension} message. - */ - public static enum Direction { - received, - sent - } - - /** - * Packet extension indicating that a message may not be carbon-copied. Adding this - * extension to any message will disallow that message from being copied. - */ - public static class Private implements PacketExtension { - public static final String ELEMENT = "private"; - - public String getElementName() { - return ELEMENT; - } - - public String getNamespace() { - return CarbonExtension.NAMESPACE; - } - - public String toXML() { - return "<" + ELEMENT + " xmlns=\"" + CarbonExtension.NAMESPACE + "\"/>"; - } - } -} +/** + * + * Copyright 2013 Georg Lukas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.carbons.packet; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.forward.Forwarded; + +/** + * Packet extension for XEP-0280: Message Carbons. The extension + * XEP-0280 is + * meant to synchronize a message flow to multiple presences of a user. + * + *

    + * It accomplishes this by wrapping a {@link Forwarded} packet in a sent + * or received element + * + * @author Georg Lukas + */ +public class CarbonExtension implements PacketExtension { + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + + private Direction dir; + private Forwarded fwd; + + /** + * Construct a Carbon message extension. + * + * @param dir Determines if the carbon is being sent/received + * @param fwd The forwarded message. + */ + public CarbonExtension(Direction dir, Forwarded fwd) { + this.dir = dir; + this.fwd = fwd; + } + + /** + * Get the direction (sent or received) of the carbon. + * + * @return the {@link Direction} of the carbon. + */ + public Direction getDirection() { + return dir; + } + + /** + * Get the forwarded packet. + * + * @return the {@link Forwarded} message contained in this Carbon. + */ + public Forwarded getForwarded() { + return fwd; + } + + @Override + public String getElementName() { + return dir.toString(); + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"") + .append(getNamespace()).append("\">"); + + buf.append(fwd.toXML()); + + buf.append(""); + return buf.toString(); + } + + /** + * Defines the direction of a {@link CarbonExtension} message. + */ + public static enum Direction { + received, + sent + } + + /** + * Packet extension indicating that a message may not be carbon-copied. Adding this + * extension to any message will disallow that message from being copied. + */ + public static class Private implements PacketExtension { + public static final String ELEMENT = "private"; + + public String getElementName() { + return ELEMENT; + } + + public String getNamespace() { + return CarbonExtension.NAMESPACE; + } + + public String toXML() { + return "<" + ELEMENT + " xmlns=\"" + CarbonExtension.NAMESPACE + "\"/>"; + } + } +} diff --git a/experimental/src/main/java/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java b/experimental/src/main/java/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java index 128a61343..dace0e6dc 100644 --- a/experimental/src/main/java/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java +++ b/experimental/src/main/java/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java @@ -1,53 +1,53 @@ -/** - * - * Copyright 2013 Georg Lukas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.carbons.provider; - -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.provider.PacketExtensionProvider; -import org.jivesoftware.smack.util.PacketParserUtils; -import org.jivesoftware.smackx.carbons.packet.CarbonExtension; -import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction; -import org.jivesoftware.smackx.forward.Forwarded; -import org.xmlpull.v1.XmlPullParser; - -/** - * This class implements the {@link PacketExtensionProvider} to parse - * cabon copied messages from a packet. It will return a {@link CarbonExtension} packet extension. - * - * @author Georg Lukas - * - */ -public class CarbonManagerProvider implements PacketExtensionProvider { - - public PacketExtension parseExtension(XmlPullParser parser) throws Exception { - Direction dir = Direction.valueOf(parser.getName()); - Forwarded fwd = null; - - boolean done = false; - while (!done) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG && parser.getName().equals("forwarded")) { - fwd = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser); - } - else if (eventType == XmlPullParser.END_TAG && dir == Direction.valueOf(parser.getName())) - done = true; - } - if (fwd == null) - throw new Exception("sent/received must contain exactly one tag"); - return new CarbonExtension(dir, fwd); - } -} +/** + * + * Copyright 2013 Georg Lukas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.carbons.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction; +import org.jivesoftware.smackx.forward.Forwarded; +import org.xmlpull.v1.XmlPullParser; + +/** + * This class implements the {@link PacketExtensionProvider} to parse + * cabon copied messages from a packet. It will return a {@link CarbonExtension} packet extension. + * + * @author Georg Lukas + * + */ +public class CarbonManagerProvider implements PacketExtensionProvider { + + public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + Direction dir = Direction.valueOf(parser.getName()); + Forwarded fwd = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG && parser.getName().equals("forwarded")) { + fwd = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser); + } + else if (eventType == XmlPullParser.END_TAG && dir == Direction.valueOf(parser.getName())) + done = true; + } + if (fwd == null) + throw new Exception("sent/received must contain exactly one tag"); + return new CarbonExtension(dir, fwd); + } +} diff --git a/experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers b/experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers index 5acbc0018..e4ff30945 100644 --- a/experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers +++ b/experimental/src/main/resources/org.jivesoftware.smackx/experimental.providers @@ -1,17 +1,17 @@ - - - - - - - sent - urn:xmpp:carbons:2 - org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider - - - received - urn:xmpp:carbons:2 - org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider - - - + + + + + + + sent + urn:xmpp:carbons:2 + org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider + + + received + urn:xmpp:carbons:2 + org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider + + + diff --git a/experimental/src/test/java/org/jivesoftware/smackx/carbons/CarbonTest.java b/experimental/src/test/java/org/jivesoftware/smackx/carbons/CarbonTest.java index 31cc775f8..ba08a7343 100644 --- a/experimental/src/test/java/org/jivesoftware/smackx/carbons/CarbonTest.java +++ b/experimental/src/test/java/org/jivesoftware/smackx/carbons/CarbonTest.java @@ -1,114 +1,114 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.carbons; - -import static org.junit.Assert.assertEquals; - -import java.util.Properties; - -import org.jivesoftware.smack.provider.ProviderManager; -import org.jivesoftware.smack.test.util.TestUtils; -import org.jivesoftware.smackx.carbons.packet.CarbonExtension; -import org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider; -import org.jivesoftware.smackx.forward.Forwarded; -import org.jivesoftware.smackx.forward.provider.ForwardedProvider; -import org.junit.BeforeClass; -import org.junit.Test; -import org.xmlpull.v1.XmlPullParser; - -import com.jamesmurty.utils.XMLBuilder; - -public class CarbonTest { - - private static Properties outputProperties = new Properties(); - static { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @BeforeClass - public static void setup() { - ProviderManager.getInstance().addExtensionProvider("forwarded", "urn:xmpp:forward:0", new ForwardedProvider()); - } - - @Test - public void carbonSentTest() throws Exception { - XmlPullParser parser; - String control; - CarbonExtension cc; - Forwarded fwd; - - control = XMLBuilder.create("sent") - .e("forwarded") - .a("xmlns", "urn:xmpp:forwarded:0") - .e("message") - .a("from", "romeo@montague.com") - .asString(outputProperties); - - parser = TestUtils.getParser(control, "sent"); - cc = (CarbonExtension) new CarbonManagerProvider().parseExtension(parser); - fwd = cc.getForwarded(); - - // meta - assertEquals(CarbonExtension.Direction.sent, cc.getDirection()); - - // no delay in packet - assertEquals(null, fwd.getDelayInfo()); - - // check message - assertEquals("romeo@montague.com", fwd.getForwardedPacket().getFrom()); - - // check end of tag - assertEquals(XmlPullParser.END_TAG, parser.getEventType()); - assertEquals("sent", parser.getName()); - } - - @Test - public void carbonReceivedTest() throws Exception { - XmlPullParser parser; - String control; - CarbonExtension cc; - - control = XMLBuilder.create("received") - .e("forwarded") - .a("xmlns", "urn:xmpp:forwarded:0") - .e("message") - .a("from", "romeo@montague.com") - .asString(outputProperties); - - parser = TestUtils.getParser(control, "received"); - cc = (CarbonExtension) new CarbonManagerProvider().parseExtension(parser); - - assertEquals(CarbonExtension.Direction.received, cc.getDirection()); - - // check end of tag - assertEquals(XmlPullParser.END_TAG, parser.getEventType()); - assertEquals("received", parser.getName()); - } - - @Test(expected=Exception.class) - public void carbonEmptyTest() throws Exception { - XmlPullParser parser; - String control; - - control = XMLBuilder.create("sent") - .a("xmlns", "urn:xmpp:forwarded:0") - .asString(outputProperties); - - parser = TestUtils.getParser(control, "sent"); - new CarbonManagerProvider().parseExtension(parser); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.carbons; + +import static org.junit.Assert.assertEquals; + +import java.util.Properties; + +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension; +import org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider; +import org.jivesoftware.smackx.forward.Forwarded; +import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +import com.jamesmurty.utils.XMLBuilder; + +public class CarbonTest { + + private static Properties outputProperties = new Properties(); + static { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @BeforeClass + public static void setup() { + ProviderManager.getInstance().addExtensionProvider("forwarded", "urn:xmpp:forward:0", new ForwardedProvider()); + } + + @Test + public void carbonSentTest() throws Exception { + XmlPullParser parser; + String control; + CarbonExtension cc; + Forwarded fwd; + + control = XMLBuilder.create("sent") + .e("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .e("message") + .a("from", "romeo@montague.com") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "sent"); + cc = (CarbonExtension) new CarbonManagerProvider().parseExtension(parser); + fwd = cc.getForwarded(); + + // meta + assertEquals(CarbonExtension.Direction.sent, cc.getDirection()); + + // no delay in packet + assertEquals(null, fwd.getDelayInfo()); + + // check message + assertEquals("romeo@montague.com", fwd.getForwardedPacket().getFrom()); + + // check end of tag + assertEquals(XmlPullParser.END_TAG, parser.getEventType()); + assertEquals("sent", parser.getName()); + } + + @Test + public void carbonReceivedTest() throws Exception { + XmlPullParser parser; + String control; + CarbonExtension cc; + + control = XMLBuilder.create("received") + .e("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .e("message") + .a("from", "romeo@montague.com") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "received"); + cc = (CarbonExtension) new CarbonManagerProvider().parseExtension(parser); + + assertEquals(CarbonExtension.Direction.received, cc.getDirection()); + + // check end of tag + assertEquals(XmlPullParser.END_TAG, parser.getEventType()); + assertEquals("received", parser.getName()); + } + + @Test(expected=Exception.class) + public void carbonEmptyTest() throws Exception { + XmlPullParser parser; + String control; + + control = XMLBuilder.create("sent") + .a("xmlns", "urn:xmpp:forwarded:0") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "sent"); + new CarbonManagerProvider().parseExtension(parser); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java index c079ab5ec..055e257f0 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/ExtensionsProviderInitializer.java @@ -1,32 +1,32 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; - -/** - * Loads the default provider file for the Smack extensions on initialization. - * - * @author Robin Collier - * - */ -public class ExtensionsProviderInitializer extends UrlProviderFileInitializer { - @Override - protected String getFilePath() { - return "classpath:org.jivesoftware.smackx/extensions.providers"; - } -} \ No newline at end of file +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; + +/** + * Loads the default provider file for the Smack extensions on initialization. + * + * @author Robin Collier + * + */ +public class ExtensionsProviderInitializer extends UrlProviderFileInitializer { + @Override + protected String getFilePath() { + return "classpath:org.jivesoftware.smackx/extensions.providers"; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/attention/packet/AttentionExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/attention/packet/AttentionExtension.java index c3fcc4967..ed3854b1d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/attention/packet/AttentionExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/attention/packet/AttentionExtension.java @@ -1,97 +1,97 @@ -/** - * - * Copyright 2003-2010 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.attention.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 XEP-0224: Attention - */ -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(); - } - } -} +/** + * + * Copyright 2003-2010 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.attention.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 XEP-0224: Attention + */ +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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamListener.java index 9f1115552..2bd5ce034 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamListener.java @@ -1,50 +1,50 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * BytestreamListener can be registered at the {@link Socks5BytestreamManager} or the - * {@link InBandBytestreamManager}. - *

    - * There are two ways to add this listener. See - * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and - * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for further - * details. - *

    - * {@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); - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * BytestreamListener can be registered at the {@link Socks5BytestreamManager} or the + * {@link InBandBytestreamManager}. + *

    + * There are two ways to add this listener. See + * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener)} and + * {@link BytestreamManager#addIncomingBytestreamListener(BytestreamListener, String)} for further + * details. + *

    + * {@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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamManager.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamManager.java index 8fc14f731..15a979c0d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamManager.java @@ -1,117 +1,117 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

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

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

    - * Use this method if you are awaiting an incoming bytestream request from a specific user. - *

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

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

    - * To establish a bytestream after negotiation the kind of data to be sent (e.g. file transfer) - * use {@link #establishSession(String, String)}. - *

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

    - * 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; - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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. + *

    + * 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. + *

    + * Use this method if you are awaiting an incoming bytestream request from a specific user. + *

    + * 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. + *

    + * 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. + *

    + * To establish a bytestream after negotiation the kind of data to be sent (e.g. file transfer) + * use {@link #establishSession(String, String)}. + *

    + * 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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java index 381e356fc..94f9280a3 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamRequest.java @@ -1,62 +1,62 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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(); - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamSession.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamSession.java index 799190415..4e57d257d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamSession.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/BytestreamSession.java @@ -1,84 +1,84 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

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

    - * 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; - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java index eebb66482..395e61dba 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListener.java @@ -1,78 +1,78 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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 <item-not-found/> 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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 <item-not-found/> 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java index c45813fbd..6b694d1ee 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/DataListener.java @@ -1,76 +1,76 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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 - * <item-not-found/> error is returned to the sender. - *

    - * 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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 + * <item-not-found/> error is returned to the sender. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java index 65006c839..c810e41bd 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamListener.java @@ -1,49 +1,49 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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); - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java index a9a04b875..21bca28d2 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java @@ -1,561 +1,561 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 XEP-0047. - *

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

    - * 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 Advanced Message - * Processing. To set the stanza that should be used invoke {@link #setStanza(StanzaType)}. - *

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

    - * If a session ID for the In-Band Bytestream was already negotiated (e.g. while negotiating a file - * transfer) invoke {@link #establishSession(String, String)}. - *

    - * 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)}. - *

    - * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming - * In-Band bytestream requests sent in the context of XEP-0096 file transfer. (See - * {@link FileTransferManager}) - *

    - * If no {@link InBandBytestreamListener}s are registered, all incoming In-Band bytestream requests - * will be rejected by returning a <not-acceptable/> 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(final Connection connection) { - // create the manager for this connection - InBandBytestreamManager.getByteStreamManager(connection); - - // register shutdown listener - connection.addConnectionListener(new AbstractConnectionListener() { - - @Override - public void connectionClosed() { - InBandBytestreamManager.getByteStreamManager(connection).disableService(); - } - - @Override +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 XEP-0047. + *

    + * 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. + *

    + * 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 Advanced Message + * Processing. To set the stanza that should be used invoke {@link #setStanza(StanzaType)}. + *

    + * 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. + *

    + * If a session ID for the In-Band Bytestream was already negotiated (e.g. while negotiating a file + * transfer) invoke {@link #establishSession(String, String)}. + *

    + * 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)}. + *

    + * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming + * In-Band bytestream requests sent in the context of XEP-0096 file transfer. (See + * {@link FileTransferManager}) + *

    + * If no {@link InBandBytestreamListener}s are registered, all incoming In-Band bytestream requests + * will be rejected by returning a <not-acceptable/> 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(final Connection connection) { + // create the manager for this connection + InBandBytestreamManager.getByteStreamManager(connection); + + // register shutdown listener + connection.addConnectionListener(new AbstractConnectionListener() { + + @Override + public void connectionClosed() { + InBandBytestreamManager.getByteStreamManager(connection).disableService(); + } + + @Override public void connectionClosedOnError(Exception e) { - InBandBytestreamManager.getByteStreamManager(connection).disableService(); - } - - @Override - public void reconnectionSuccessful() { - // re-create the manager for this connection - InBandBytestreamManager.getByteStreamManager(connection); - } - - }); - - } - }); - } - - /** - * 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 managers = new HashMap(); - - /* 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 userListeners = new ConcurrentHashMap(); - - /* - * list of listeners that respond to all In-Band Bytestream requests if there are no user - * specific listeners for that request - */ - private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); - - /* 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 sessions = new ConcurrentHashMap(); - - /* 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 ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); - - /** - * 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. - *

    - * If no listeners are registered all In-Band Bytestream request are rejected with a - * <not-acceptable/> error. - *

    - * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming - * Socks5 bytestream requests sent in the context of XEP-0096 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. - *

    - * Use this method if you are awaiting an incoming Socks5 bytestream request from a specific - * user. - *

    - * If no listeners are registered all In-Band Bytestream request are rejected with a - * <not-acceptable/> error. - *

    - * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming - * Socks5 bytestream requests sent in the context of XEP-0096 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. - *

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

    - * The recommended default block size is 4096 bytes. See XEP-0047 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. - *

    - * The default block size must be between 1 and 65535 bytes. The recommended default block size - * is 4096 bytes. See XEP-0047 - * 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. - *

    - * Incoming In-Band Bytestream open request will be rejected with an - * <resource-constraint/> error if the block size is greater then the maximum allowed - * block size. - *

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

    - * The maximum block size must be between 1 and 65535 bytes. - *

    - * Incoming In-Band Bytestream open request will be rejected with an - * <resource-constraint/> 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. - *

    - * Default is {@link StanzaType#IQ}. See XEP-0047 Section 4. - * - * @return the stanza used to send data packets - */ - public StanzaType getStanza() { - return stanza; - } - - /** - * Sets the stanza used to send data packets. - *

    - * The use of {@link StanzaType#IQ} is recommended. See XEP-0047 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. - *

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

    - * 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 getAllRequestListeners() { - return this.allRequestListeners; - } - - /** - * Returns the sessions map. - * - * @return the sessions map - */ - protected Map getSessions() { - return sessions; - } - - /** - * Returns the list of session IDs that should be ignored by the InitialtionListener - * - * @return list of session IDs - */ - protected List getIgnoredBytestreamRequests() { - return ignoredBytestreamRequests; - } - - /** - * Disables the InBandBytestreamManager by removing its packet listeners and resetting its - * internal status, which includes removing this instance from the managers map. - */ - 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(); - - } - -} + InBandBytestreamManager.getByteStreamManager(connection).disableService(); + } + + @Override + public void reconnectionSuccessful() { + // re-create the manager for this connection + InBandBytestreamManager.getByteStreamManager(connection); + } + + }); + + } + }); + } + + /** + * 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 managers = new HashMap(); + + /* 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 userListeners = new ConcurrentHashMap(); + + /* + * list of listeners that respond to all In-Band Bytestream requests if there are no user + * specific listeners for that request + */ + private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); + + /* 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 sessions = new ConcurrentHashMap(); + + /* 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 ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); + + /** + * 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. + *

    + * If no listeners are registered all In-Band Bytestream request are rejected with a + * <not-acceptable/> error. + *

    + * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming + * Socks5 bytestream requests sent in the context of XEP-0096 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. + *

    + * Use this method if you are awaiting an incoming Socks5 bytestream request from a specific + * user. + *

    + * If no listeners are registered all In-Band Bytestream request are rejected with a + * <not-acceptable/> error. + *

    + * Note that the registered {@link InBandBytestreamListener} will NOT be notified on incoming + * Socks5 bytestream requests sent in the context of XEP-0096 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. + *

    + * 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. + *

    + * The recommended default block size is 4096 bytes. See XEP-0047 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. + *

    + * The default block size must be between 1 and 65535 bytes. The recommended default block size + * is 4096 bytes. See XEP-0047 + * 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. + *

    + * Incoming In-Band Bytestream open request will be rejected with an + * <resource-constraint/> error if the block size is greater then the maximum allowed + * block size. + *

    + * 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. + *

    + * The maximum block size must be between 1 and 65535 bytes. + *

    + * Incoming In-Band Bytestream open request will be rejected with an + * <resource-constraint/> 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. + *

    + * Default is {@link StanzaType#IQ}. See XEP-0047 Section 4. + * + * @return the stanza used to send data packets + */ + public StanzaType getStanza() { + return stanza; + } + + /** + * Sets the stanza used to send data packets. + *

    + * The use of {@link StanzaType#IQ} is recommended. See XEP-0047 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. + *

    + * 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. + *

    + * 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 getAllRequestListeners() { + return this.allRequestListeners; + } + + /** + * Returns the sessions map. + * + * @return the sessions map + */ + protected Map getSessions() { + return sessions; + } + + /** + * Returns the list of session IDs that should be ignored by the InitialtionListener + * + * @return list of session IDs + */ + protected List getIgnoredBytestreamRequests() { + return ignoredBytestreamRequests; + } + + /** + * Disables the InBandBytestreamManager by removing its packet listeners and resetting its + * internal status, which includes removing this instance from the managers map. + */ + 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java index 7ed539c24..36f9415a5 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequest.java @@ -1,95 +1,95 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java index bdc18e6d0..482a0e37e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java @@ -1,798 +1,798 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * In-band bytestreams are bidirectional and this session encapsulates the streams for both - * directions. - *

    - * 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 false. - * - * @return true if both streams will be closed if one of the streams is closed, - * false 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 false. - * - * @param closeBothStreamsEnabled true if both streams should be closed if one of - * the streams is closed, false 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 dataQueue = new LinkedBlockingQueue(); - - /* 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 true if data was received, otherwise false - * @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()]; - } - - /** - * 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 true 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); - - } - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * In-band bytestreams are bidirectional and this session encapsulates the streams for both + * directions. + *

    + * 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 false. + * + * @return true if both streams will be closed if one of the streams is closed, + * false 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 false. + * + * @param closeBothStreamsEnabled true if both streams should be closed if one of + * the streams is closed, false 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 dataQueue = new LinkedBlockingQueue(); + + /* 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 true if data was received, otherwise false + * @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()]; + } + + /** + * 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 true 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java index 44464d688..82a63404f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java @@ -1,130 +1,130 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 <not-acceptable/> error (XEP-0047 Section 2.1). - *

    - * All In-Band Bytestream request having a block size greater than the maximum allowed block size - * for this connection are rejected with an <resource-constraint/> 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(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 <not-acceptable/> error (XEP-0047 Section 2.1). + *

    + * All In-Band Bytestream request having a block size greater than the maximum allowed block size + * for this connection are rejected with an <resource-constraint/> 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java index f9a768524..fb0933695 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java @@ -1,68 +1,68 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - return buf.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java index dac3aba2d..d9198ec4f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java @@ -1,67 +1,67 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * Convenience method for packet.getExtension("data", - * "http://jabber.org/protocol/ibb"). - * - * @return the data packet extension - */ - public DataPacketExtension getDataPacketExtension() { - return this.dataPacketExtension; - } - - public String getChildElementXML() { - return this.dataPacketExtension.toXML(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * Convenience method for packet.getExtension("data", + * "http://jabber.org/protocol/ibb"). + * + * @return the data packet extension + */ + public DataPacketExtension getDataPacketExtension() { + return this.dataPacketExtension; + } + + public String getChildElementXML() { + return this.dataPacketExtension.toXML(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java index 0d63512fd..ee3c4e49b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtension.java @@ -1,152 +1,152 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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 XEP-0047 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(""); - return buf.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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 XEP-0047 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(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java index b7303b75e..9f755a824 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java @@ -1,129 +1,129 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

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

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

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

    - * 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(""); - return buf.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java index 68095292f..280210b75 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/CloseIQProvider.java @@ -1,36 +1,36 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java index 1d7da00a1..4fc8fdde2 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/DataPacketProvider.java @@ -1,48 +1,48 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java index f512d2467..53964ca9b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProvider.java @@ -1,48 +1,48 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java index 7682717ea..0ad885fcf 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListener.java @@ -1,122 +1,122 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 <not-acceptable/> error (XEP-0065 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(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 <not-acceptable/> error (XEP-0065 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java index 84fab8e22..6ff919ee8 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamListener.java @@ -1,46 +1,46 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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); - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 190ec3d06..67210d6a1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -1,783 +1,783 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; -import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; -import org.jivesoftware.smackx.filetransfer.FileTransferManager; - -/** - * The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the XEP-0065. - *

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

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

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

    - * If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file - * transfer) invoke {@link #establishSession(String, String)}. - *

    - * 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)}. - *

    - * Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5 - * bytestream requests sent in the context of XEP-0096 file transfer. (See - * {@link FileTransferManager}) - *

    - * If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests - * will be rejected by returning a <not-acceptable/> 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(final Connection connection) { - // create the manager for this connection - Socks5BytestreamManager.getBytestreamManager(connection); - - // register shutdown listener - connection.addConnectionListener(new AbstractConnectionListener() { - - @Override - public void connectionClosed() { - Socks5BytestreamManager.getBytestreamManager(connection).disableService(); - } - - @Override - public void connectionClosedOnError(Exception e) { - Socks5BytestreamManager.getBytestreamManager(connection).disableService(); - } - - @Override - public void reconnectionSuccessful() { - // re-create the manager for this connection - Socks5BytestreamManager.getBytestreamManager(connection); - } - - }); - } - - }); - } - - /** - * 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 managers = new HashMap(); - - /* 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 userListeners = new ConcurrentHashMap(); - - /* - * list of listeners that respond to all bytestream requests if there are not user specific - * listeners for that request - */ - private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); - - /* 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 proxyBlacklist = Collections.synchronizedList(new LinkedList()); - - /* 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 ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); - - /** - * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given - * {@link Connection}. - *

    - * If no manager exists a new is created and initialized. - * - * @param connection the XMPP connection or null if given connection is - * null - * @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. - *

    - * If no listeners are registered all SOCKS5 Bytestream request are rejected with a - * <not-acceptable/> error. - *

    - * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5 - * bytestream requests sent in the context of XEP-0096 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. - *

    - * Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific - * user. - *

    - * If no listeners are registered all SOCKS5 Bytestream request are rejected with a - * <not-acceptable/> error. - *

    - * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5 - * bytestream requests sent in the context of XEP-0096 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. - *

    - * 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, which includes removing this instance from the managers map. - *

    - * 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 true. - * - * @return true if prioritization is enabled, false 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. - *

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

    - * 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 proxies = new ArrayList(); - // 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 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 true if the given target JID supports feature SOCKS5 Bytestream. - * - * @param targetJID the target JID - * @return true if the given target JID supports feature SOCKS5 Bytestream - * otherwise false - * @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 determineProxies() throws XMPPException { - ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection); - - List proxies = new ArrayList(); - - // get all items form XMPP server - DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName()); - Iterator 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 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 determineStreamHostInfos(List proxies) { - List streamHosts = new ArrayList(); - - // add local proxy on first position if exists - List 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 getLocalStreamHost() { - - // get local proxy singleton - Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); - - if (socks5Server.isRunning()) { - List addresses = socks5Server.getLocalAddresses(); - int port = socks5Server.getPort(); - - if (addresses.size() >= 1) { - List streamHosts = new ArrayList(); - 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 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 getAllRequestListeners() { - return this.allRequestListeners; - } - - /** - * Returns the list of session IDs that should be ignored by the InitialtionListener - * - * @return list of session IDs - */ - protected List getIgnoredBytestreamRequests() { - return ignoredBytestreamRequests; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; +import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; +import org.jivesoftware.smackx.filetransfer.FileTransferManager; + +/** + * The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the XEP-0065. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file + * transfer) invoke {@link #establishSession(String, String)}. + *

    + * 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)}. + *

    + * Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5 + * bytestream requests sent in the context of XEP-0096 file transfer. (See + * {@link FileTransferManager}) + *

    + * If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests + * will be rejected by returning a <not-acceptable/> 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(final Connection connection) { + // create the manager for this connection + Socks5BytestreamManager.getBytestreamManager(connection); + + // register shutdown listener + connection.addConnectionListener(new AbstractConnectionListener() { + + @Override + public void connectionClosed() { + Socks5BytestreamManager.getBytestreamManager(connection).disableService(); + } + + @Override + public void connectionClosedOnError(Exception e) { + Socks5BytestreamManager.getBytestreamManager(connection).disableService(); + } + + @Override + public void reconnectionSuccessful() { + // re-create the manager for this connection + Socks5BytestreamManager.getBytestreamManager(connection); + } + + }); + } + + }); + } + + /** + * 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 managers = new HashMap(); + + /* 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 userListeners = new ConcurrentHashMap(); + + /* + * list of listeners that respond to all bytestream requests if there are not user specific + * listeners for that request + */ + private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); + + /* 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 proxyBlacklist = Collections.synchronizedList(new LinkedList()); + + /* 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 ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); + + /** + * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given + * {@link Connection}. + *

    + * If no manager exists a new is created and initialized. + * + * @param connection the XMPP connection or null if given connection is + * null + * @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. + *

    + * If no listeners are registered all SOCKS5 Bytestream request are rejected with a + * <not-acceptable/> error. + *

    + * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5 + * bytestream requests sent in the context of XEP-0096 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. + *

    + * Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific + * user. + *

    + * If no listeners are registered all SOCKS5 Bytestream request are rejected with a + * <not-acceptable/> error. + *

    + * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5 + * bytestream requests sent in the context of XEP-0096 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. + *

    + * 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, which includes removing this instance from the managers map. + *

    + * 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 true. + * + * @return true if prioritization is enabled, false 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. + *

    + * 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. + *

    + * 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 proxies = new ArrayList(); + // 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 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 true if the given target JID supports feature SOCKS5 Bytestream. + * + * @param targetJID the target JID + * @return true if the given target JID supports feature SOCKS5 Bytestream + * otherwise false + * @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 determineProxies() throws XMPPException { + ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection); + + List proxies = new ArrayList(); + + // get all items form XMPP server + DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName()); + Iterator 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 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 determineStreamHostInfos(List proxies) { + List streamHosts = new ArrayList(); + + // add local proxy on first position if exists + List 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 getLocalStreamHost() { + + // get local proxy singleton + Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); + + if (socks5Server.isRunning()) { + List addresses = socks5Server.getLocalAddresses(); + int port = socks5Server.getPort(); + + if (addresses.size() >= 1) { + List streamHosts = new ArrayList(); + 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 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 getAllRequestListeners() { + return this.allRequestListeners; + } + + /** + * Returns the list of session IDs that should be ignored by the InitialtionListener + * + * @return list of session IDs + */ + protected List getIgnoredBytestreamRequests() { + return ignoredBytestreamRequests; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java index 0f01fd649..73dce1a78 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java @@ -1,319 +1,319 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 ADDRESS_BLACKLIST = new Cache( - 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. - *

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

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

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

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

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

    - * 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 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 ADDRESS_BLACKLIST = new Cache( + 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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. + *

    + * 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 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java index 52468c0e9..adb423c65 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java @@ -14,84 +14,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 true if the session is established through a direct connection between - * the initiator and target, false if the session is mediated over a SOCKS proxy. - * - * @return true if session is a direct connection, false if session is - * mediated over a SOCKS5 proxy - */ - public boolean isDirect() { - return this.isDirect; - } - - /** - * Returns true if the session is mediated over a SOCKS proxy, false - * if this session is established through a direct connection between the initiator and target. - * - * @return true if session is mediated over a SOCKS5 proxy, false 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 true if the session is established through a direct connection between + * the initiator and target, false if the session is mediated over a SOCKS proxy. + * + * @return true if session is a direct connection, false if session is + * mediated over a SOCKS5 proxy + */ + public boolean isDirect() { + return this.isDirect; + } + + /** + * Returns true if the session is mediated over a SOCKS proxy, false + * if this session is established through a direct connection between the initiator and target. + * + * @return true if session is mediated over a SOCKS5 proxy, false 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java index b805f89ba..5e538b68a 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java @@ -1,207 +1,207 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 futureTask = new FutureTask(new Callable() { - - 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. - *

    - * Returns true if a stream could be established, otherwise false. If - * false is returned the given Socket should be closed. - * - * @param socket connected to a SOCKS5 proxy - * @return true if if a stream could be established, otherwise false. - * If false 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. - *

    - * (see RFC1928) - * - * @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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 futureTask = new FutureTask(new Callable() { + + 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. + *

    + * Returns true if a stream could be established, otherwise false. If + * false is returned the given Socket should be closed. + * + * @param socket connected to a SOCKS5 proxy + * @return true if if a stream could be established, otherwise false. + * If false 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. + *

    + * (see RFC1928) + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java index 385ae5d8e..1359bd4e1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java @@ -1,120 +1,120 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java index bfc9d35ec..cbe4cc971 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java @@ -1,428 +1,428 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.logging.Level; -import java.util.logging.Logger; - -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 localSocks5ProxyEnabled flag in the smack-config.xml or by - * invoking {@link SmackConfiguration#setLocalSocks5ProxyEnabled(boolean)}. The proxy is enabled by - * default. - *

    - * The port of the local SOCKS5 proxy can be configured by setting localSocks5ProxyPort - * in the smack-config.xml 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. - *

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

    - * 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)}). - *

    - * This Implementation has the following limitations: - *

      - *
    • only supports the no-authentication authentication method
    • - *
    • only supports the connect command and will not answer correctly to other - * commands
    • - *
    • only supports requests with the domain address type and will not correctly answer to requests - * with other address types
    • - *
    - * (see RFC 1928) - * - * @author Henning Staib - */ -public class Socks5Proxy { - private static Logger log = Logger.getLogger(Socks5Proxy.class.getName()); - - /* 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 connectionMap = new ConcurrentHashMap(); - - /* list of digests connections should be stored */ - private final List allowedConnections = Collections.synchronizedList(new LinkedList()); - - private final Set localAddresses = Collections.synchronizedSet(new LinkedHashSet()); - - /** - * 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 - log.log(Level.SEVERE, "couldn't setup local SOCKS5 proxy on port " + SmackConfiguration.getLocalSocks5ProxyPort(), e); - } - } - - /** - * 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. - *

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

    - * The order of the addresses used is determined by the order you add addresses. - *

    - * Note that the list of addresses initially contains the address returned by - * InetAddress.getLocalHost().getHostAddress(). 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 getLocalAddresses() { - return Collections.unmodifiableList(new ArrayList(this.localAddresses)); - } - - /** - * Replaces the list of local network addresses. - *

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

    - * 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 true if the local SOCKS5 proxy server is running, otherwise - * false. - * - * @return true if the local SOCKS5 proxy server is running, otherwise - * false - */ - 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); - } - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.logging.Level; +import java.util.logging.Logger; + +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 localSocks5ProxyEnabled flag in the smack-config.xml or by + * invoking {@link SmackConfiguration#setLocalSocks5ProxyEnabled(boolean)}. The proxy is enabled by + * default. + *

    + * The port of the local SOCKS5 proxy can be configured by setting localSocks5ProxyPort + * in the smack-config.xml 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. + *

    + * 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. + *

    + * 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)}). + *

    + * This Implementation has the following limitations: + *

      + *
    • only supports the no-authentication authentication method
    • + *
    • only supports the connect command and will not answer correctly to other + * commands
    • + *
    • only supports requests with the domain address type and will not correctly answer to requests + * with other address types
    • + *
    + * (see RFC 1928) + * + * @author Henning Staib + */ +public class Socks5Proxy { + private static Logger log = Logger.getLogger(Socks5Proxy.class.getName()); + + /* 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 connectionMap = new ConcurrentHashMap(); + + /* list of digests connections should be stored */ + private final List allowedConnections = Collections.synchronizedList(new LinkedList()); + + private final Set localAddresses = Collections.synchronizedSet(new LinkedHashSet()); + + /** + * 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 + log.log(Level.SEVERE, "couldn't setup local SOCKS5 proxy on port " + SmackConfiguration.getLocalSocks5ProxyPort(), e); + } + } + + /** + * 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. + *

    + * 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. + *

    + * The order of the addresses used is determined by the order you add addresses. + *

    + * Note that the list of addresses initially contains the address returned by + * InetAddress.getLocalHost().getHostAddress(). 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 getLocalAddresses() { + return Collections.unmodifiableList(new ArrayList(this.localAddresses)); + } + + /** + * Replaces the list of local network addresses. + *

    + * 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 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. + *

    + * 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 true if the local SOCKS5 proxy server is running, otherwise + * false. + * + * @return true if the local SOCKS5 proxy server is running, otherwise + * false + */ + 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java index 819ce124e..9c7cc490c 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java @@ -1,76 +1,76 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 XEP-0065. - * - * @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. - *

    - * (see RFC1928) - * - * @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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 XEP-0065. + * + * @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. + *

    + * (see RFC1928) + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java index 8efba8bee..fba74539f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java @@ -1,477 +1,477 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 streamHosts = new ArrayList(); - - 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 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(""); - 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(""); - - 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(""); - 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; - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 streamHosts = new ArrayList(); + + 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 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(""); + 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(""); + + 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(""); + 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/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java index 8f44d45d1..92c700483 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/provider/BytestreamsProvider.java @@ -1,85 +1,85 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/chatstates/packet/ChatStateExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/chatstates/packet/ChatStateExtension.java index 4475a4de6..f7d96a427 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/chatstates/packet/ChatStateExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/chatstates/packet/ChatStateExtension.java @@ -1,70 +1,70 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.chatstates.packet; - -import org.jivesoftware.smackx.chatstates.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.chatstates.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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.chatstates.packet; + +import org.jivesoftware.smackx.chatstates.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.chatstates.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/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommand.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommand.java index dbc39f33c..5a48c9ed4 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommand.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommand.java @@ -1,447 +1,447 @@ -/** - * - * Copyright 2005-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.packet.AdHocCommandData; -import org.jivesoftware.smackx.xdata.Form; - -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 - * getForm method retrieves a form with all the users. - *

    - * Each command has a node that should be unique within a given JID. - *

    - * 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). - *

    - * All the actions may throw an XMPPException if there is a problem executing - * them. The XMPPError of that exception may have some specific - * information about the problem. The possible extensions are: - * - *

  • malformed-action. Extension of a bad-request error.
  • - *
  • bad-action. Extension of a bad-request error.
  • - *
  • bad-locale. Extension of a bad-request error.
  • - *
  • bad-payload. Extension of a bad-request error.
  • - *
  • bad-sessionid. Extension of a bad-request error.
  • - *
  • session-expired. Extension of a not-allowed error.
  • - *

    - * See the SpecificErrorCondition class for detailed description - * of each one. - *

    - * Use the getSpecificErrorConditionFrom to obtain the specific - * information from an XMPPError. - * - * @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 error or null 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 OwnerJID. - * - * @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 - * OwnerJID. - * - * @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 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 response. 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 - * response. 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 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 action is available in the current stage. - * The {@link Action#cancel cancel} action is always allowed. To define the - * available actions use the addActionAvailable 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 +/** + * + * Copyright 2005-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.packet.AdHocCommandData; +import org.jivesoftware.smackx.xdata.Form; + +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 + * getForm method retrieves a form with all the users. + *

    + * Each command has a node that should be unique within a given JID. + *

    + * 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). + *

    + * All the actions may throw an XMPPException if there is a problem executing + * them. The XMPPError of that exception may have some specific + * information about the problem. The possible extensions are: + * + *

  • malformed-action. Extension of a bad-request error.
  • + *
  • bad-action. Extension of a bad-request error.
  • + *
  • bad-locale. Extension of a bad-request error.
  • + *
  • bad-payload. Extension of a bad-request error.
  • + *
  • bad-sessionid. Extension of a bad-request error.
  • + *
  • session-expired. Extension of a not-allowed error.
  • + *

    + * See the SpecificErrorCondition class for detailed description + * of each one. + *

    + * Use the getSpecificErrorConditionFrom to obtain the specific + * information from an XMPPError. + * + * @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 error or null 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 OwnerJID. + * + * @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 + * OwnerJID. + * + * @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 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 response. 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 + * response. 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 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 action is available in the current stage. + * The {@link Action#cancel cancel} action is always allowed. To define the + * available actions use the addActionAvailable 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; + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java index a54e84b63..e9e07f5fc 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java @@ -1,703 +1,703 @@ -/** - * - * Copyright 2005-2008 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.AdHocCommand.Action; -import org.jivesoftware.smackx.commands.AdHocCommand.Status; -import org.jivesoftware.smackx.commands.packet.AdHocCommandData; -import org.jivesoftware.smackx.disco.NodeInformationProvider; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; -import org.jivesoftware.smackx.xdata.Form; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -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 instances = - Collections.synchronizedMap(new WeakHashMap()); - - /** - * 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) { - getAddHocCommandsManager(connection); - } - }); - } - - /** - * Returns the AdHocCommandManager related to the - * connection. - * - * @param connection the XMPP connection. - * @return the AdHocCommandManager associated with the connection. - */ - public static synchronized AdHocCommandManager getAddHocCommandsManager(Connection connection) { - AdHocCommandManager ahcm = instances.get(connection); - if (ahcm == null) ahcm = new AdHocCommandManager(connection); - return ahcm; - } - - /** - * The Connection that this instances of AdHocCommandManager manages - */ - private final WeakReference 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 final Map commands = new ConcurrentHashMap(); - - /** - * 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 final Map executingCommands = new ConcurrentHashMap(); - - private final ServiceDiscoveryManager serviceDiscoveryManager; - - /** - * Thread that reaps stale sessions. - */ - private Thread sessionsSweeper; - - private AdHocCommandManager(Connection connection) { - this.connection = new WeakReference(connection); - this.serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); - - // Register the new instance and associate it with the connection - instances.put(connection, this); - - // 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 getNodeItems() { - - List answer = new ArrayList(); - Collection 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 getNodeFeatures() { - return null; - } - - public List getNodeIdentities() { - return null; - } - - @Override - public List 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; - } - - /** - * Registers a new command with this command manager, which is related to a - * connection. The node is an unique identifier of that command for - * the connection related to this command manager. The name is the - * human readable name of the command. The class 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 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 node is an unique identifier of that - * command for the connection related to this command manager. The name - * is the human readeale name of the command. The factory 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.get().getUser(), factory); - - commands.put(node, commandInfo); - // Set the NodeInformationProvider that will provide information about - // the added command - serviceDiscoveryManager.setNodeInformationProvider(node, - new NodeInformationProvider() { - public List getNodeItems() { - return null; - } - - public List getNodeFeatures() { - List answer = new ArrayList(); - 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 getNodeIdentities() { - List answer = new ArrayList(); - DiscoverInfo.Identity identity = new DiscoverInfo.Identity( - "automation", name, "command-node"); - answer.add(identity); - return answer; - } - - @Override - public List getNodePacketExtensions() { - return null; - } - - }); - } - - /** - * Discover the commands of an specific JID. The jid 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 { - 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 { - // Collects the commands to publish as items - DiscoverItems discoverItems = new DiscoverItems(); - Collection 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.get(), node, jid); - } - - /** - * 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: - *

      - *
    • The requested command exists
    • - *
    • The requester has permissions to execute it
    • - *
    • The command has more than one stage, if so, it saves the command and - * session ID for further use
    • - *
    - * - *
    - *
    - * If this is not the first request, this method checks, before executing - * the command, if: - *
      - *
    • The session ID of the request was stored
    • - *
    • The session life do not exceed the time out
    • - *
    • The action to execute is one of the available actions
    • - *
    - * - * @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.get().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); - } - } - 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 required 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.get().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); - } - } - } - } - - /** - * 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.get().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) { - throw new XMPPException(new XMPPError( - XMPPError.Condition.interna_server_error)); - } - catch (IllegalAccessException e) { - 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 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 +/** + * + * Copyright 2005-2008 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.AdHocCommand.Action; +import org.jivesoftware.smackx.commands.AdHocCommand.Status; +import org.jivesoftware.smackx.commands.packet.AdHocCommandData; +import org.jivesoftware.smackx.disco.NodeInformationProvider; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; +import org.jivesoftware.smackx.xdata.Form; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +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 instances = + Collections.synchronizedMap(new WeakHashMap()); + + /** + * 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) { + getAddHocCommandsManager(connection); + } + }); + } + + /** + * Returns the AdHocCommandManager related to the + * connection. + * + * @param connection the XMPP connection. + * @return the AdHocCommandManager associated with the connection. + */ + public static synchronized AdHocCommandManager getAddHocCommandsManager(Connection connection) { + AdHocCommandManager ahcm = instances.get(connection); + if (ahcm == null) ahcm = new AdHocCommandManager(connection); + return ahcm; + } + + /** + * The Connection that this instances of AdHocCommandManager manages + */ + private final WeakReference 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 final Map commands = new ConcurrentHashMap(); + + /** + * 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 final Map executingCommands = new ConcurrentHashMap(); + + private final ServiceDiscoveryManager serviceDiscoveryManager; + + /** + * Thread that reaps stale sessions. + */ + private Thread sessionsSweeper; + + private AdHocCommandManager(Connection connection) { + this.connection = new WeakReference(connection); + this.serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); + + // Register the new instance and associate it with the connection + instances.put(connection, this); + + // 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 getNodeItems() { + + List answer = new ArrayList(); + Collection 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 getNodeFeatures() { + return null; + } + + public List getNodeIdentities() { + return null; + } + + @Override + public List 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; + } + + /** + * Registers a new command with this command manager, which is related to a + * connection. The node is an unique identifier of that command for + * the connection related to this command manager. The name is the + * human readable name of the command. The class 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 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 node is an unique identifier of that + * command for the connection related to this command manager. The name + * is the human readeale name of the command. The factory 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.get().getUser(), factory); + + commands.put(node, commandInfo); + // Set the NodeInformationProvider that will provide information about + // the added command + serviceDiscoveryManager.setNodeInformationProvider(node, + new NodeInformationProvider() { + public List getNodeItems() { + return null; + } + + public List getNodeFeatures() { + List answer = new ArrayList(); + 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 getNodeIdentities() { + List answer = new ArrayList(); + DiscoverInfo.Identity identity = new DiscoverInfo.Identity( + "automation", name, "command-node"); + answer.add(identity); + return answer; + } + + @Override + public List getNodePacketExtensions() { + return null; + } + + }); + } + + /** + * Discover the commands of an specific JID. The jid 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 { + 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 { + // Collects the commands to publish as items + DiscoverItems discoverItems = new DiscoverItems(); + Collection 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.get(), node, jid); + } + + /** + * 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: + *
      + *
    • The requested command exists
    • + *
    • The requester has permissions to execute it
    • + *
    • The command has more than one stage, if so, it saves the command and + * session ID for further use
    • + *
    + * + *
    + *
    + * If this is not the first request, this method checks, before executing + * the command, if: + *
      + *
    • The session ID of the request was stored
    • + *
    • The session life do not exceed the time out
    • + *
    • The action to execute is one of the available actions
    • + *
    + * + * @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.get().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); + } + } + 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 required 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.get().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); + } + } + } + } + + /** + * 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.get().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) { + throw new XMPPException(new XMPPError( + XMPPError.Condition.interna_server_error)); + } + catch (IllegalAccessException e) { + 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 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; + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandNote.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandNote.java index 1cd8d1f69..3d3ca8d99 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandNote.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandNote.java @@ -1,83 +1,83 @@ -/** - * - * Copyright 2005-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 +/** + * + * Copyright 2005-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommand.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommand.java index 6987f9455..457a0edb8 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommand.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommand.java @@ -1,166 +1,166 @@ -/** - * - * Copyright 2005-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.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:
      - *
    • Node
    • - *
    • Name
    • - *
    • Session ID
    • - *
    • Current Stage
    • - *
    • Available actions
    • - *
    • Default action
    • - *

    - * 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 true. - * - * @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 +/** + * + * Copyright 2005-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.commands.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:

      + *
    • Node
    • + *
    • Name
    • + *
    • Session ID
    • + *
    • Current Stage
    • + *
    • Available actions
    • + *
    • Default action
    • + *

    + * 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 true. + * + * @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--; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommandFactory.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommandFactory.java index d5500b701..aabf5b490 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommandFactory.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/LocalCommandFactory.java @@ -37,4 +37,4 @@ public interface LocalCommandFactory { */ public LocalCommand getInstance() throws InstantiationException, IllegalAccessException; -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/RemoteCommand.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/RemoteCommand.java index 6d1e76da1..eea0804c1 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/RemoteCommand.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/RemoteCommand.java @@ -195,4 +195,4 @@ public class RemoteCommand extends AdHocCommand { public void setPacketReplyTimeout(long packetReplyTimeout) { this.packetReplyTimeout = packetReplyTimeout; } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/packet/AdHocCommandData.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/packet/AdHocCommandData.java index 63559a41f..a6d351570 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/packet/AdHocCommandData.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/packet/AdHocCommandData.java @@ -1,277 +1,277 @@ -/** - * - * Copyright 2005-2008 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.jivesoftware.smackx.xdata.packet.DataForm; - -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 notes = new ArrayList(); - - private DataForm form; - - /* Action request to be executed */ - private AdHocCommand.Action action; - - /* Current execution status */ - private AdHocCommand.Status status; - - private ArrayList actions = new ArrayList(); - - private AdHocCommand.Action executeAction; - - private String lang; - - public AdHocCommandData() { - } - - @Override - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append(""); - - if (getType() == Type.RESULT) { - buf.append(""); - } else { - buf.append(">"); - - for (AdHocCommand.Action action : actions) { - buf.append("<").append(action).append("/>"); - } - buf.append(""); - } - } - - if (form != null) { - buf.append(form.toXML()); - } - - for (AdHocCommandNote note : notes) { - buf.append(""); - buf.append(note.getValue()); - buf.append(""); - } - - // TODO ERRORS -// if (getError() != null) { -// buf.append(getError().toXML()); -// } - - buf.append(""); - 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 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 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 +/** + * + * Copyright 2005-2008 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.jivesoftware.smackx.xdata.packet.DataForm; + +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 notes = new ArrayList(); + + private DataForm form; + + /* Action request to be executed */ + private AdHocCommand.Action action; + + /* Current execution status */ + private AdHocCommand.Status status; + + private ArrayList actions = new ArrayList(); + + private AdHocCommand.Action executeAction; + + private String lang; + + public AdHocCommandData() { + } + + @Override + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append(""); + + if (getType() == Type.RESULT) { + buf.append(""); + } else { + buf.append(">"); + + for (AdHocCommand.Action action : actions) { + buf.append("<").append(action).append("/>"); + } + buf.append(""); + } + } + + if (form != null) { + buf.append(form.toXML()); + } + + for (AdHocCommandNote note : notes) { + buf.append(""); + buf.append(note.getValue()); + buf.append(""); + } + + // TODO ERRORS +// if (getError() != null) { +// buf.append(getError().toXML()); +// } + + buf.append(""); + 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 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 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(); + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/commands/provider/AdHocCommandDataProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/commands/provider/AdHocCommandDataProvider.java index d186b9112..a2fc42205 100755 --- a/extensions/src/main/java/org/jivesoftware/smackx/commands/provider/AdHocCommandDataProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/commands/provider/AdHocCommandDataProvider.java @@ -1,153 +1,153 @@ -/** - * - * Copyright 2005-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.packet.AdHocCommandData; -import org.jivesoftware.smackx.commands.AdHocCommandNote; -import org.jivesoftware.smackx.xdata.packet.DataForm; -import org.jivesoftware.smackx.xdata.provider.DataFormProvider; -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); - } - } -} +/** + * + * Copyright 2005-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.packet.AdHocCommandData; +import org.jivesoftware.smackx.commands.AdHocCommandNote; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jivesoftware.smackx.xdata.provider.DataFormProvider; +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/extensions/src/main/java/org/jivesoftware/smackx/delay/packet/DelayInfo.java b/extensions/src/main/java/org/jivesoftware/smackx/delay/packet/DelayInfo.java index 36ad30811..5626ac2b6 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/delay/packet/DelayInfo.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/delay/packet/DelayInfo.java @@ -1,108 +1,108 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.delay.packet; - -import java.util.Date; - -import org.jivesoftware.smack.util.StringUtils; - -/** - * A decorator for the {@link DelayInformation} class to transparently support - * both the new Delay Delivery specification XEP-0203 and - * the old one XEP-0091. - * - * 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(""); - return buf.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.delay.packet; + +import java.util.Date; + +import org.jivesoftware.smack.util.StringUtils; + +/** + * A decorator for the {@link DelayInformation} class to transparently support + * both the new Delay Delivery specification XEP-0203 and + * the old one XEP-0091. + * + * 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(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/delay/provider/DelayInfoProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/delay/provider/DelayInfoProvider.java index b9e99597d..727f7f7c1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/delay/provider/DelayInfoProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/delay/provider/DelayInfoProvider.java @@ -1,44 +1,44 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.delay.provider; - -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smackx.delay.packet.DelayInfo; -import org.jivesoftware.smackx.delay.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 - * Delay Information XEP-0203 to be - * backward compatible with XEP-0091. - * - *

    This provider must be registered in the smack.properties file for the element - * delay with namespace urn:xmpp:delay

    - * - * @author Robin Collier - */ -public class DelayInfoProvider extends DelayInformationProvider -{ - - @Override - public PacketExtension parseExtension(XmlPullParser parser) throws Exception - { - return new DelayInfo((DelayInformation)super.parseExtension(parser)); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.delay.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.delay.packet.DelayInfo; +import org.jivesoftware.smackx.delay.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 + * Delay Information XEP-0203 to be + * backward compatible with XEP-0091. + * + *

    This provider must be registered in the smack.properties file for the element + * delay with namespace urn:xmpp:delay

    + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverInfoProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverInfoProvider.java index 8e9accbce..db0d7851e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverInfoProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverInfoProvider.java @@ -79,4 +79,4 @@ public class DiscoverInfoProvider implements IQProvider { return discoverInfo; } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverItemsProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverItemsProvider.java index cbe9a7ab9..8d17e8a69 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverItemsProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/disco/provider/DiscoverItemsProvider.java @@ -63,4 +63,4 @@ public class DiscoverItemsProvider implements IQProvider { return discoverItems; } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransfer.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransfer.java index ef71b5573..b67873f70 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransfer.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransfer.java @@ -1,377 +1,377 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferListener.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferListener.java index fc8d05473..179e3f930 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferListener.java @@ -1,33 +1,33 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferManager.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferManager.java index 5c57527e2..4aaa11820 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferManager.java @@ -1,179 +1,179 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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. - *

    - * 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 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(); - - 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); - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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. + *

    + * 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 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(); + + 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java index cbf76546b..57066fb08 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java @@ -1,482 +1,482 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.si.packet.StreamInitiation; -import org.jivesoftware.smackx.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; -import org.jivesoftware.smackx.xdata.packet.DataForm; - -/** - * 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 XEP-0096: SI File Transfer - */ -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 transferObject = - new ConcurrentHashMap(); - - 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 namespaces = new ArrayList(); - 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 namespaces = new ArrayList(); - 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 getSupportedProtocols() { - List protocols = new ArrayList(); - 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 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 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. - *

    - * 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 SOCKS5 Bytestreams, - * which is the preferred method of transfer, and In-Band Bytestreams, - * which is the fallback mechanism. - *

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

    - * 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.setSessionID(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 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; - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.si.packet.StreamInitiation; +import org.jivesoftware.smackx.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * 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 XEP-0096: SI File Transfer + */ +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 transferObject = + new ConcurrentHashMap(); + + 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 namespaces = new ArrayList(); + 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 namespaces = new ArrayList(); + 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 getSupportedProtocols() { + List protocols = new ArrayList(); + 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 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 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. + *

    + * 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 SOCKS5 Bytestreams, + * which is the preferred method of transfer, and In-Band Bytestreams, + * which is the fallback mechanism. + *

    + * 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. + *

    + * 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.setSessionID(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 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java index 16374b6bd..98e2d792a 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferRequest.java @@ -1,135 +1,135 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 IncomingFileTransfer 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); - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 IncomingFileTransfer 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java index b0b365a4e..ee92b3caf 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java @@ -1,149 +1,149 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 XEP-0047: In-Band - * Bytestreams (IBB) - */ -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); - } - - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 XEP-0047: In-Band + * Bytestreams (IBB) + */ +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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java index cdedbda00..e5b269433 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java @@ -1,212 +1,212 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

    - * 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 InputStream to read the file - * data from. - *

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

    - * This method will return immedialtly, file transfer progress can be - * monitored through several methods: - *

    - *

      - *
    • {@link FileTransfer#getStatus()} - *
    • {@link FileTransfer#getProgress()} - *
    • {@link FileTransfer#isDone()} - *
    - * - * @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 streamNegotiatorTask = new FutureTask( - new Callable() { - - 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); - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *

    + * 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 InputStream to read the file + * data from. + *

    + * 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. + *

    + * This method will return immedialtly, file transfer progress can be + * monitored through several methods: + *

    + *

      + *
    • {@link FileTransfer#getStatus()} + *
    • {@link FileTransfer#getProgress()} + *
    • {@link FileTransfer#isDone()} + *
    + * + * @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 streamNegotiatorTask = new FutureTask( + new Callable() { + + 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java index 8f6987111..c9fae458e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java @@ -1,446 +1,446 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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: - * - *
      - *
    • {@link FileTransfer#getStatus()} - *
    • {@link FileTransfer#getProgress()} - *
    • {@link FileTransfer#isDone()} - *
    - * - * @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: - * - *
      - *
    • {@link FileTransfer#getStatus()} - *
    • {@link FileTransfer#getProgress()} - *
    • {@link FileTransfer#isDone()} - *
    - * - * @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. - *

    - * 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); - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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: + * + *

      + *
    • {@link FileTransfer#getStatus()} + *
    • {@link FileTransfer#getProgress()} + *
    • {@link FileTransfer#isDone()} + *
    + * + * @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: + * + *
      + *
    • {@link FileTransfer#getStatus()} + *
    • {@link FileTransfer#getProgress()} + *
    • {@link FileTransfer#isDone()} + *
    + * + * @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. + *

    + * 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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java index a8dbb6861..8d6aec0e9 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java @@ -1,167 +1,167 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 XEP-0065: SOCKS5 Bytestreams - */ -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); - } - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.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 XEP-0065: SOCKS5 Bytestreams + */ +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/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java index 8b1957d0c..3b9c66336 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java @@ -1,164 +1,164 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.packet.StreamInitiation; -import org.jivesoftware.smackx.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; -import org.jivesoftware.smackx.xdata.packet.DataForm; - -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 PacketFilter 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(); - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.si.packet.StreamInitiation; +import org.jivesoftware.smackx.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +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 PacketFilter 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/extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java index 154cbb8b5..06bddac0e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java @@ -1,55 +1,55 @@ -/** - * - * Copyright 2013 Georg Lukas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.forward.provider; - -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.provider.PacketExtensionProvider; -import org.jivesoftware.smack.util.PacketParserUtils; -import org.jivesoftware.smackx.delay.packet.DelayInfo; -import org.jivesoftware.smackx.forward.Forwarded; -import org.xmlpull.v1.XmlPullParser; - -/** - * This class implements the {@link PacketExtensionProvider} to parse - * forwarded messages from a packet. It will return a {@link Forwarded} packet extension. - * - * @author Georg Lukas - */ -public class ForwardedProvider implements PacketExtensionProvider { - public PacketExtension parseExtension(XmlPullParser parser) throws Exception { - DelayInfo di = null; - Packet packet = null; - - boolean done = false; - while (!done) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("delay")) - di = (DelayInfo)PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser); - else if (parser.getName().equals("message")) - packet = PacketParserUtils.parseMessage(parser); - else throw new Exception("Unsupported forwarded packet type: " + parser.getName()); - } - else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(Forwarded.ELEMENT_NAME)) - done = true; - } - if (packet == null) - throw new Exception("forwarded extension must contain a packet"); - return new Forwarded(di, packet); - } -} \ No newline at end of file +/** + * + * Copyright 2013 Georg Lukas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.forward.provider; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.delay.packet.DelayInfo; +import org.jivesoftware.smackx.forward.Forwarded; +import org.xmlpull.v1.XmlPullParser; + +/** + * This class implements the {@link PacketExtensionProvider} to parse + * forwarded messages from a packet. It will return a {@link Forwarded} packet extension. + * + * @author Georg Lukas + */ +public class ForwardedProvider implements PacketExtensionProvider { + public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + DelayInfo di = null; + Packet packet = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("delay")) + di = (DelayInfo)PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser); + else if (parser.getName().equals("message")) + packet = PacketParserUtils.parseMessage(parser); + else throw new Exception("Unsupported forwarded packet type: " + parser.getName()); + } + else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(Forwarded.ELEMENT_NAME)) + done = true; + } + if (packet == null) + throw new Exception("forwarded extension must contain a packet"); + return new Forwarded(di, packet); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java index c0c83adba..381ec3fb0 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java @@ -1,235 +1,235 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.iqlast; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.PacketCollector; -import org.jivesoftware.smack.PacketListener; -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.XMPPException; -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.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.iqlast.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. - *

    - * - * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval - * of: - *

      - *
    1. How long a particular user has been idle - *
    2. How long a particular user has been logged-out and the message the - * specified when doing so. - *
    3. How long a host has been up. - *
    - *

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

    - * - *

    - * Connection con = new XMPPConnection("jabber.org");
    - * con.login("john", "doe");
    - * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org/Smack");
    - * 
    - * - * To get the lapsed time since the last user logout is the same as above but - * with out the resource: - * - *
    - * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org");
    - * 
    - * - * To get the uptime of a host, you simple send the LastActivity packet to it, - * as in the following code example: - *

    - * - *

    - * LastActivity activity = LastActivity.getLastActivity(con, "jabber.org");
    - * 
    - * - * @author Gabriel Guardincerri - * @see XEP-0012: Last - * Activity - */ - -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(); - default: - break; - } - } - }, 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; - } - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.iqlast; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +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.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.iqlast.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. + *

    + * + * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval + * of: + *

      + *
    1. How long a particular user has been idle + *
    2. How long a particular user has been logged-out and the message the + * specified when doing so. + *
    3. How long a host has been up. + *
    + *

    + * + * 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: + *

    + * + *

    + * Connection con = new XMPPConnection("jabber.org");
    + * con.login("john", "doe");
    + * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org/Smack");
    + * 
    + * + * To get the lapsed time since the last user logout is the same as above but + * with out the resource: + * + *
    + * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org");
    + * 
    + * + * To get the uptime of a host, you simple send the LastActivity packet to it, + * as in the following code example: + *

    + * + *

    + * LastActivity activity = LastActivity.getLastActivity(con, "jabber.org");
    + * 
    + * + * @author Gabriel Guardincerri + * @see XEP-0012: Last + * Activity + */ + +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(); + default: + break; + } + } + }, 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/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/PrivateDataManager.java b/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/PrivateDataManager.java index d57daa04c..108260148 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/PrivateDataManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/PrivateDataManager.java @@ -353,4 +353,4 @@ public class PrivateDataManager { return buf.toString(); } } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/DefaultPrivateData.java b/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/DefaultPrivateData.java index 1dc7f8a5f..f6ccba0b3 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/DefaultPrivateData.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/DefaultPrivateData.java @@ -131,4 +131,4 @@ public class DefaultPrivateData implements PrivateData { } map.put(name, value); } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/GroupChatInvitation.java b/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/GroupChatInvitation.java index 6868d6ade..f7d807d8d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/GroupChatInvitation.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/GroupChatInvitation.java @@ -109,4 +109,4 @@ public class GroupChatInvitation implements PacketExtension { return new GroupChatInvitation(roomAddress); } } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCUser.java b/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCUser.java index 734b4a07d..51460c018 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCUser.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCUser.java @@ -621,4 +621,4 @@ public class MUCUser implements PacketExtension { } } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java b/extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java index 233164cf0..34b91fe11 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java @@ -1,110 +1,110 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.nick.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 XEP-0172: User Nickname - */ -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("'); - - 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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.nick.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 XEP-0172: User Nickname + */ +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("'); + + 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/extensions/src/main/java/org/jivesoftware/smackx/pep/PEPManager.java b/extensions/src/main/java/org/jivesoftware/smackx/pep/PEPManager.java index c1eaa1d36..ed8c94bd1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pep/PEPManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pep/PEPManager.java @@ -155,4 +155,4 @@ public class PEPManager { destroy(); super.finalize(); } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/ping/PingFailedListener.java b/extensions/src/main/java/org/jivesoftware/smackx/ping/PingFailedListener.java index b90fed2cd..5692fa719 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/ping/PingFailedListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/ping/PingFailedListener.java @@ -24,4 +24,4 @@ public interface PingFailedListener { * Called when the server ping fails. */ void pingFailed(); -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyList.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyList.java index 8d125ad21..dcd3830cf 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyList.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyList.java @@ -14,60 +14,60 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.privacy; - +package org.jivesoftware.smackx.privacy; + import org.jivesoftware.smackx.privacy.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:
      - * - *
    • Handle many {@link org.jivesoftware.smackx.privacy.packet.PrivacyItem}.
    • - *
    • Answer if it is the default list.
    • - *
    • Answer if it is the active list.
    • - *
    - * - * {@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 items; - - protected PrivacyList(boolean isActiveList, boolean isDefaultList, - String listName, List 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 getItems() { - return items; - } - - public String toString() { - return listName; - } - -} +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:
      + * + *
    • Handle many {@link org.jivesoftware.smackx.privacy.packet.PrivacyItem}.
    • + *
    • Answer if it is the default list.
    • + *
    • Answer if it is the active list.
    • + *
    + * + * {@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 items; + + protected PrivacyList(boolean isActiveList, boolean isDefaultList, + String listName, List 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 getItems() { + return items; + } + + public String toString() { + return listName; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListListener.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListListener.java index d40b30485..fbbcafbd5 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListListener.java @@ -1,48 +1,48 @@ -/** - * - * Copyright 2006-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.privacy; - -import org.jivesoftware.smackx.privacy.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 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 +/** + * + * Copyright 2006-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.privacy; + +import org.jivesoftware.smackx.privacy.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 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); + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java index fe7160ebd..c8b51ea81 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java @@ -1,443 +1,443 @@ -/** - * - * Copyright 2006-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.privacy; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.PacketCollector; -import org.jivesoftware.smack.PacketListener; -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.filter.*; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smackx.privacy.packet.Privacy; -import org.jivesoftware.smackx.privacy.packet.PrivacyItem; - -import java.lang.ref.WeakReference; -import java.util.*; - -/** - * A PrivacyListManager is used by XMPP clients to block or allow communications from other - * users. Use the manager to:
      - *
    • Retrieve privacy lists. - *
    • Add, remove, and edit privacy lists. - *
    • Set, change, or decline active lists. - *
    • Set, change, or decline the default list (i.e., the list that is active by default). - *
    - * 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 instances = Collections - .synchronizedMap(new WeakHashMap()); - - private WeakReference connection; - private final List listeners = new ArrayList(); - 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) { - getInstanceFor(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.

    - * - * @param connection the XMPP connection. - */ - private PrivacyListManager(final Connection connection) { - this.connection = new WeakReference(connection); - // Register the new instance and associate it with the connection - instances.put(connection, this); - - 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> entry : privacy.getItemLists().entrySet()) { - String listName = entry.getKey(); - List 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); } - - /** Answer the connection userJID that owns the privacy. - * @return the userJID that owns the privacy - */ - private String getUser() { - return connection.get().getUser(); - } - - /** - * 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 synchronized PrivacyListManager getInstanceFor(Connection connection) { - PrivacyListManager plm = instances.get(connection); - if (plm == null) plm = new PrivacyListManager(connection); - return plm; - } - - /** - * 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 { - Connection connection = PrivacyListManager.this.connection.get(); - if (connection == null) throw new XMPPException("Connection instance already gc'ed"); - // 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 { - Connection connection = PrivacyListManager.this.connection.get(); - if (connection == null) throw new XMPPException("Connection instance already gc'ed"); - // 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 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()); - - // 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 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 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 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()); - - // 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); - } - } -} +/** + * + * Copyright 2006-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.privacy; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.privacy.packet.Privacy; +import org.jivesoftware.smackx.privacy.packet.PrivacyItem; + +import java.lang.ref.WeakReference; +import java.util.*; + +/** + * A PrivacyListManager is used by XMPP clients to block or allow communications from other + * users. Use the manager to:

      + *
    • Retrieve privacy lists. + *
    • Add, remove, and edit privacy lists. + *
    • Set, change, or decline active lists. + *
    • Set, change, or decline the default list (i.e., the list that is active by default). + *
    + * 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 instances = Collections + .synchronizedMap(new WeakHashMap()); + + private WeakReference connection; + private final List listeners = new ArrayList(); + 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) { + getInstanceFor(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.

    + * + * @param connection the XMPP connection. + */ + private PrivacyListManager(final Connection connection) { + this.connection = new WeakReference(connection); + // Register the new instance and associate it with the connection + instances.put(connection, this); + + 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> entry : privacy.getItemLists().entrySet()) { + String listName = entry.getKey(); + List 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); } + + /** Answer the connection userJID that owns the privacy. + * @return the userJID that owns the privacy + */ + private String getUser() { + return connection.get().getUser(); + } + + /** + * 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 synchronized PrivacyListManager getInstanceFor(Connection connection) { + PrivacyListManager plm = instances.get(connection); + if (plm == null) plm = new PrivacyListManager(connection); + return plm; + } + + /** + * 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 { + Connection connection = PrivacyListManager.this.connection.get(); + if (connection == null) throw new XMPPException("Connection instance already gc'ed"); + // 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 { + Connection connection = PrivacyListManager.this.connection.get(); + if (connection == null) throw new XMPPException("Connection instance already gc'ed"); + // 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 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()); + + // 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 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 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 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()); + + // 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/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/Privacy.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/Privacy.java index 3670f6463..92eabfdce 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/Privacy.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/Privacy.java @@ -1,322 +1,322 @@ -/** - * - * Copyright 2006-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.privacy.packet; - -import java.util.*; - -import org.jivesoftware.smack.packet.IQ; - -/** - * A Privacy IQ Packet, is used by the {@link org.jivesoftware.smackx.privacy.PrivacyListManager} - * and {@link org.jivesoftware.smackx.privacy.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: - *

      - *
    • Retrieving one's privacy lists. - *
    • Adding, removing, and editing one's privacy lists. - *
    • Setting, changing, or declining active lists. - *
    • Setting, changing, or declining the default list (i.e., the list that is active by default). - *
    - * 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> itemLists = new HashMap>(); - - /** - * 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 setPrivacyList(String listName, List 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 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 null if none was found. - * - * @return list with {@link PrivacyItem} or null if none was found. - */ - public List 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 null if none was found. - * - * @return list with {@link PrivacyItem} or null if none was found. - */ - public List 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 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 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> 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 getPrivacyListNames() { - return this.itemLists.keySet(); - } - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append(""); - - // Add the active tag - if (this.isDeclineActiveList()) { - buf.append(""); - } else { - if (this.getActiveName() != null) { - buf.append(""); - } - } - // Add the default tag - if (this.isDeclineDefaultList()) { - buf.append(""); - } else { - if (this.getDefaultName() != null) { - buf.append(""); - } - } - - // Add the list with their privacy items - for (Map.Entry> entry : this.getItemLists().entrySet()) { - String listName = entry.getKey(); - List items = entry.getValue(); - // Begin the list tag - if (items.isEmpty()) { - buf.append(""); - } else { - buf.append(""); - } - for (PrivacyItem item : items) { - // Append the item xml representation - buf.append(item.toXML()); - } - // Close the list tag - if (!items.isEmpty()) { - buf.append(""); - } - } - - // Add packet extensions, if any are defined. - buf.append(getExtensionsXML()); - buf.append(""); - return buf.toString(); - } - -} \ No newline at end of file +/** + * + * Copyright 2006-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.privacy.packet; + +import java.util.*; + +import org.jivesoftware.smack.packet.IQ; + +/** + * A Privacy IQ Packet, is used by the {@link org.jivesoftware.smackx.privacy.PrivacyListManager} + * and {@link org.jivesoftware.smackx.privacy.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: + *
      + *
    • Retrieving one's privacy lists. + *
    • Adding, removing, and editing one's privacy lists. + *
    • Setting, changing, or declining active lists. + *
    • Setting, changing, or declining the default list (i.e., the list that is active by default). + *
    + * 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> itemLists = new HashMap>(); + + /** + * 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 setPrivacyList(String listName, List 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 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 null if none was found. + * + * @return list with {@link PrivacyItem} or null if none was found. + */ + public List 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 null if none was found. + * + * @return list with {@link PrivacyItem} or null if none was found. + */ + public List 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 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 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> 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 getPrivacyListNames() { + return this.itemLists.keySet(); + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append(""); + + // Add the active tag + if (this.isDeclineActiveList()) { + buf.append(""); + } else { + if (this.getActiveName() != null) { + buf.append(""); + } + } + // Add the default tag + if (this.isDeclineDefaultList()) { + buf.append(""); + } else { + if (this.getDefaultName() != null) { + buf.append(""); + } + } + + // Add the list with their privacy items + for (Map.Entry> entry : this.getItemLists().entrySet()) { + String listName = entry.getKey(); + List items = entry.getValue(); + // Begin the list tag + if (items.isEmpty()) { + buf.append(""); + } else { + buf.append(""); + } + for (PrivacyItem item : items) { + // Append the item xml representation + buf.append(item.toXML()); + } + // Close the list tag + if (!items.isEmpty()) { + buf.append(""); + } + } + + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java index 1a84863ec..54240c957 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java @@ -14,448 +14,448 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.privacy.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:
      - *
    • Allowing or blocking messages. - *
    • Allowing or blocking inbound presence notifications. - *
    • Allowing or blocking outbound presence notifications. - *
    • Allowing or blocking IQ stanzas. - *
    • Allowing or blocking all communications. - *
    - * @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(""); - } else { - buf.append(">"); - if (this.isFilterIQ()) { - buf.append(""); - } - if (this.isFilterMessage()) { - buf.append(""); - } - if (this.isFilterPresence_in()) { - buf.append(""); - } - if (this.isFilterPresence_out()) { - buf.append(""); - } - buf.append(""); - } - 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.smackx.privacy.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:
      + *
    • Allowing or blocking messages. + *
    • Allowing or blocking inbound presence notifications. + *
    • Allowing or blocking outbound presence notifications. + *
    • Allowing or blocking IQ stanzas. + *
    • Allowing or blocking all communications. + *
    + * @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(""); + } else { + buf.append(">"); + if (this.isFilterIQ()) { + buf.append(""); + } + if (this.isFilterMessage()) { + buf.append(""); + } + if (this.isFilterPresence_in()) { + buf.append(""); + } + if (this.isFilterPresence_out()) { + buf.append(""); + } + buf.append(""); + } + 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/extensions/src/main/java/org/jivesoftware/smackx/privacy/provider/PrivacyProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/privacy/provider/PrivacyProvider.java index c43b165b7..39913f9c7 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/privacy/provider/PrivacyProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/privacy/provider/PrivacyProvider.java @@ -14,138 +14,138 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.privacy.provider; - -import org.jivesoftware.smack.packet.DefaultPacketExtension; -import org.jivesoftware.smack.packet.IQ; +package org.jivesoftware.smackx.privacy.provider; + +import org.jivesoftware.smack.packet.DefaultPacketExtension; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smackx.privacy.packet.Privacy; import org.jivesoftware.smackx.privacy.packet.PrivacyItem; -import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParser; -import java.util.ArrayList; - -/** - * The PrivacyProvider parses {@link Privacy} packets. {@link Privacy} - * Parses the query sub-document and creates an instance of {@link Privacy}. - * For each item in the list element, it creates an instance - * of {@link PrivacyItem} and {@link org.jivesoftware.smackx.privacy.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 items = new ArrayList(); - 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; - } -} +import java.util.ArrayList; + +/** + * The PrivacyProvider parses {@link Privacy} packets. {@link Privacy} + * Parses the query sub-document and creates an instance of {@link Privacy}. + * For each item in the list element, it creates an instance + * of {@link PrivacyItem} and {@link org.jivesoftware.smackx.privacy.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 items = new ArrayList(); + 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AccessModel.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AccessModel.java index b6e583065..7f46f1bef 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AccessModel.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AccessModel.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 16.4.3 - * - * @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; -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 16.4.3 + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Affiliation.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Affiliation.java index a4864eb69..5a5a78d92 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Affiliation.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Affiliation.java @@ -1,93 +1,93 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("'"); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java index 448c51f57..86cf25045 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java @@ -1,72 +1,72 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 affiliations element of the reply to a request for affiliations. - * It is defined in the specification in section 5.7 Retrieve Affiliations. - * - * @author Robin Collier - */ -public class AffiliationsExtension extends NodeExtension -{ - protected List items = Collections.emptyList(); - - public AffiliationsExtension() - { - super(PubSubElementType.AFFILIATIONS); - } - - public AffiliationsExtension(List subList) - { - super(PubSubElementType.AFFILIATIONS); - items = subList; - } - - public List 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(""); - return builder.toString(); - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 affiliations element of the reply to a request for affiliations. + * It is defined in the specification in section 5.7 Retrieve Affiliations. + * + * @author Robin Collier + */ +public class AffiliationsExtension extends NodeExtension +{ + protected List items = Collections.emptyList(); + + public AffiliationsExtension() + { + super(PubSubElementType.AFFILIATIONS); + } + + public AffiliationsExtension(List subList) + { + super(PubSubElementType.AFFILIATIONS); + items = subList; + } + + public List 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(""); + return builder.toString(); + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java index 72783a501..f97d1e092 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ChildrenAssociationPolicy.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 16.4.3 - * - * @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; -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 16.4.3 + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/CollectionNode.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/CollectionNode.java index 111abc132..3c530dc85 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/CollectionNode.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/CollectionNode.java @@ -14,15 +14,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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java index 60d895e59..0b26decd8 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigurationEvent.java @@ -1,59 +1,59 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 configuration 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 getExtensions() - { - if (getConfiguration() == null) - return Collections.emptyList(); - else - return Arrays.asList(((PacketExtension)getConfiguration().getDataFormToSend())); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 configuration 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 getExtensions() + { + if (getConfiguration() == null) + return Collections.emptyList(); + else + return Arrays.asList(((PacketExtension)getConfiguration().getDataFormToSend())); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureForm.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureForm.java index 9e796dc08..7c70333bb 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureForm.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureForm.java @@ -1,712 +1,712 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; -import org.jivesoftware.smackx.xdata.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}. - * - *

    Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not - * exist, all ConfigureForm.setXXX 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 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 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 values = new ArrayList(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 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 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 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 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 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 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 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 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 fields = getFields(); - - while (fields.hasNext()) - { - FormField formField = fields.next(); - result.append('('); - result.append(formField.getVariable()); - result.append(':'); - - Iterator 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 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 getListSingle(String value) - { - List list = new ArrayList(1); - list.add(value); - return list; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.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}. + * + *

    Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not + * exist, all ConfigureForm.setXXX 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 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 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 values = new ArrayList(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 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 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 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 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 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 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 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 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 fields = getFields(); + + while (fields.hasNext()) + { + FormField formField = fields.next(); + result.append('('); + result.append(formField.getVariable()); + result.append(':'); + + Iterator 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 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 getListSingle(String value) + { + List list = new ArrayList(1); + list.add(value); + return list; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java index c0e286ea4..1f00e9260 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ConfigureNodeFields.java @@ -1,221 +1,221 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.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 - * - *

    Value: {@link AccessModel}

    - */ - access_model, - - /** - * The URL of an XSL transformation which can be applied to - * payloads in order to generate an appropriate message - * body element - * - *

    Value: {@link URL}

    - */ - body_xslt, - - /** - * The collection with which a node is affiliated - * - *

    Value: String

    - */ - 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. - * - *

    Value: {@link URL}

    - */ - dataform_xslt, - - /** - * Whether to deliver payloads with event notifications - * - *

    Value: boolean

    - */ - deliver_payloads, - - /** - * Whether owners or publisher should receive replies to items - * - *

    Value: {@link ItemReply}

    - */ - itemreply, - - /** - * Who may associate leaf nodes with a collection - * - *

    Value: {@link ChildrenAssociationPolicy}

    - */ - children_association_policy, - - /** - * The list of JIDs that may associate leaf nodes with a - * collection - * - *

    Value: List of JIDs as Strings

    - */ - children_association_whitelist, - - /** - * The child nodes (leaf or collection) associated with a collection - * - *

    Value: List of Strings

    - */ - children, - - /** - * The maximum number of child nodes that can be associated with a - * collection - * - *

    Value: int

    - */ - children_max, - - /** - * The maximum number of items to persist - * - *

    Value: int

    - */ - max_items, - - /** - * The maximum payload size in bytes - * - *

    Value: int

    - */ - max_payload_size, - - /** - * Whether the node is a leaf (default) or collection - * - *

    Value: {@link NodeType}

    - */ - node_type, - - /** - * Whether to notify subscribers when the node configuration changes - * - *

    Value: boolean

    - */ - notify_config, - - /** - * Whether to notify subscribers when the node is deleted - * - *

    Value: boolean

    - */ - notify_delete, - - /** - * Whether to notify subscribers when items are removed from the node - * - *

    Value: boolean

    - */ - notify_retract, - - /** - * Whether to persist items to storage. This is required to have multiple - * items in the node. - * - *

    Value: boolean

    - */ - persist_items, - - /** - * Whether to deliver notifications to available users only - * - *

    Value: boolean

    - */ - presence_based_delivery, - - /** - * Defines who can publish to the node - * - *

    Value: {@link PublishModel}

    - */ - publish_model, - - /** - * The specific multi-user chat rooms to specify for replyroom - * - *

    Value: List of JIDs as Strings

    - */ - replyroom, - - /** - * The specific JID(s) to specify for replyto - * - *

    Value: List of JIDs as Strings

    - */ - replyto, - - /** - * The roster group(s) allowed to subscribe and retrieve items - * - *

    Value: List of strings

    - */ - roster_groups_allowed, - - /** - * Whether to allow subscriptions - * - *

    Value: boolean

    - */ - subscribe, - - /** - * A friendly name for the node - * - *

    Value: String

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

    Value: String

    - */ - type; - - public String getFieldName() - { - return "pubsub#" + toString(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.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 + * + *

    Value: {@link AccessModel}

    + */ + access_model, + + /** + * The URL of an XSL transformation which can be applied to + * payloads in order to generate an appropriate message + * body element + * + *

    Value: {@link URL}

    + */ + body_xslt, + + /** + * The collection with which a node is affiliated + * + *

    Value: String

    + */ + 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. + * + *

    Value: {@link URL}

    + */ + dataform_xslt, + + /** + * Whether to deliver payloads with event notifications + * + *

    Value: boolean

    + */ + deliver_payloads, + + /** + * Whether owners or publisher should receive replies to items + * + *

    Value: {@link ItemReply}

    + */ + itemreply, + + /** + * Who may associate leaf nodes with a collection + * + *

    Value: {@link ChildrenAssociationPolicy}

    + */ + children_association_policy, + + /** + * The list of JIDs that may associate leaf nodes with a + * collection + * + *

    Value: List of JIDs as Strings

    + */ + children_association_whitelist, + + /** + * The child nodes (leaf or collection) associated with a collection + * + *

    Value: List of Strings

    + */ + children, + + /** + * The maximum number of child nodes that can be associated with a + * collection + * + *

    Value: int

    + */ + children_max, + + /** + * The maximum number of items to persist + * + *

    Value: int

    + */ + max_items, + + /** + * The maximum payload size in bytes + * + *

    Value: int

    + */ + max_payload_size, + + /** + * Whether the node is a leaf (default) or collection + * + *

    Value: {@link NodeType}

    + */ + node_type, + + /** + * Whether to notify subscribers when the node configuration changes + * + *

    Value: boolean

    + */ + notify_config, + + /** + * Whether to notify subscribers when the node is deleted + * + *

    Value: boolean

    + */ + notify_delete, + + /** + * Whether to notify subscribers when items are removed from the node + * + *

    Value: boolean

    + */ + notify_retract, + + /** + * Whether to persist items to storage. This is required to have multiple + * items in the node. + * + *

    Value: boolean

    + */ + persist_items, + + /** + * Whether to deliver notifications to available users only + * + *

    Value: boolean

    + */ + presence_based_delivery, + + /** + * Defines who can publish to the node + * + *

    Value: {@link PublishModel}

    + */ + publish_model, + + /** + * The specific multi-user chat rooms to specify for replyroom + * + *

    Value: List of JIDs as Strings

    + */ + replyroom, + + /** + * The specific JID(s) to specify for replyto + * + *

    Value: List of JIDs as Strings

    + */ + replyto, + + /** + * The roster group(s) allowed to subscribe and retrieve items + * + *

    Value: List of strings

    + */ + roster_groups_allowed, + + /** + * Whether to allow subscriptions + * + *

    Value: boolean

    + */ + subscribe, + + /** + * A friendly name for the node + * + *

    Value: String

    + */ + 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 + * + *

    Value: String

    + */ + type; + + public String getFieldName() + { + return "pubsub#" + toString(); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java index 3e5b5845b..99302665d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EmbeddedPacketExtension.java @@ -1,48 +1,48 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - * - *

    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 getExtensions(); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + * + *

    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 getExtensions(); +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElement.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElement.java index 3502e5363..b3e0b136b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElement.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElement.java @@ -1,77 +1,77 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(""); - - builder.append(ext.toXML()); - builder.append(""); - return builder.toString(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(""); + + builder.append(ext.toXML()); + builder.append(""); + return builder.toString(); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElementType.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElementType.java index fbddd4063..70da54ea6 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElementType.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/EventElementType.java @@ -1,44 +1,44 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java index e1a51d96a..a4c476b2a 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java @@ -1,102 +1,102 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.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("'); - return builder.toString(); - } - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.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("'); + return builder.toString(); + } + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNodeType.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNodeType.java index 8d569febb..c7c58a3f2 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNodeType.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNodeType.java @@ -1,53 +1,53 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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()); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormType.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormType.java index 15b1d9b19..855c8dbbf 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormType.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormType.java @@ -1,29 +1,29 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.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 +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.xdata.Form; + +/** + * Defines the allowable types for a {@link Form} + * + * @author Robin Collier + */ +public enum FormType +{ + form, submit, cancel, result; +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java index a8ac194fd..e2b24b268 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java @@ -1,88 +1,88 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java index 7f44d48a7..30a57893f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java @@ -1,135 +1,135 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Item has several properties that are dependent - * on the configuration of the node to which it has been or will be published. - * - *

    An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)} - *
  • 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. - *
  • Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set - * to true, otherwise it will be null. - * - *

    An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()} - *
  • 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. - *
  • Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set - * to true. - * - *

    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 Item 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 Item 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 Item 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 Item with an id and a node id. - *

    - * Note: 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 may 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(""); - - return builder.toString(); - } - - @Override - public String toString() - { - return getClass().getName() + " | Content [" + toXML() + "]"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Item has several properties that are dependent + * on the configuration of the node to which it has been or will be published. + * + *

    An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)} + *
  • 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. + *
  • Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set + * to true, otherwise it will be null. + * + *

    An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()} + *
  • 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. + *
  • Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set + * to true. + * + *

    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 Item 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 Item 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 Item 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 Item with an id and a node id. + *

    + * Note: 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 may 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(""); + + return builder.toString(); + } + + @Override + public String toString() + { + return getClass().getName() + " | Content [" + toXML() + "]"; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java index 584f1c45a..41b11c52b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java @@ -1,65 +1,65 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 itemIds = Collections.emptyList(); - - /** - * Constructs an ItemDeleteEvent 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 deletedItemIds, List 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 getItemIds() - { - return Collections.unmodifiableList(itemIds); - } - - @Override - public String toString() - { - return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Deleted Items: " + itemIds + ']'; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 itemIds = Collections.emptyList(); + + /** + * Constructs an ItemDeleteEvent 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 deletedItemIds, List 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 getItemIds() + { + return Collections.unmodifiableList(itemIds); + } + + @Override + public String toString() + { + return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Deleted Items: " + itemIds + ']'; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java index d961d8435..ba47ba48e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java @@ -1,124 +1,124 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 extends SubscriptionEvent -{ - private List items; - private Date originalDate; - - /** - * Constructs an ItemPublishEvent 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 eventItems) - { - super(nodeId); - items = eventItems; - } - - /** - * Constructs an ItemPublishEvent 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 eventItems, List subscriptionIds) - { - super(nodeId, subscriptionIds); - items = eventItems; - } - - /** - * Constructs an ItemPublishEvent 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 - */ - public ItemPublishEvent(String nodeId, List eventItems, List 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 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. - * - */ - public Date getPublishedDate() - { - return originalDate; - } - - @Override - public String toString() - { - return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Delayed: " + - (isDelayed() ? originalDate.toString() : "false") + ']'; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 extends SubscriptionEvent +{ + private List items; + private Date originalDate; + + /** + * Constructs an ItemPublishEvent 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 eventItems) + { + super(nodeId); + items = eventItems; + } + + /** + * Constructs an ItemPublishEvent 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 eventItems, List subscriptionIds) + { + super(nodeId, subscriptionIds); + items = eventItems; + } + + /** + * Constructs an ItemPublishEvent 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 + */ + public ItemPublishEvent(String nodeId, List eventItems, List 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 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. + * + */ + public Date getPublishedDate() + { + return originalDate; + } + + @Override + public String toString() + { + return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Delayed: " + + (isDelayed() ? originalDate.toString() : "false") + ']'; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemReply.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemReply.java index 1a791e620..302507a66 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemReply.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemReply.java @@ -1,32 +1,32 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java index a7f9ec899..a9fa94006 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java @@ -1,200 +1,200 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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. - *

  • It can represent an event containing a list of items that have been published - *
  • It can represent an event containing a list of retracted (deleted) items. - *
  • It can represent a request to delete a list of items. - *
  • It can represent a request to get existing items. - * - *

    Please note, this class is used for internal purposes, and is not required for usage of - * pubsub functionality. - * - * @author Robin Collier - */ -public class ItemsExtension extends NodeExtension implements EmbeddedPacketExtension -{ - protected ItemsElementType type; - protected Boolean notify; - protected List items; - - public enum ItemsElementType - { - /** An items element, which has an optional max_items attribute when requesting items */ - items(PubSubElementType.ITEMS, "max_items"), - - /** A retract element, which has an optional notify 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. - * - *

    Valid scenarios are: - *

  • Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an - * optional value for the max_items attribute. - *
  • Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing - * only id's and an optional value for the notify attribute. - *
  • Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and - * attributeValue = null - *
  • Items deleted event - itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and - * attributeValue = null - * - * @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 max_items - */ - public ItemsExtension(ItemsElementType itemsType, String nodeId, List items) - { - super(itemsType.getNodeElement(), nodeId); - type = itemsType; - this.items = items; - } - - /** - * Construct an instance with a list representing items that have been published or deleted. - * - *

    Valid scenarios are: - *

  • Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an - * optional value for the max_items attribute. - *
  • Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing - * only id's and an optional value for the notify attribute. - *
  • Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and - * attributeValue = null - *
  • Items deleted event - itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and - * attributeValue = null - * - * @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 max_items - */ - public ItemsExtension(String nodeId, List 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; - } - - @SuppressWarnings("unchecked") - public List getExtensions() - { - return (List)getItems(); - } - - /** - * Gets the items related to the type of request or event. - * - * return List of {@link Item}, {@link RetractItem}, or null - */ - public List 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(""); - return builder.toString(); - } - } - - @Override - public String toString() - { - return getClass().getName() + "Content [" + toXML() + "]"; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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. + *
  • It can represent an event containing a list of items that have been published + *
  • It can represent an event containing a list of retracted (deleted) items. + *
  • It can represent a request to delete a list of items. + *
  • It can represent a request to get existing items. + * + *

    Please note, this class is used for internal purposes, and is not required for usage of + * pubsub functionality. + * + * @author Robin Collier + */ +public class ItemsExtension extends NodeExtension implements EmbeddedPacketExtension +{ + protected ItemsElementType type; + protected Boolean notify; + protected List items; + + public enum ItemsElementType + { + /** An items element, which has an optional max_items attribute when requesting items */ + items(PubSubElementType.ITEMS, "max_items"), + + /** A retract element, which has an optional notify 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. + * + *

    Valid scenarios are: + *

  • Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an + * optional value for the max_items attribute. + *
  • Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing + * only id's and an optional value for the notify attribute. + *
  • Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and + * attributeValue = null + *
  • Items deleted event - itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and + * attributeValue = null + * + * @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 max_items + */ + public ItemsExtension(ItemsElementType itemsType, String nodeId, List items) + { + super(itemsType.getNodeElement(), nodeId); + type = itemsType; + this.items = items; + } + + /** + * Construct an instance with a list representing items that have been published or deleted. + * + *

    Valid scenarios are: + *

  • Request items from node - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and an + * optional value for the max_items attribute. + *
  • Request to delete items - itemsType = {@link ItemsElementType#retract}, items = list of {@link Item} containing + * only id's and an optional value for the notify attribute. + *
  • Items published event - itemsType = {@link ItemsElementType#items}, items = list of {@link Item} and + * attributeValue = null + *
  • Items deleted event - itemsType = {@link ItemsElementType#items}, items = list of {@link RetractItem} and + * attributeValue = null + * + * @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 max_items + */ + public ItemsExtension(String nodeId, List 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; + } + + @SuppressWarnings("unchecked") + public List getExtensions() + { + return (List)getItems(); + } + + /** + * Gets the items related to the type of request or event. + * + * return List of {@link Item}, {@link RetractItem}, or null + */ + public List 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(""); + return builder.toString(); + } + } + + @Override + public String toString() + { + return getClass().getName() + "Content [" + toXML() + "]"; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/LeafNode.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/LeafNode.java index b798b033a..a650bba21 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/LeafNode.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/LeafNode.java @@ -1,362 +1,362 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.disco.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 - */ - @SuppressWarnings("unchecked") - public List 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)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 - */ - @SuppressWarnings("unchecked") - public List 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)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 - */ - @SuppressWarnings("unchecked") - public List getItems(Collection ids) - throws XMPPException - { - List itemList = new ArrayList(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)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 - */ - @SuppressWarnings("unchecked") - public List 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)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 - */ - @SuppressWarnings("unchecked") - public List 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)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 - */ - @SuppressWarnings("unchecked") - public void publish(T item) - { - Collection items = new ArrayList(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 void publish(Collection items) - { - PubSub packet = createPubsubPacket(Type.SET, new PublishItem(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 deliver_payload=false, 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 - */ - @SuppressWarnings("unchecked") - public void send(T item) - throws XMPPException - { - Collection items = new ArrayList(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 void send(Collection items) - throws XMPPException - { - PubSub packet = createPubsubPacket(Type.SET, new PublishItem(getId(), items)); - - SyncPacketSend.getReply(con, packet); - } - - /** - * Purges the node of all items. - * - *

    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 items = new ArrayList(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 itemIds) - throws XMPPException - { - List items = new ArrayList(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); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.disco.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 + */ + @SuppressWarnings("unchecked") + public List 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)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 + */ + @SuppressWarnings("unchecked") + public List 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)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 + */ + @SuppressWarnings("unchecked") + public List getItems(Collection ids) + throws XMPPException + { + List itemList = new ArrayList(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)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 + */ + @SuppressWarnings("unchecked") + public List 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)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 + */ + @SuppressWarnings("unchecked") + public List 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)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 + */ + @SuppressWarnings("unchecked") + public void publish(T item) + { + Collection items = new ArrayList(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 void publish(Collection items) + { + PubSub packet = createPubsubPacket(Type.SET, new PublishItem(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 deliver_payload=false, 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 + */ + @SuppressWarnings("unchecked") + public void send(T item) + throws XMPPException + { + Collection items = new ArrayList(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 void send(Collection items) + throws XMPPException + { + PubSub packet = createPubsubPacket(Type.SET, new PublishItem(getId(), items)); + + SyncPacketSend.getReply(con, packet); + } + + /** + * Purges the node of all items. + * + *

    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 items = new ArrayList(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 itemIds) + throws XMPPException + { + List items = new ArrayList(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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java index c0b218c6d..5944976ea 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java @@ -14,525 +14,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; +package org.jivesoftware.smackx.pubsub; -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 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.delay.packet.DelayInformation; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -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; +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; import org.jivesoftware.smackx.shim.packet.Header; import org.jivesoftware.smackx.shim.packet.HeadersExtension; import org.jivesoftware.smackx.xdata.Form; - -abstract public class Node -{ - protected Connection con; - protected String id; - protected String to; - - protected ConcurrentHashMap, PacketListener> itemEventToListenerMap = new ConcurrentHashMap, PacketListener>(); - protected ConcurrentHashMap itemDeleteToListenerMap = new ConcurrentHashMap(); - protected ConcurrentHashMap configEventToListenerMap = new ConcurrentHashMap(); - - /** - * 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 pubsub - */ - 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 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 getSubscriptionIds(Packet packet) - { - HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim"); - List values = null; - - if (headers != null) - { - values = new ArrayList(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)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 extList = event.getExtensions(); - - if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName())) - { - listener.handlePurge(); - } - else - { - ItemsExtension itemsElem = (ItemsExtension)event.getEvent(); - Collection pubItems = itemsElem.getItems(); - Iterator it = (Iterator)pubItems.iterator(); - List items = new ArrayList(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 secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions(); - - if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement)) - return true; - } - } - return false; - } - } -} + +abstract public class Node +{ + protected Connection con; + protected String id; + protected String to; + + protected ConcurrentHashMap, PacketListener> itemEventToListenerMap = new ConcurrentHashMap, PacketListener>(); + protected ConcurrentHashMap itemDeleteToListenerMap = new ConcurrentHashMap(); + protected ConcurrentHashMap configEventToListenerMap = new ConcurrentHashMap(); + + /** + * 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 pubsub + */ + 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 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 getSubscriptionIds(Packet packet) + { + HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim"); + List values = null; + + if (headers != null) + { + values = new ArrayList(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)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 extList = event.getExtensions(); + + if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName())) + { + listener.handlePurge(); + } + else + { + ItemsExtension itemsElem = (ItemsExtension)event.getEvent(); + Collection pubItems = itemsElem.getItems(); + Iterator it = (Iterator)pubItems.iterator(); + List items = new ArrayList(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 secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions(); + + if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement)) + return true; + } + } + return false; + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeEvent.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeEvent.java index e81aac0d6..97d3cef72 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeEvent.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeEvent.java @@ -14,19 +14,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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java index 0aca28210..e0ed017ca 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java @@ -1,88 +1,88 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 node 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 NodeExtension 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 NodeExtension 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() + "]"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 node 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 NodeExtension 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 NodeExtension 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeType.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeType.java index 17aaeadba..1fe20ec10 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeType.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeType.java @@ -1,28 +1,28 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java index 5d8125367..2719d1ac1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java @@ -1,75 +1,75 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 options 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(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 options 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java index 786c631d7..e8b7eebd1 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java @@ -1,141 +1,141 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Item has several properties that are dependent - * on the configuration of the node to which it has been or will be published. - * - *

    An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)} - *
  • 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. - *
  • Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set - * to true, otherwise it will be null. - * - *

    An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()} - *
  • 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. - *
  • Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set - * to true. - * - *

    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 extends Item -{ - private E payload; - - /** - * Create an Item 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 Item 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 Item with an id, node id and payload. - * - *

    - * Note: 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 may 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 Item. 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(""); - builder.append(payload.toXML()); - builder.append(""); - - return builder.toString(); - } - - @Override - public String toString() - { - return getClass().getName() + " | Content [" + toXML() + "]"; - } -} \ No newline at end of file +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Item has several properties that are dependent + * on the configuration of the node to which it has been or will be published. + * + *

    An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)} + *
  • 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. + *
  • Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set + * to true, otherwise it will be null. + * + *

    An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()} + *
  • 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. + *
  • Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set + * to true. + * + *

    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 extends Item +{ + private E payload; + + /** + * Create an Item 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 Item 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 Item with an id, node id and payload. + * + *

    + * Note: 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 may 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 Item. 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(""); + builder.append(payload.toXML()); + builder.append(""); + + return builder.toString(); + } + + @Override + public String toString() + { + return getClass().getName() + " | Content [" + toXML() + "]"; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PresenceState.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PresenceState.java index efdfb8bd3..0f4a83d05 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PresenceState.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PresenceState.java @@ -1,28 +1,28 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubElementType.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubElementType.java index 1c82891f9..53c89565d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubElementType.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubElementType.java @@ -1,83 +1,83 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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('-', '_')); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java index e6f8899b6..d56cc3a92 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java @@ -1,333 +1,333 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.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; -import org.jivesoftware.smackx.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; - -/** - * 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 XEP-0060. - * - * @author Robin Collier - */ -final public class PubSubManager -{ - private Connection con; - private String to; - private Map nodeMap = new ConcurrentHashMap(); - - /** - * Create a pubsub manager associated to the specified connection. Defaults the service - * name to pubsub - * - * @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 - */ - @SuppressWarnings("unchecked") - public 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 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 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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.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; +import org.jivesoftware.smackx.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; + +/** + * 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 XEP-0060. + * + * @author Robin Collier + */ +final public class PubSubManager +{ + private Connection con; + private String to; + private Map nodeMap = new ConcurrentHashMap(); + + /** + * Create a pubsub manager associated to the specified connection. Defaults the service + * name to pubsub + * + * @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 + */ + @SuppressWarnings("unchecked") + public 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 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 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java index b6ebbee2a..9980b61c7 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java @@ -1,73 +1,73 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 extends NodeExtension -{ - protected Collection 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(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 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(""); - - return builder.toString(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 extends NodeExtension +{ + protected Collection 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(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 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(""); + + return builder.toString(); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishModel.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishModel.java index 134ab980c..d4c616ad5 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishModel.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishModel.java @@ -1,35 +1,35 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/RetractItem.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/RetractItem.java index 1b94ccc5c..7f32965ff 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/RetractItem.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/RetractItem.java @@ -1,62 +1,62 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 RetractItem 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 ""; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 RetractItem 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 ""; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java index 004be469e..d22a41b68 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java @@ -1,68 +1,68 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 SimplePayload 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() + "]"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 SimplePayload 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java index eda87a96a..de7a75861 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java @@ -1,63 +1,63 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeForm.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeForm.java index 3217cb774..9c8e5dae7 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeForm.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeForm.java @@ -1,243 +1,243 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; -import org.jivesoftware.smackx.xdata.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}. - * - *

    Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not - * exist, all SubscribeForm.setXXX 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 getShowValues() - { - ArrayList result = new ArrayList(5); - Iterator 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 stateValues) - { - ArrayList values = new ArrayList(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 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); - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.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}. + * + *

    Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not + * exist, all SubscribeForm.setXXX 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 getShowValues() + { + ArrayList result = new ArrayList(5); + Iterator 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 stateValues) + { + ArrayList values = new ArrayList(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 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java index d8d10cfde..263e2b4fe 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeOptionFields.java @@ -1,101 +1,101 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Section 16.4.2. - * - * @author Robin Collier - */ -public enum SubscribeOptionFields -{ - /** - * Whether an entity wants to receive or disable notifications - * - *

    Value: boolean

    - */ - deliver, - - /** - * Whether an entity wants to receive digests (aggregations) of - * notifications or all notifications individually. - * - *

    Value: boolean

    - */ - digest, - - /** - * The minimum number of seconds between sending any two notifications digests - * - *

    Value: int

    - */ - digest_frequency, - - /** - * - *

    Value: {@link Calendar}

    - */ - expire, - - /** - * Whether an entity wants to receive an XMPP message body in addition to - * the payload format. - * - *

    Value: boolean

    - */ - include_body, - - /** - * The presence states for which an entity wants to receive notifications. - * - *

    Value: {@link PresenceState}

    - */ - show_values, - - /** - * - * - *

    Value:

    - */ - subscription_type, - - /** - * - *

    Value:

    - */ - 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); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Section 16.4.2. + * + * @author Robin Collier + */ +public enum SubscribeOptionFields +{ + /** + * Whether an entity wants to receive or disable notifications + * + *

    Value: boolean

    + */ + deliver, + + /** + * Whether an entity wants to receive digests (aggregations) of + * notifications or all notifications individually. + * + *

    Value: boolean

    + */ + digest, + + /** + * The minimum number of seconds between sending any two notifications digests + * + *

    Value: int

    + */ + digest_frequency, + + /** + * + *

    Value: {@link Calendar}

    + */ + expire, + + /** + * Whether an entity wants to receive an XMPP message body in addition to + * the payload format. + * + *

    Value: boolean

    + */ + include_body, + + /** + * The presence states for which an entity wants to receive notifications. + * + *

    Value: {@link PresenceState}

    + */ + show_values, + + /** + * + * + *

    Value:

    + */ + subscription_type, + + /** + * + *

    Value:

    + */ + 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java index 7cb3adf01..d93b0c55e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java @@ -1,163 +1,163 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - return builder.toString(); - } - - private void appendAttribute(StringBuilder builder, String att, String value) - { - builder.append(" "); - builder.append(att); - builder.append("='"); - builder.append(value); - builder.append("'"); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java index 48c3ef1ea..ccfc50f2d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionEvent.java @@ -1,78 +1,78 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 subIds = Collections.emptyList(); - - /** - * 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 subscriptionIds) - { - super(nodeId); - - if (subscriptionIds != null) - subIds = subscriptionIds; - } - - /** - * Get the subscriptions this event is associated with. - * - * @return List of subscription id's - */ - public List 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 subscriptionIds) - { - if (subscriptionIds != null) - subIds = subscriptionIds; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 subIds = Collections.emptyList(); + + /** + * 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 subscriptionIds) + { + super(nodeId); + + if (subscriptionIds != null) + subIds = subscriptionIds; + } + + /** + * Get the subscriptions this event is associated with. + * + * @return List of subscription id's + */ + public List 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 subscriptionIds) + { + if (subscriptionIds != null) + subIds = subscriptionIds; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java index c375aace1..be739be2f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java @@ -1,99 +1,99 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 items = Collections.emptyList(); - - /** - * Subscriptions to the root node - * - * @param subList The list of subscriptions - */ - public SubscriptionsExtension(List 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 subList) - { - super(PubSubElementType.SUBSCRIPTIONS, nodeId); - - if (subList != null) - items = subList; - } - - /** - * Gets the list of subscriptions. - * - * @return List of subscriptions - */ - public List 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(""); - return builder.toString(); - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 items = Collections.emptyList(); + + /** + * Subscriptions to the root node + * + * @param subList The list of subscriptions + */ + public SubscriptionsExtension(List 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 subList) + { + super(PubSubElementType.SUBSCRIPTIONS, nodeId); + + if (subList != null) + items = subList; + } + + /** + * Gets the list of subscriptions. + * + * @return List of subscriptions + */ + public List 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(""); + return builder.toString(); + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java index 6fe8de345..e55852bd3 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java @@ -1,76 +1,76 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java index 9bd9aefdf..7e940d62f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemDeleteListener.java @@ -1,44 +1,44 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 all items are deleted from a node the listener is - * registered with. - */ - void handlePurge(); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 all items are deleted from a node the listener is + * registered with. + */ + void handlePurge(); +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java index f8d71f4e5..9d995bec5 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/ItemEventListener.java @@ -1,39 +1,39 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 -{ - /** - * Called whenever an item is published to the node the listener - * is registered with. - * - * @param items The publishing details. - */ - void handlePublishedItems(ItemPublishEvent items); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 +{ + /** + * Called whenever an item is published to the node the listener + * is registered with. + * + * @param items The publishing details. + */ + void handlePublishedItems(ItemPublishEvent items); +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java index 1d95a815f..55a97d05c 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/listener/NodeConfigListener.java @@ -1,38 +1,38 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java index 0e8d8efd1..4dd383ac2 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java @@ -1,109 +1,109 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Publish-Subscribe - * 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 XMPP Registrar 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: - *
    -     * <iq type='set' id="MlIpV-4" to="pubsub.gato.home" from="gato3@gato.home/Smack">
    -     *     <pubsub xmlns="http://jabber.org/protocol/pubsub">
    -     *                      :
    -     *         Specific request extension
    -     *                      :
    -     *     </pubsub>
    -     * </iq>
    -     * 
    - * - */ - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); - buf.append(getExtensionsXML()); - buf.append(""); - return buf.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Publish-Subscribe + * 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 XMPP Registrar 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: + *
    +     * <iq type='set' id="MlIpV-4" to="pubsub.gato.home" from="gato3@gato.home/Smack">
    +     *     <pubsub xmlns="http://jabber.org/protocol/pubsub">
    +     *                      :
    +     *         Specific request extension
    +     *                      :
    +     *     </pubsub>
    +     * </iq>
    +     * 
    + * + */ + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); + buf.append(getExtensionsXML()); + buf.append(""); + return buf.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java index eb9154726..492676f48 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSubNamespace.java @@ -1,66 +1,66 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java index 1dad19592..291bfdbe6 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/SyncPacketSend.java @@ -1,66 +1,66 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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()); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java index 079ef9c45..6b91d93c8 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationProvider.java @@ -1,40 +1,40 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.Affiliation; - -/** - * Parses the affiliation element out of the reply stanza from the server - * as specified in the affiliation schema. - * - * @author Robin Collier - */ -public class AffiliationProvider extends EmbeddedExtensionProvider -{ - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new Affiliation(attributeMap.get("node"), Affiliation.Type.valueOf(attributeMap.get("affiliation"))); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.Affiliation; + +/** + * Parses the affiliation element out of the reply stanza from the server + * as specified in the affiliation schema. + * + * @author Robin Collier + */ +public class AffiliationProvider extends EmbeddedExtensionProvider +{ + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new Affiliation(attributeMap.get("node"), Affiliation.Type.valueOf(attributeMap.get("affiliation"))); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java index d054bb16b..f8ca651c3 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/AffiliationsProvider.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.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 affiliation schema. - * - * @author Robin Collier - */public class AffiliationsProvider extends EmbeddedExtensionProvider -{ - @SuppressWarnings("unchecked") - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new AffiliationsExtension((List)content); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.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 affiliation schema. + * + * @author Robin Collier + */public class AffiliationsProvider extends EmbeddedExtensionProvider +{ + @SuppressWarnings("unchecked") + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new AffiliationsExtension((List)content); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java index 0e449408b..b2186dd61 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ConfigEventProvider.java @@ -1,44 +1,44 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.ConfigurationEvent; -import org.jivesoftware.smackx.pubsub.ConfigureForm; -import org.jivesoftware.smackx.xdata.packet.DataForm; - -/** - * Parses the node configuration element out of the message event stanza from - * the server as specified in the configuration schema. - * - * @author Robin Collier - */ -public class ConfigEventProvider extends EmbeddedExtensionProvider -{ - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attMap, List content) - { - if (content.size() == 0) - return new ConfigurationEvent(attMap.get("node")); - else - return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm)content.iterator().next())); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.ConfigurationEvent; +import org.jivesoftware.smackx.pubsub.ConfigureForm; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * Parses the node configuration element out of the message event stanza from + * the server as specified in the configuration schema. + * + * @author Robin Collier + */ +public class ConfigEventProvider extends EmbeddedExtensionProvider +{ + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attMap, List 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/EventProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/EventProvider.java index 31e5649c1..8536d9027 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/EventProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/EventProvider.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.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 event schema. - * - * @author Robin Collier - */ -public class EventProvider extends EmbeddedExtensionProvider -{ - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attMap, List content) - { - return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0)); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.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 event schema. + * + * @author Robin Collier + */ +public class EventProvider extends EmbeddedExtensionProvider +{ + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attMap, List content) + { + return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0)); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java index f8769d6e6..a4235d01d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/FormNodeProvider.java @@ -1,42 +1,42 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.FormNode; -import org.jivesoftware.smackx.pubsub.FormNodeType; -import org.jivesoftware.smackx.xdata.Form; -import org.jivesoftware.smackx.xdata.packet.DataForm; - -/** - * 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 attributeMap, List content) - { - return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm)content.iterator().next())); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.FormNode; +import org.jivesoftware.smackx.pubsub.FormNodeType; +import org.jivesoftware.smackx.xdata.Form; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * 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 attributeMap, List content) + { + return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm)content.iterator().next())); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java index c45bff96f..74669e9c5 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java @@ -1,117 +1,117 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 item 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 smack.providers 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; - continue; - } - 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(""); - } - } - else if (parser.getEventType() == XmlPullParser.TEXT) - { - payloadText.append(parser.getText()); - } - tag = parser.next(); - } - return new PayloadItem(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText.toString())); - } - else - { - return new PayloadItem(id, node, PacketParserUtils.parsePacketExtension(payloadElemName, payloadNS, parser)); - } - } - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 item 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 smack.providers 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; + continue; + } + 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(""); + } + } + else if (parser.getEventType() == XmlPullParser.TEXT) + { + payloadText.append(parser.getText()); + } + tag = parser.next(); + } + return new PayloadItem(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText.toString())); + } + else + { + return new PayloadItem(id, node, PacketParserUtils.parsePacketExtension(payloadElemName, payloadNS, parser)); + } + } + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java index aa81ace51..984874e86 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemsProvider.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.ItemsExtension; - -/** - * Parses the items element out of the message event stanza from - * the server as specified in the items schema. - * - * @author Robin Collier - */ -public class ItemsProvider extends EmbeddedExtensionProvider -{ - - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new ItemsExtension(ItemsExtension.ItemsElementType.items, attributeMap.get("node"), content); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.ItemsExtension; + +/** + * Parses the items element out of the message event stanza from + * the server as specified in the items schema. + * + * @author Robin Collier + */ +public class ItemsProvider extends EmbeddedExtensionProvider +{ + + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new ItemsExtension(ItemsExtension.ItemsElementType.items, attributeMap.get("node"), content); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java index 2a5fc6826..b4b565703 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/PubSubProvider.java @@ -1,65 +1,65 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java index 25175e689..a49ebe3fc 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/RetractEventProvider.java @@ -1,41 +1,41 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.RetractItem; - -/** - * Parses the retract element out of the message event stanza from - * the server as specified in the retract schema. - * This element is a child of the items element. - * - * @author Robin Collier - */ -public class RetractEventProvider extends EmbeddedExtensionProvider -{ - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new RetractItem(attributeMap.get("id")); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.RetractItem; + +/** + * Parses the retract element out of the message event stanza from + * the server as specified in the retract schema. + * This element is a child of the items element. + * + * @author Robin Collier + */ +public class RetractEventProvider extends EmbeddedExtensionProvider +{ + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new RetractItem(attributeMap.get("id")); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java index b6c19d5ff..2468a226b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SimpleNodeProvider.java @@ -1,40 +1,40 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.NodeExtension; -import org.jivesoftware.smackx.pubsub.PubSubElementType; - -/** - * Parses simple elements that only contain a node 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 attributeMap, List content) - { - return new NodeExtension(PubSubElementType.valueOfFromElemName(currentElement, currentNamespace), attributeMap.get("node")); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.NodeExtension; +import org.jivesoftware.smackx.pubsub.PubSubElementType; + +/** + * Parses simple elements that only contain a node 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 attributeMap, List content) + { + return new NodeExtension(PubSubElementType.valueOfFromElemName(currentElement, currentNamespace), attributeMap.get("node")); + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java index fb4d6bb63..2560f8af4 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionProvider.java @@ -1,55 +1,55 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 subscription element out of the pubsub IQ message from - * the server as specified in the subscription schema. - * - * @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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 subscription element out of the pubsub IQ message from + * the server as specified in the subscription schema. + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java index b6e032d69..33168bde2 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/SubscriptionsProvider.java @@ -1,42 +1,42 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.pubsub.Subscription; -import org.jivesoftware.smackx.pubsub.SubscriptionsExtension; - -/** - * Parses the subscriptions element out of the pubsub IQ message from - * the server as specified in the subscriptions schema. - * - * @author Robin Collier - */ -public class SubscriptionsProvider extends EmbeddedExtensionProvider -{ - @SuppressWarnings("unchecked") - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new SubscriptionsExtension(attributeMap.get("node"), (List)content); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.pubsub.Subscription; +import org.jivesoftware.smackx.pubsub.SubscriptionsExtension; + +/** + * Parses the subscriptions element out of the pubsub IQ message from + * the server as specified in the subscriptions schema. + * + * @author Robin Collier + */ +public class SubscriptionsProvider extends EmbeddedExtensionProvider +{ + @SuppressWarnings("unchecked") + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new SubscriptionsExtension(attributeMap.get("node"), (List)content); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/NodeUtils.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/NodeUtils.java index 01a67d2e4..319dc101e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/NodeUtils.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/NodeUtils.java @@ -1,46 +1,46 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.pubsub.ConfigureForm; -import org.jivesoftware.smackx.pubsub.FormNode; -import org.jivesoftware.smackx.pubsub.PubSubElementType; -import org.jivesoftware.smackx.xdata.Form; - -/** - * 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); - - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pubsub.ConfigureForm; +import org.jivesoftware.smackx.pubsub.FormNode; +import org.jivesoftware.smackx.pubsub.PubSubElementType; +import org.jivesoftware.smackx.xdata.Form; + +/** + * 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/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/XmlUtils.java b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/XmlUtils.java index 0ee4576b7..79d910f69 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/XmlUtils.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/pubsub/util/XmlUtils.java @@ -1,70 +1,70 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 + "'; - } - 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("'"); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 + "'; + } + 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/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java b/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java index 41deb30b5..ccee66e0d 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java @@ -1,80 +1,80 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 message delivery receipt entry as specified by - * Message Delivery Receipts. - * - * @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 ""; - } - - /** - * This Provider parses and returns DeliveryReceipt packets. - */ - public static class Provider extends EmbeddedExtensionProvider - { - - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, - Map attributeMap, List content) - { - return new DeliveryReceipt(attributeMap.get("id")); - } - - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 message delivery receipt entry as specified by + * Message Delivery Receipts. + * + * @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 ""; + } + + /** + * This Provider parses and returns DeliveryReceipt packets. + */ + public static class Provider extends EmbeddedExtensionProvider + { + + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, + Map attributeMap, List content) + { + return new DeliveryReceipt(attributeMap.get("id")); + } + + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java b/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java index e33fba443..e1c432b5a 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java @@ -1,57 +1,57 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 message delivery receipt request entry as specified by - * Message Delivery Receipts. - * - * @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 ""; - } - - /** - * This Provider parses and returns DeliveryReceiptRequest packets. - */ - public static class Provider implements PacketExtensionProvider { - @Override - public PacketExtension parseExtension(XmlPullParser parser) { - return new DeliveryReceiptRequest(); - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 message delivery receipt request entry as specified by + * Message Delivery Receipts. + * + * @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 ""; + } + + /** + * 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/extensions/src/main/java/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java b/extensions/src/main/java/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java index 78e882cea..3b3f81dbb 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/receipts/ReceiptReceivedListener.java @@ -1,26 +1,26 @@ -/** - * - * Copyright 2013 Georg Lukas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 +/** + * + * Copyright 2013 Georg Lukas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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); +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/SharedGroupManager.java b/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/SharedGroupManager.java index f1b87c1d9..c2dc5477f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/SharedGroupManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/SharedGroupManager.java @@ -14,56 +14,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.sharedgroups; - -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; +package org.jivesoftware.smackx.sharedgroups; + +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.sharedgroups.packet.SharedGroupsInfo; -import java.util.List; - -/** - * A SharedGroupManager provides services for discovering the shared groups where a user belongs.

    - * - * 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 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(); - } -} +import java.util.List; + +/** + * A SharedGroupManager provides services for discovering the shared groups where a user belongs.

    + * + * 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 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/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/packet/SharedGroupsInfo.java b/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/packet/SharedGroupsInfo.java index 1916daccc..afc21af7f 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/packet/SharedGroupsInfo.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/sharedgroups/packet/SharedGroupsInfo.java @@ -14,76 +14,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.sharedgroups.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.

    - * - * 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 groups = new ArrayList(); - - /** - * 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 getGroups() { - return groups; - } - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append(""); - for (Iterator it=groups.iterator(); it.hasNext();) { - buf.append("").append(it.next()).append(""); - } - buf.append(""); - 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.sharedgroups.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.

    + * + * 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 groups = new ArrayList(); + + /** + * 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 getGroups() { + return groups; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append(""); + for (Iterator it=groups.iterator(); it.hasNext();) { + buf.append("").append(it.next()).append(""); + } + buf.append(""); + 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/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/Header.java b/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/Header.java index 352b9431e..cf343bfca 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/Header.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/Header.java @@ -1,62 +1,62 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.shim.packet; - -import org.jivesoftware.smack.packet.PacketExtension; - -/** - * Represents a Header entry as specified by the Stanza Headers and Internet Metadata (SHIM) - - * @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 "

    " + value + "
    "; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.shim.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents a Header entry as specified by the Stanza Headers and Internet Metadata (SHIM) + + * @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 "
    " + value + "
    "; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/HeadersExtension.java b/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/HeadersExtension.java index c3982e3c1..a7d2d53ad 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/HeadersExtension.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/shim/packet/HeadersExtension.java @@ -1,71 +1,71 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.shim.packet; - -import java.util.Collection; -import java.util.Collections; - -import org.jivesoftware.smack.packet.PacketExtension; - -/** - * Extension representing a list of headers as specified in Stanza Headers and Internet Metadata (SHIM) - * - * @see Header - * - * @author Robin Collier - */ -public class HeadersExtension implements PacketExtension -{ - public static final String NAMESPACE = "http://jabber.org/protocol/shim"; - - private Collection
    headers = Collections.emptyList(); - - public HeadersExtension(Collection
    headerList) - { - if (headerList != null) - headers = headerList; - } - - public Collection
    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("'); - - return builder.toString(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.shim.packet; + +import java.util.Collection; +import java.util.Collections; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Extension representing a list of headers as specified in Stanza Headers and Internet Metadata (SHIM) + * + * @see Header + * + * @author Robin Collier + */ +public class HeadersExtension implements PacketExtension +{ + public static final String NAMESPACE = "http://jabber.org/protocol/shim"; + + private Collection
    headers = Collections.emptyList(); + + public HeadersExtension(Collection
    headerList) + { + if (headerList != null) + headers = headerList; + } + + public Collection
    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("'); + + return builder.toString(); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeaderProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeaderProvider.java index f759da164..75bc154c0 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeaderProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeaderProvider.java @@ -1,47 +1,47 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.shim.provider; - -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.provider.PacketExtensionProvider; -import org.jivesoftware.smackx.shim.packet.Header; -import org.xmlpull.v1.XmlPullParser; - -/** - * Parses the header element as defined in Stanza Headers and Internet Metadata (SHIM). - * - * @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); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.shim.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smackx.shim.packet.Header; +import org.xmlpull.v1.XmlPullParser; + +/** + * Parses the header element as defined in Stanza Headers and Internet Metadata (SHIM). + * + * @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/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeadersProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeadersProvider.java index 3e98f1461..30d270ec4 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeadersProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/shim/provider/HeadersProvider.java @@ -1,42 +1,42 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.shim.provider; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.provider.EmbeddedExtensionProvider; -import org.jivesoftware.smackx.shim.packet.Header; -import org.jivesoftware.smackx.shim.packet.HeadersExtension; - -/** - * Parses the headers element as defined in Stanza Headers and Internet Metadata (SHIM). - * - * @author Robin Collier - */ -public class HeadersProvider extends EmbeddedExtensionProvider -{ - @SuppressWarnings("unchecked") - @Override - protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) - { - return new HeadersExtension((Collection
    )content); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.shim.provider; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smackx.shim.packet.Header; +import org.jivesoftware.smackx.shim.packet.HeadersExtension; + +/** + * Parses the headers element as defined in Stanza Headers and Internet Metadata (SHIM). + * + * @author Robin Collier + */ +public class HeadersProvider extends EmbeddedExtensionProvider +{ + @SuppressWarnings("unchecked") + @Override + protected PacketExtension createReturnExtension(String currentElement, String currentNamespace, Map attributeMap, List content) + { + return new HeadersExtension((Collection
    )content); + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/si/packet/StreamInitiation.java b/extensions/src/main/java/org/jivesoftware/smackx/si/packet/StreamInitiation.java index 4b309b1cd..da5afc54e 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/si/packet/StreamInitiation.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/si/packet/StreamInitiation.java @@ -1,417 +1,417 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.si.packet; - -import java.util.Date; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.xdata.packet.DataForm; - -/** - * 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 "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 setSessionID(final String id) { - this.id = id; - } - - /** - * Uniquely identifies a stream initiation to the recipient. - * - * @return The "id" attribute. - * @see #setSessionID(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 ). 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(""); - - // 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(""); - } - else { - throw new IllegalArgumentException("IQ Type not understood"); - } - if (featureNegotiation != null) { - buf.append(featureNegotiation.toXML()); - } - buf.append(""); - return buf.toString(); - } - - /** - *
      - *
    • size: The size, in bytes, of the data to be sent.
    • - *
    • name: The name of the file that the Sender wishes to send.
    • - *
    • date: The last modification time of the file. This is specified - * using the DateTime profile as described in Jabber Date and Time Profiles.
    • - *
    • hash: The MD5 sum of the file contents.
    • - *
    - *

    - *

    - * <desc> 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. - *

    - *

    - * When <range> 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 element, it uses these - * attributes: - *

    - *

      - *
    • offset: Specifies the position, in bytes, to start transferring the - * file data from. This defaults to zero (0) if not specified.
    • - *
    • length - Specifies the number of bytes to retrieve starting at - * offset. This defaults to the length of the file from offset to the end.
    • - *
    - *

    - *

    - * Both attributes are OPTIONAL on the <range> element. Sending no - * attributes is synonymous with not sending the <range> element. When - * no <range> 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("").append(StringUtils.escapeForXML(getDesc())).append(""); - } - if (isRanged()) { - buffer.append(""); - } - buffer.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(""); - buf.append(data.toXML()); - buf.append(""); - return buf.toString(); - } - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.si.packet; + +import java.util.Date; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * 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 "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 setSessionID(final String id) { + this.id = id; + } + + /** + * Uniquely identifies a stream initiation to the recipient. + * + * @return The "id" attribute. + * @see #setSessionID(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 ). 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(""); + + // 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(""); + } + else { + throw new IllegalArgumentException("IQ Type not understood"); + } + if (featureNegotiation != null) { + buf.append(featureNegotiation.toXML()); + } + buf.append(""); + return buf.toString(); + } + + /** + *

      + *
    • size: The size, in bytes, of the data to be sent.
    • + *
    • name: The name of the file that the Sender wishes to send.
    • + *
    • date: The last modification time of the file. This is specified + * using the DateTime profile as described in Jabber Date and Time Profiles.
    • + *
    • hash: The MD5 sum of the file contents.
    • + *
    + *

    + *

    + * <desc> 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. + *

    + *

    + * When <range> 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 element, it uses these + * attributes: + *

    + *

      + *
    • offset: Specifies the position, in bytes, to start transferring the + * file data from. This defaults to zero (0) if not specified.
    • + *
    • length - Specifies the number of bytes to retrieve starting at + * offset. This defaults to the length of the file from offset to the end.
    • + *
    + *

    + *

    + * Both attributes are OPTIONAL on the <range> element. Sending no + * attributes is synonymous with not sending the <range> element. When + * no <range> 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("").append(StringUtils.escapeForXML(getDesc())).append(""); + } + if (isRanged()) { + buffer.append(""); + } + buffer.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(""); + buf.append(data.toXML()); + buf.append(""); + return buf.toString(); + } + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java index f1c6d282f..c4d3180f9 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java @@ -1,125 +1,125 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.si.provider; - -import java.text.ParseException; -import java.util.Date; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.provider.IQProvider; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.si.packet.StreamInitiation; -import org.jivesoftware.smackx.si.packet.StreamInitiation.File; -import org.jivesoftware.smackx.xdata.packet.DataForm; -import org.jivesoftware.smackx.xdata.provider.DataFormProvider; -import org.xmlpull.v1.XmlPullParser; - -/** - * The StreamInitiationProvider parses StreamInitiation packets. - * - * @author Alexander Wenckus - * - */ -public class StreamInitiationProvider implements IQProvider { - private static Logger log = Logger.getLogger(StreamInitiationProvider.class.getName()); - - 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) { - log.log(Level.SEVERE, "Failed to parse file size from " + fileSize, e); - } - } - - Date fileDate = new Date(); - if (date != null) { - try { - fileDate = StringUtils.parseDate(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.setSessionID(id); - initiation.setMimeType(mimeType); - - initiation.setFeatureNegotiationForm(form); - - return initiation; - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.si.provider; + +import java.text.ParseException; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.si.packet.StreamInitiation; +import org.jivesoftware.smackx.si.packet.StreamInitiation.File; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jivesoftware.smackx.xdata.provider.DataFormProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * The StreamInitiationProvider parses StreamInitiation packets. + * + * @author Alexander Wenckus + * + */ +public class StreamInitiationProvider implements IQProvider { + private static Logger log = Logger.getLogger(StreamInitiationProvider.class.getName()); + + 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) { + log.log(Level.SEVERE, "Failed to parse file size from " + fileSize, e); + } + } + + Date fileDate = new Date(); + if (date != null) { + try { + fileDate = StringUtils.parseDate(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.setSessionID(id); + initiation.setMimeType(mimeType); + + initiation.setFeatureNegotiationForm(form); + + return initiation; + } + +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java b/extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java index 06b1cc644..f844c4551 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java @@ -195,4 +195,4 @@ public class Time extends IQ { buf.append(""); return buf.toString(); } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/xevent/MessageEventManager.java b/extensions/src/main/java/org/jivesoftware/smackx/xevent/MessageEventManager.java index fe704d01f..2d9953a9b 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/xevent/MessageEventManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/xevent/MessageEventManager.java @@ -298,4 +298,4 @@ public class MessageEventManager { destroy(); super.finalize(); } -} \ No newline at end of file +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java b/extensions/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java index aae0b6a83..fa71b03fe 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java @@ -181,4 +181,4 @@ public class RosterExchangeManager { destroy(); super.finalize(); } -} \ No newline at end of file +} diff --git a/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers index 2d2e60d6f..054dd5381 100644 --- a/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers +++ b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers @@ -1,459 +1,459 @@ - - - - - - - query - jabber:iq:private - org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider - - - - - query - jabber:iq:time - org.jivesoftware.smackx.time.packet.Time - - - - - x - jabber:x:roster - org.jivesoftware.smackx.xroster.provider.RosterExchangeProvider - - - - - x - jabber:x:event - org.jivesoftware.smackx.xevent.provider.MessageEventProvider - - - - - active - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider - - - - composing - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider - - - - paused - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider - - - - inactive - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider - - - - gone - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider - - - - - html - http://jabber.org/protocol/xhtml-im - org.jivesoftware.smackx.xhtmlim.provider.XHTMLExtensionProvider - - - - - x - jabber:x:conference - org.jivesoftware.smackx.muc.packet.GroupChatInvitation$Provider - - - - - query - http://jabber.org/protocol/disco#items - org.jivesoftware.smackx.disco.provider.DiscoverItemsProvider - - - - - query - http://jabber.org/protocol/disco#info - org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider - - - - - x - jabber:x:data - org.jivesoftware.smackx.xdata.provider.DataFormProvider - - - - - x - http://jabber.org/protocol/muc#user - org.jivesoftware.smackx.muc.provider.MUCUserProvider - - - - - query - http://jabber.org/protocol/muc#admin - org.jivesoftware.smackx.muc.provider.MUCAdminProvider - - - - - query - http://jabber.org/protocol/muc#owner - org.jivesoftware.smackx.muc.provider.MUCOwnerProvider - - - - - x - jabber:x:delay - org.jivesoftware.smackx.delay.provider.DelayInformationProvider - - - - delay - urn:xmpp:delay - org.jivesoftware.smackx.delay.provider.DelayInfoProvider - - - - - query - jabber:iq:version - org.jivesoftware.smackx.iqversion.packet.Version - - - - - vCard - vcard-temp - org.jivesoftware.smackx.vcardtemp.provider.VCardProvider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.offline.packet.OfflineMessageRequest$Provider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.offline.packet.OfflineMessageInfo$Provider - - - - - query - jabber:iq:last - org.jivesoftware.smackx.iqlast.packet.LastActivity$Provider - - - - - query - jabber:iq:search - org.jivesoftware.smackx.search.UserSearch$Provider - - - - - sharedgroup - http://www.jivesoftware.org/protocol/sharedgroup - org.jivesoftware.smackx.sharedgroups.packet.SharedGroupsInfo$Provider - - - - - addresses - http://jabber.org/protocol/address - org.jivesoftware.smackx.address.provider.MultipleAddressesProvider - - - - - si - http://jabber.org/protocol/si - org.jivesoftware.smackx.si.provider.StreamInitiationProvider - - - - query - http://jabber.org/protocol/bytestreams - org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider - - - - open - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - close - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - - command - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider - - - - bad-action - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadActionError - - - - malformed-actionn - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$MalformedActionError - - - - bad-locale - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadLocaleError - - - - bad-payload - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadPayloadError - - - - bad-sessionid - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadSessionIDError - - - - session-expired - http://jabber.org/protocol/commands - org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$SessionExpiredError - - - - - headers - http://jabber.org/protocol/shim - org.jivesoftware.smackx.shim.provider.HeadersProvider - - - - header - http://jabber.org/protocol/shim - org.jivesoftware.smackx.shim.provider.HeaderProvider - - - - - pubsub - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - create - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - items - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - subscriptions - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider - - - - subscription - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider - - - - affiliations - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider - - - - affiliation - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationProvider - - - - options - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - pubsub - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - configure - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - default - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - event - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.EventProvider - - - - configuration - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider - - - - delete - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - options - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - items - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - retract - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.RetractEventProvider - - - - purge - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - - nick - http://jabber.org/protocol/nick - org.jivesoftware.smackx.nick.packet.Nick$Provider - - - - - attention - urn:xmpp:attention:0 - org.jivesoftware.smackx.attention.packet.AttentionExtension$Provider - - - - - received - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider - - - request - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider - - - - - c - http://jabber.org/protocol/caps - org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider - - - - - forwarded - urn:xmpp:forward:0 - org.jivesoftware.smackx.forward.provider.ForwardedProvider - - - - - ping - urn:xmpp:ping - org.jivesoftware.smackx.ping.provider.PingProvider - - - - - query - jabber:iq:privacy - org.jivesoftware.smackx.privacy.provider.PrivacyProvider - - - + + + + + + + query + jabber:iq:private + org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider + + + + + query + jabber:iq:time + org.jivesoftware.smackx.time.packet.Time + + + + + x + jabber:x:roster + org.jivesoftware.smackx.xroster.provider.RosterExchangeProvider + + + + + x + jabber:x:event + org.jivesoftware.smackx.xevent.provider.MessageEventProvider + + + + + active + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider + + + + composing + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider + + + + paused + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider + + + + inactive + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider + + + + gone + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.chatstates.packet.ChatStateExtension$Provider + + + + + html + http://jabber.org/protocol/xhtml-im + org.jivesoftware.smackx.xhtmlim.provider.XHTMLExtensionProvider + + + + + x + jabber:x:conference + org.jivesoftware.smackx.muc.packet.GroupChatInvitation$Provider + + + + + query + http://jabber.org/protocol/disco#items + org.jivesoftware.smackx.disco.provider.DiscoverItemsProvider + + + + + query + http://jabber.org/protocol/disco#info + org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider + + + + + x + jabber:x:data + org.jivesoftware.smackx.xdata.provider.DataFormProvider + + + + + x + http://jabber.org/protocol/muc#user + org.jivesoftware.smackx.muc.provider.MUCUserProvider + + + + + query + http://jabber.org/protocol/muc#admin + org.jivesoftware.smackx.muc.provider.MUCAdminProvider + + + + + query + http://jabber.org/protocol/muc#owner + org.jivesoftware.smackx.muc.provider.MUCOwnerProvider + + + + + x + jabber:x:delay + org.jivesoftware.smackx.delay.provider.DelayInformationProvider + + + + delay + urn:xmpp:delay + org.jivesoftware.smackx.delay.provider.DelayInfoProvider + + + + + query + jabber:iq:version + org.jivesoftware.smackx.iqversion.packet.Version + + + + + vCard + vcard-temp + org.jivesoftware.smackx.vcardtemp.provider.VCardProvider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.offline.packet.OfflineMessageRequest$Provider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.offline.packet.OfflineMessageInfo$Provider + + + + + query + jabber:iq:last + org.jivesoftware.smackx.iqlast.packet.LastActivity$Provider + + + + + query + jabber:iq:search + org.jivesoftware.smackx.search.UserSearch$Provider + + + + + sharedgroup + http://www.jivesoftware.org/protocol/sharedgroup + org.jivesoftware.smackx.sharedgroups.packet.SharedGroupsInfo$Provider + + + + + addresses + http://jabber.org/protocol/address + org.jivesoftware.smackx.address.provider.MultipleAddressesProvider + + + + + si + http://jabber.org/protocol/si + org.jivesoftware.smackx.si.provider.StreamInitiationProvider + + + + query + http://jabber.org/protocol/bytestreams + org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider + + + + open + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + close + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + + command + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider + + + + bad-action + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadActionError + + + + malformed-actionn + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$MalformedActionError + + + + bad-locale + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadLocaleError + + + + bad-payload + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadPayloadError + + + + bad-sessionid + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadSessionIDError + + + + session-expired + http://jabber.org/protocol/commands + org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$SessionExpiredError + + + + + headers + http://jabber.org/protocol/shim + org.jivesoftware.smackx.shim.provider.HeadersProvider + + + + header + http://jabber.org/protocol/shim + org.jivesoftware.smackx.shim.provider.HeaderProvider + + + + + pubsub + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + create + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + items + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + subscriptions + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider + + + + subscription + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider + + + + affiliations + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider + + + + affiliation + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationProvider + + + + options + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + pubsub + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + configure + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + default + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + event + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.EventProvider + + + + configuration + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider + + + + delete + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + options + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + items + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + retract + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.RetractEventProvider + + + + purge + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + + nick + http://jabber.org/protocol/nick + org.jivesoftware.smackx.nick.packet.Nick$Provider + + + + + attention + urn:xmpp:attention:0 + org.jivesoftware.smackx.attention.packet.AttentionExtension$Provider + + + + + received + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider + + + request + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider + + + + + c + http://jabber.org/protocol/caps + org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider + + + + + forwarded + urn:xmpp:forward:0 + org.jivesoftware.smackx.forward.provider.ForwardedProvider + + + + + ping + urn:xmpp:ping + org.jivesoftware.smackx.ping.provider.PingProvider + + + + + query + jabber:iq:privacy + org.jivesoftware.smackx.privacy.provider.PrivacyProvider + + + diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java index ec2941f37..6ef55f89a 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java @@ -1,82 +1,82 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.bytestreams.ibb.CloseListener; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Close; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.powermock.reflect.Whitebox; - -/** - * Test for the CloseListener class. - * - * @author Henning Staib - */ -public class CloseListenerTest { - - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - - /** - * If a close request to an unknown session is received it should be replied - * with an <item-not-found/> error. - * - * @throws Exception should not happen - */ - @Test - public void shouldReplyErrorIfSessionIsUnknown() throws Exception { - - // mock connection - Connection connection = mock(Connection.class); - - // initialize InBandBytestreamManager to get the CloseListener - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // get the CloseListener from InBandByteStreamManager - CloseListener closeListener = Whitebox.getInternalState(byteStreamManager, - CloseListener.class); - - Close close = new Close("unknownSessionId"); - close.setFrom(initiatorJID); - close.setTo(targetJID); - - closeListener.processPacket(close); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream close request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - argument.getValue().getError().getCondition()); - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.ibb.CloseListener; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Close; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.powermock.reflect.Whitebox; + +/** + * Test for the CloseListener class. + * + * @author Henning Staib + */ +public class CloseListenerTest { + + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + + /** + * If a close request to an unknown session is received it should be replied + * with an <item-not-found/> error. + * + * @throws Exception should not happen + */ + @Test + public void shouldReplyErrorIfSessionIsUnknown() throws Exception { + + // mock connection + Connection connection = mock(Connection.class); + + // initialize InBandBytestreamManager to get the CloseListener + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // get the CloseListener from InBandByteStreamManager + CloseListener closeListener = Whitebox.getInternalState(byteStreamManager, + CloseListener.class); + + Close close = new Close("unknownSessionId"); + close.setFrom(initiatorJID); + close.setTo(targetJID); + + closeListener.processPacket(close); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // capture reply to the In-Band Bytestream close request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + argument.getValue().getError().getCondition()); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java index 2275090cb..1f3930941 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java @@ -1,84 +1,84 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.bytestreams.ibb.DataListener; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.powermock.reflect.Whitebox; - -/** - * Test for the CloseListener class. - * - * @author Henning Staib - */ -public class DataListenerTest { - - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - - /** - * If a data packet of an unknown session is received it should be replied - * with an <item-not-found/> error. - * - * @throws Exception should not happen - */ - @Test - public void shouldReplyErrorIfSessionIsUnknown() throws Exception { - - // mock connection - Connection connection = mock(Connection.class); - - // initialize InBandBytestreamManager to get the DataListener - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // get the DataListener from InBandByteStreamManager - DataListener dataListener = Whitebox.getInternalState(byteStreamManager, - DataListener.class); - - DataPacketExtension dpe = new DataPacketExtension("unknownSessionID", 0, "Data"); - Data data = new Data(dpe); - data.setFrom(initiatorJID); - data.setTo(targetJID); - - dataListener.processPacket(data); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream close request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - argument.getValue().getError().getCondition()); - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.ibb.DataListener; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.powermock.reflect.Whitebox; + +/** + * Test for the CloseListener class. + * + * @author Henning Staib + */ +public class DataListenerTest { + + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + + /** + * If a data packet of an unknown session is received it should be replied + * with an <item-not-found/> error. + * + * @throws Exception should not happen + */ + @Test + public void shouldReplyErrorIfSessionIsUnknown() throws Exception { + + // mock connection + Connection connection = mock(Connection.class); + + // initialize InBandBytestreamManager to get the DataListener + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // get the DataListener from InBandByteStreamManager + DataListener dataListener = Whitebox.getInternalState(byteStreamManager, + DataListener.class); + + DataPacketExtension dpe = new DataPacketExtension("unknownSessionID", 0, "Data"); + Data data = new Data(dpe); + data.setFrom(initiatorJID); + data.setTo(targetJID); + + dataListener.processPacket(data); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // capture reply to the In-Band Bytestream close request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + argument.getValue().getError().getCondition()); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBPacketUtils.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBPacketUtils.java index 00a5000cd..49ef5d56e 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBPacketUtils.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBPacketUtils.java @@ -14,60 +14,60 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.bytestreams.ibb; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; - -/** - * Utility methods to create packets. - * - * @author Henning Staib - */ -public class IBBPacketUtils { - - /** - * Returns an error IQ. - * - * @param from the senders JID - * @param to the recipients JID - * @param xmppError the XMPP error - * @return an error IQ - */ - public static IQ createErrorIQ(String from, String to, XMPPError xmppError) { - IQ errorIQ = new IQ() { - - public String getChildElementXML() { - return null; - } - - }; - errorIQ.setType(IQ.Type.ERROR); - errorIQ.setFrom(from); - errorIQ.setTo(to); - errorIQ.setError(xmppError); - return errorIQ; - } - - /** - * Returns a result IQ. - * - * @param from the senders JID - * @param to the recipients JID - * @return a result IQ - */ - public static IQ createResultIQ(String from, String to) { - IQ result = new IQ() { - - public String getChildElementXML() { - return null; - } - - }; - result.setType(IQ.Type.RESULT); - result.setFrom(from); - result.setTo(to); - return result; - } - -} +package org.jivesoftware.smackx.bytestreams.ibb; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; + +/** + * Utility methods to create packets. + * + * @author Henning Staib + */ +public class IBBPacketUtils { + + /** + * Returns an error IQ. + * + * @param from the senders JID + * @param to the recipients JID + * @param xmppError the XMPP error + * @return an error IQ + */ + public static IQ createErrorIQ(String from, String to, XMPPError xmppError) { + IQ errorIQ = new IQ() { + + public String getChildElementXML() { + return null; + } + + }; + errorIQ.setType(IQ.Type.ERROR); + errorIQ.setFrom(from); + errorIQ.setTo(to); + errorIQ.setError(xmppError); + return errorIQ; + } + + /** + * Returns a result IQ. + * + * @param from the senders JID + * @param to the recipients JID + * @return a result IQ + */ + public static IQ createResultIQ(String from, String to) { + IQ result = new IQ() { + + public String getChildElementXML() { + return null; + } + + }; + result.setType(IQ.Type.RESULT); + result.setFrom(from); + result.setTo(to); + return result; + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBTestsSuite.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBTestsSuite.java index b8061eb5b..86b2d366d 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBTestsSuite.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBTestsSuite.java @@ -14,24 +14,24 @@ * 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.ibb.packet.CloseTest; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtensionTest; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataTest; -import org.jivesoftware.smackx.bytestreams.ibb.packet.OpenTest; -import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProviderTest; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses( { CloseTest.class, DataPacketExtensionTest.class, DataTest.class, - OpenTest.class, OpenIQProviderTest.class, CloseListenerTest.class, - DataListenerTest.class, InBandBytestreamManagerTest.class, - InBandBytestreamRequestTest.class, - InBandBytestreamSessionMessageTest.class, - InBandBytestreamSessionTest.class, InitiationListenerTest.class }) -public class IBBTestsSuite { - // the class remains completely empty, - // being used only as a holder for the above annotations -} +package org.jivesoftware.smackx.bytestreams.ibb; + +import org.jivesoftware.smackx.bytestreams.ibb.packet.CloseTest; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtensionTest; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataTest; +import org.jivesoftware.smackx.bytestreams.ibb.packet.OpenTest; +import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProviderTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses( { CloseTest.class, DataPacketExtensionTest.class, DataTest.class, + OpenTest.class, OpenIQProviderTest.class, CloseListenerTest.class, + DataListenerTest.class, InBandBytestreamManagerTest.class, + InBandBytestreamRequestTest.class, + InBandBytestreamSessionMessageTest.class, + InBandBytestreamSessionTest.class, InitiationListenerTest.class }) +public class IBBTestsSuite { + // the class remains completely empty, + // being used only as a holder for the above annotations +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java index 1571aea37..551357304 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java @@ -1,190 +1,190 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.jivesoftware.util.Verification; -import org.junit.Before; -import org.junit.Test; - -/** - * Test for InBandBytestreamManager. - * - * @author Henning Staib - */ -public class InBandBytestreamManagerTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String sessionID = "session_id"; - - // protocol verifier - Protocol protocol; - - // mocked XMPP connection - Connection connection; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, - xmppServer); - - } - - /** - * Test that - * {@link InBandBytestreamManager#getByteStreamManager(Connection)} returns - * one bytestream manager for every connection - */ - @Test - public void shouldHaveOneManagerForEveryConnection() { - - // mock two connections - Connection connection1 = mock(Connection.class); - Connection connection2 = mock(Connection.class); - - // get bytestream manager for the first connection twice - InBandBytestreamManager conn1ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection1); - InBandBytestreamManager conn1ByteStreamManager2 = InBandBytestreamManager.getByteStreamManager(connection1); - - // get bytestream manager for second connection - InBandBytestreamManager conn2ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection2); - - // assertions - assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2); - assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1); - - } - - /** - * Invoking {@link InBandBytestreamManager#establishSession(String)} should - * throw an exception if the given target does not support in-band - * bytestream. - */ - @Test - public void shouldFailIfTargetDoesNotSupportIBB() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - try { - XMPPError xmppError = new XMPPError( - XMPPError.Condition.feature_not_implemented); - IQ errorIQ = IBBPacketUtils.createErrorIQ(targetJID, initiatorJID, xmppError); - protocol.addResponse(errorIQ); - - // start In-Band Bytestream - byteStreamManager.establishSession(targetJID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertEquals(XMPPError.Condition.feature_not_implemented.toString(), - e.getXMPPError().getCondition()); - } - - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowTooBigDefaultBlockSize() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - byteStreamManager.setDefaultBlockSize(1000000); - } - - @Test - public void shouldCorrectlySetDefaultBlockSize() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - byteStreamManager.setDefaultBlockSize(1024); - assertEquals(1024, byteStreamManager.getDefaultBlockSize()); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowTooBigMaximumBlockSize() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - byteStreamManager.setMaximumBlockSize(1000000); - } - - @Test - public void shouldCorrectlySetMaximumBlockSize() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - byteStreamManager.setMaximumBlockSize(1024); - assertEquals(1024, byteStreamManager.getMaximumBlockSize()); - } - - @Test - public void shouldUseConfiguredStanzaType() { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - byteStreamManager.setStanza(StanzaType.MESSAGE); - - protocol.addResponse(null, new Verification() { - - public void verify(Open request, IQ response) { - assertEquals(StanzaType.MESSAGE, request.getStanza()); - } - - }); - - try { - // start In-Band Bytestream - byteStreamManager.establishSession(targetJID); - } - catch (XMPPException e) { - protocol.verifyAll(); - } - - } - - @Test - public void shouldReturnSession() throws Exception { - InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - IQ success = IBBPacketUtils.createResultIQ(targetJID, initiatorJID); - protocol.addResponse(success, Verification.correspondingSenderReceiver, - Verification.requestTypeSET); - - // start In-Band Bytestream - InBandBytestreamSession session = byteStreamManager.establishSession(targetJID); - - assertNotNull(session); - assertNotNull(session.getInputStream()); - assertNotNull(session.getOutputStream()); - - protocol.verifyAll(); - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.jivesoftware.util.Verification; +import org.junit.Before; +import org.junit.Test; + +/** + * Test for InBandBytestreamManager. + * + * @author Henning Staib + */ +public class InBandBytestreamManagerTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String sessionID = "session_id"; + + // protocol verifier + Protocol protocol; + + // mocked XMPP connection + Connection connection; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, + xmppServer); + + } + + /** + * Test that + * {@link InBandBytestreamManager#getByteStreamManager(Connection)} returns + * one bytestream manager for every connection + */ + @Test + public void shouldHaveOneManagerForEveryConnection() { + + // mock two connections + Connection connection1 = mock(Connection.class); + Connection connection2 = mock(Connection.class); + + // get bytestream manager for the first connection twice + InBandBytestreamManager conn1ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection1); + InBandBytestreamManager conn1ByteStreamManager2 = InBandBytestreamManager.getByteStreamManager(connection1); + + // get bytestream manager for second connection + InBandBytestreamManager conn2ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection2); + + // assertions + assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2); + assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1); + + } + + /** + * Invoking {@link InBandBytestreamManager#establishSession(String)} should + * throw an exception if the given target does not support in-band + * bytestream. + */ + @Test + public void shouldFailIfTargetDoesNotSupportIBB() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + try { + XMPPError xmppError = new XMPPError( + XMPPError.Condition.feature_not_implemented); + IQ errorIQ = IBBPacketUtils.createErrorIQ(targetJID, initiatorJID, xmppError); + protocol.addResponse(errorIQ); + + // start In-Band Bytestream + byteStreamManager.establishSession(targetJID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertEquals(XMPPError.Condition.feature_not_implemented.toString(), + e.getXMPPError().getCondition()); + } + + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotAllowTooBigDefaultBlockSize() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + byteStreamManager.setDefaultBlockSize(1000000); + } + + @Test + public void shouldCorrectlySetDefaultBlockSize() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + byteStreamManager.setDefaultBlockSize(1024); + assertEquals(1024, byteStreamManager.getDefaultBlockSize()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotAllowTooBigMaximumBlockSize() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + byteStreamManager.setMaximumBlockSize(1000000); + } + + @Test + public void shouldCorrectlySetMaximumBlockSize() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + byteStreamManager.setMaximumBlockSize(1024); + assertEquals(1024, byteStreamManager.getMaximumBlockSize()); + } + + @Test + public void shouldUseConfiguredStanzaType() { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + byteStreamManager.setStanza(StanzaType.MESSAGE); + + protocol.addResponse(null, new Verification() { + + public void verify(Open request, IQ response) { + assertEquals(StanzaType.MESSAGE, request.getStanza()); + } + + }); + + try { + // start In-Band Bytestream + byteStreamManager.establishSession(targetJID); + } + catch (XMPPException e) { + protocol.verifyAll(); + } + + } + + @Test + public void shouldReturnSession() throws Exception { + InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + IQ success = IBBPacketUtils.createResultIQ(targetJID, initiatorJID); + protocol.addResponse(success, Verification.correspondingSenderReceiver, + Verification.requestTypeSET); + + // start In-Band Bytestream + InBandBytestreamSession session = byteStreamManager.establishSession(targetJID); + + assertNotNull(session); + assertNotNull(session.getInputStream()); + assertNotNull(session.getOutputStream()); + + protocol.verifyAll(); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java index 089000e04..b57b35f45 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java @@ -14,104 +14,104 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.bytestreams.ibb; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -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.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -/** - * Test for InBandBytestreamRequest. - * - * @author Henning Staib - */ -public class InBandBytestreamRequestTest { - - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String sessionID = "session_id"; - - Connection connection; - InBandBytestreamManager byteStreamManager; - Open initBytestream; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // mock connection - connection = mock(Connection.class); - - // initialize InBandBytestreamManager to get the InitiationListener - byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // create a In-Band Bytestream open packet - initBytestream = new Open(sessionID, 4096); - initBytestream.setFrom(initiatorJID); - initBytestream.setTo(targetJID); - - } - - /** - * Test reject() method. - */ - @Test - public void shouldReplyWithErrorIfRequestIsRejected() { - InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest( - byteStreamManager, initBytestream); - - // reject request - ibbRequest.reject(); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), - argument.getValue().getError().getCondition()); - - } - - /** - * Test accept() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldReturnSessionIfRequestIsAccepted() throws Exception { - InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest( - byteStreamManager, initBytestream); - - // accept request - InBandBytestreamSession session = ibbRequest.accept(); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct acknowledgment packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.RESULT, argument.getValue().getType()); - - assertNotNull(session); - assertNotNull(session.getInputStream()); - assertNotNull(session.getOutputStream()); - - } - -} +package org.jivesoftware.smackx.bytestreams.ibb; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +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.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +/** + * Test for InBandBytestreamRequest. + * + * @author Henning Staib + */ +public class InBandBytestreamRequestTest { + + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String sessionID = "session_id"; + + Connection connection; + InBandBytestreamManager byteStreamManager; + Open initBytestream; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // mock connection + connection = mock(Connection.class); + + // initialize InBandBytestreamManager to get the InitiationListener + byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // create a In-Band Bytestream open packet + initBytestream = new Open(sessionID, 4096); + initBytestream.setFrom(initiatorJID); + initBytestream.setTo(targetJID); + + } + + /** + * Test reject() method. + */ + @Test + public void shouldReplyWithErrorIfRequestIsRejected() { + InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest( + byteStreamManager, initBytestream); + + // reject request + ibbRequest.reject(); + + // capture reply to the In-Band Bytestream open request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), + argument.getValue().getError().getCondition()); + + } + + /** + * Test accept() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldReturnSessionIfRequestIsAccepted() throws Exception { + InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest( + byteStreamManager, initBytestream); + + // accept request + InBandBytestreamSession session = ibbRequest.accept(); + + // capture reply to the In-Band Bytestream open request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct acknowledgment packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.RESULT, argument.getValue().getType()); + + assertNotNull(session); + assertNotNull(session.getInputStream()); + assertNotNull(session.getOutputStream()); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java index 0e9026eb9..b1cbb3758 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java @@ -14,359 +14,359 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.bytestreams.ibb; - -import static org.junit.Assert.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Random; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.PacketListener; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.jivesoftware.util.Verification; -import org.junit.Before; -import org.junit.Test; -import org.powermock.reflect.Whitebox; - -/** - * Test for InBandBytestreamSession. - *

    - * Tests sending data encapsulated in message stanzas. - * - * @author Henning Staib - */ -public class InBandBytestreamSessionMessageTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String sessionID = "session_id"; - - int blockSize = 10; - - // protocol verifier - Protocol protocol; - - // mocked XMPP connection - Connection connection; - - InBandBytestreamManager byteStreamManager; - - Open initBytestream; - - Verification incrementingSequence; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); - - // initialize InBandBytestreamManager to get the InitiationListener - byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // create a In-Band Bytestream open packet with message stanza - initBytestream = new Open(sessionID, blockSize, StanzaType.MESSAGE); - initBytestream.setFrom(initiatorJID); - initBytestream.setTo(targetJID); - - incrementingSequence = new Verification() { - - long lastSeq = 0; - - public void verify(Message request, IQ response) { - DataPacketExtension dpe = (DataPacketExtension) request.getExtension( - DataPacketExtension.ELEMENT_NAME, InBandBytestreamManager.NAMESPACE); - assertEquals(lastSeq++, dpe.getSeq()); - } - - }; - - } - - /** - * Test the output stream write(byte[]) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets1() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // verify the data packets - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - - byte[] controlData = new byte[blockSize * 3]; - - OutputStream outputStream = session.getOutputStream(); - outputStream.write(controlData); - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream write(byte) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets2() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // verify the data packets - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - - byte[] controlData = new byte[blockSize * 3]; - - OutputStream outputStream = session.getOutputStream(); - for (byte b : controlData) { - outputStream.write(b); - } - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream write(byte[], int, int) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets3() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // verify the data packets - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - - byte[] controlData = new byte[(blockSize * 3) - 2]; - - OutputStream outputStream = session.getOutputStream(); - int off = 0; - for (int i = 1; i <= 7; i++) { - outputStream.write(controlData, off, i); - off += i; - } - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream flush() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThirtyDataPackets() throws Exception { - byte[] controlData = new byte[blockSize * 3]; - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // verify the data packets - for (int i = 0; i < controlData.length; i++) { - protocol.addResponse(null, incrementingSequence); - } - - OutputStream outputStream = session.getOutputStream(); - for (byte b : controlData) { - outputStream.write(b); - outputStream.flush(); - } - - protocol.verifyAll(); - - } - - /** - * Test successive calls to the output stream flush() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { - byte[] controlData = new byte[blockSize * 3]; - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // verify the data packets - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - protocol.addResponse(null, incrementingSequence); - - OutputStream outputStream = session.getOutputStream(); - outputStream.write(controlData); - - outputStream.flush(); - outputStream.flush(); - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * If a data packet is received out of order the session should be closed. See XEP-0047 Section - * 2.2. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { - // confirm close request - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ, Verification.requestTypeSET, - Verification.correspondingSenderReceiver); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build invalid packet with out of order sequence - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data); - Message dataMessage = new Message(); - dataMessage.addExtension(dpe); - - // add data packets - listener.processPacket(dataMessage); - - // read until exception is thrown - try { - inputStream.read(); - fail("exception should be thrown"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("Packets out of sequence")); - } - - protocol.verifyAll(); - - } - - /** - * Test the input stream read(byte[], int, int) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldReadAllReceivedData1() throws Exception { - // create random data - Random rand = new Random(); - byte[] controlData = new byte[3 * blockSize]; - rand.nextBytes(controlData); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // verify data packet and notify listener - for (int i = 0; i < controlData.length / blockSize; i++) { - String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, - false); - DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); - Message dataMessage = new Message(); - dataMessage.addExtension(dpe); - listener.processPacket(dataMessage); - } - - byte[] bytes = new byte[3 * blockSize]; - int read = 0; - read = inputStream.read(bytes, 0, blockSize); - assertEquals(blockSize, read); - read = inputStream.read(bytes, 10, blockSize); - assertEquals(blockSize, read); - read = inputStream.read(bytes, 20, blockSize); - assertEquals(blockSize, read); - - // verify data - for (int i = 0; i < bytes.length; i++) { - assertEquals(controlData[i], bytes[i]); - } - - protocol.verifyAll(); - - } - - /** - * Test the input stream read() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldReadAllReceivedData2() throws Exception { - // create random data - Random rand = new Random(); - byte[] controlData = new byte[3 * blockSize]; - rand.nextBytes(controlData); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // verify data packet and notify listener - for (int i = 0; i < controlData.length / blockSize; i++) { - String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, - false); - DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); - Message dataMessage = new Message(); - dataMessage.addExtension(dpe); - listener.processPacket(dataMessage); - } - - // read data - byte[] bytes = new byte[3 * blockSize]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) inputStream.read(); - } - - // verify data - for (int i = 0; i < bytes.length; i++) { - assertEquals(controlData[i], bytes[i]); - } - - protocol.verifyAll(); - - } - -} +package org.jivesoftware.smackx.bytestreams.ibb; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.jivesoftware.util.Verification; +import org.junit.Before; +import org.junit.Test; +import org.powermock.reflect.Whitebox; + +/** + * Test for InBandBytestreamSession. + *

    + * Tests sending data encapsulated in message stanzas. + * + * @author Henning Staib + */ +public class InBandBytestreamSessionMessageTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String sessionID = "session_id"; + + int blockSize = 10; + + // protocol verifier + Protocol protocol; + + // mocked XMPP connection + Connection connection; + + InBandBytestreamManager byteStreamManager; + + Open initBytestream; + + Verification incrementingSequence; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); + + // initialize InBandBytestreamManager to get the InitiationListener + byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // create a In-Band Bytestream open packet with message stanza + initBytestream = new Open(sessionID, blockSize, StanzaType.MESSAGE); + initBytestream.setFrom(initiatorJID); + initBytestream.setTo(targetJID); + + incrementingSequence = new Verification() { + + long lastSeq = 0; + + public void verify(Message request, IQ response) { + DataPacketExtension dpe = (DataPacketExtension) request.getExtension( + DataPacketExtension.ELEMENT_NAME, InBandBytestreamManager.NAMESPACE); + assertEquals(lastSeq++, dpe.getSeq()); + } + + }; + + } + + /** + * Test the output stream write(byte[]) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets1() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // verify the data packets + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + + byte[] controlData = new byte[blockSize * 3]; + + OutputStream outputStream = session.getOutputStream(); + outputStream.write(controlData); + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream write(byte) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets2() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // verify the data packets + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + + byte[] controlData = new byte[blockSize * 3]; + + OutputStream outputStream = session.getOutputStream(); + for (byte b : controlData) { + outputStream.write(b); + } + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream write(byte[], int, int) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets3() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // verify the data packets + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + + byte[] controlData = new byte[(blockSize * 3) - 2]; + + OutputStream outputStream = session.getOutputStream(); + int off = 0; + for (int i = 1; i <= 7; i++) { + outputStream.write(controlData, off, i); + off += i; + } + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream flush() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThirtyDataPackets() throws Exception { + byte[] controlData = new byte[blockSize * 3]; + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // verify the data packets + for (int i = 0; i < controlData.length; i++) { + protocol.addResponse(null, incrementingSequence); + } + + OutputStream outputStream = session.getOutputStream(); + for (byte b : controlData) { + outputStream.write(b); + outputStream.flush(); + } + + protocol.verifyAll(); + + } + + /** + * Test successive calls to the output stream flush() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { + byte[] controlData = new byte[blockSize * 3]; + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // verify the data packets + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + protocol.addResponse(null, incrementingSequence); + + OutputStream outputStream = session.getOutputStream(); + outputStream.write(controlData); + + outputStream.flush(); + outputStream.flush(); + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * If a data packet is received out of order the session should be closed. See XEP-0047 Section + * 2.2. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { + // confirm close request + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ, Verification.requestTypeSET, + Verification.correspondingSenderReceiver); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build invalid packet with out of order sequence + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data); + Message dataMessage = new Message(); + dataMessage.addExtension(dpe); + + // add data packets + listener.processPacket(dataMessage); + + // read until exception is thrown + try { + inputStream.read(); + fail("exception should be thrown"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("Packets out of sequence")); + } + + protocol.verifyAll(); + + } + + /** + * Test the input stream read(byte[], int, int) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldReadAllReceivedData1() throws Exception { + // create random data + Random rand = new Random(); + byte[] controlData = new byte[3 * blockSize]; + rand.nextBytes(controlData); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // verify data packet and notify listener + for (int i = 0; i < controlData.length / blockSize; i++) { + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, + false); + DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); + Message dataMessage = new Message(); + dataMessage.addExtension(dpe); + listener.processPacket(dataMessage); + } + + byte[] bytes = new byte[3 * blockSize]; + int read = 0; + read = inputStream.read(bytes, 0, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 10, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 20, blockSize); + assertEquals(blockSize, read); + + // verify data + for (int i = 0; i < bytes.length; i++) { + assertEquals(controlData[i], bytes[i]); + } + + protocol.verifyAll(); + + } + + /** + * Test the input stream read() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldReadAllReceivedData2() throws Exception { + // create random data + Random rand = new Random(); + byte[] controlData = new byte[3 * blockSize]; + rand.nextBytes(controlData); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // verify data packet and notify listener + for (int i = 0; i < controlData.length / blockSize; i++) { + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, + false); + DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); + Message dataMessage = new Message(); + dataMessage.addExtension(dpe); + listener.processPacket(dataMessage); + } + + // read data + byte[] bytes = new byte[3 * blockSize]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) inputStream.read(); + } + + // verify data + for (int i = 0; i < bytes.length; i++) { + assertEquals(controlData[i], bytes[i]); + } + + protocol.verifyAll(); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java index fca8fbd65..5d893091e 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java @@ -14,703 +14,703 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.bytestreams.ibb; - -import static org.junit.Assert.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Random; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.PacketListener; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.jivesoftware.util.Verification; -import org.junit.Before; -import org.junit.Test; -import org.powermock.reflect.Whitebox; - -/** - * Test for InBandBytestreamSession. - *

    - * Tests the basic behavior of an In-Band Bytestream session along with sending data encapsulated in - * IQ stanzas. - * - * @author Henning Staib - */ -public class InBandBytestreamSessionTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String sessionID = "session_id"; - - int blockSize = 10; - - // protocol verifier - Protocol protocol; - - // mocked XMPP connection - Connection connection; - - InBandBytestreamManager byteStreamManager; - - Open initBytestream; - - Verification incrementingSequence; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); - - // initialize InBandBytestreamManager to get the InitiationListener - byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // create a In-Band Bytestream open packet - initBytestream = new Open(sessionID, blockSize); - initBytestream.setFrom(initiatorJID); - initBytestream.setTo(targetJID); - - incrementingSequence = new Verification() { - - long lastSeq = 0; - - public void verify(Data request, IQ response) { - assertEquals(lastSeq++, request.getDataPacketExtension().getSeq()); - } - - }; - - } - - /** - * Test the output stream write(byte[]) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets1() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - - byte[] controlData = new byte[blockSize * 3]; - - OutputStream outputStream = session.getOutputStream(); - outputStream.write(controlData); - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream write(byte) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets2() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - - byte[] controlData = new byte[blockSize * 3]; - - OutputStream outputStream = session.getOutputStream(); - for (byte b : controlData) { - outputStream.write(b); - } - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream write(byte[], int, int) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThreeDataPackets3() throws Exception { - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - - byte[] controlData = new byte[(blockSize * 3) - 2]; - - OutputStream outputStream = session.getOutputStream(); - int off = 0; - for (int i = 1; i <= 7; i++) { - outputStream.write(controlData, off, i); - off += i; - } - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test the output stream flush() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendThirtyDataPackets() throws Exception { - byte[] controlData = new byte[blockSize * 3]; - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - for (int i = 0; i < controlData.length; i++) { - protocol.addResponse(resultIQ, incrementingSequence); - } - - OutputStream outputStream = session.getOutputStream(); - for (byte b : controlData) { - outputStream.write(b); - outputStream.flush(); - } - - protocol.verifyAll(); - - } - - /** - * Test successive calls to the output stream flush() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { - byte[] controlData = new byte[blockSize * 3]; - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - protocol.addResponse(resultIQ, incrementingSequence); - - OutputStream outputStream = session.getOutputStream(); - outputStream.write(controlData); - - outputStream.flush(); - outputStream.flush(); - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * Test that the data is correctly chunked. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendDataCorrectly() throws Exception { - // create random data - Random rand = new Random(); - final byte[] controlData = new byte[256 * blockSize]; - rand.nextBytes(controlData); - - // compares the data of each packet with the control data - Verification dataVerification = new Verification() { - - public void verify(Data request, IQ response) { - byte[] decodedData = request.getDataPacketExtension().getDecodedData(); - int seq = (int) request.getDataPacketExtension().getSeq(); - for (int i = 0; i < decodedData.length; i++) { - assertEquals(controlData[(seq * blockSize) + i], decodedData[i]); - } - } - - }; - - // set acknowledgments for the data packets - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - for (int i = 0; i < controlData.length / blockSize; i++) { - protocol.addResponse(resultIQ, incrementingSequence, dataVerification); - } - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - - OutputStream outputStream = session.getOutputStream(); - outputStream.write(controlData); - outputStream.flush(); - - protocol.verifyAll(); - - } - - /** - * If the input stream is closed the output stream should not be closed as well. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotCloseBothStreamsIfOutputStreamIsClosed() throws Exception { - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - OutputStream outputStream = session.getOutputStream(); - outputStream.close(); - - // verify data packet confirmation is of type RESULT - protocol.addResponse(null, Verification.requestTypeRESULT); - - // insert data to read - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data = new Data(dpe); - listener.processPacket(data); - - // verify no packet send - protocol.verifyAll(); - - try { - outputStream.flush(); - fail("should throw an exception"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("closed")); - } - - assertTrue(inputStream.read() != 0); - - } - - /** - * Valid data packets should be confirmed. - * - * @throws Exception should not happen - */ - @Test - public void shouldConfirmReceivedDataPacket() throws Exception { - // verify data packet confirmation is of type RESULT - protocol.addResponse(null, Verification.requestTypeRESULT); - - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data = new Data(dpe); - - listener.processPacket(data); - - protocol.verifyAll(); - - } - - /** - * If the data packet has a sequence that is already used an 'unexpected-request' error should - * be returned. See XEP-0047 Section 2.2. - * - * @throws Exception should not happen - */ - @Test - public void shouldReplyWithErrorIfAlreadyUsedSequenceIsReceived() throws Exception { - // verify reply to first valid data packet is of type RESULT - protocol.addResponse(null, Verification.requestTypeRESULT); - - // verify reply to invalid data packet is an error - protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { - - public void verify(IQ request, IQ response) { - assertEquals(XMPPError.Condition.unexpected_request.toString(), - request.getError().getCondition()); - } - - }); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build data packets - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data1 = new Data(dpe); - Data data2 = new Data(dpe); - - // notify listener - listener.processPacket(data1); - listener.processPacket(data2); - - protocol.verifyAll(); - - } - - /** - * If the data packet contains invalid Base64 encoding an 'bad-request' error should be - * returned. See XEP-0047 Section 2.2. - * - * @throws Exception should not happen - */ - @Test - public void shouldReplyWithErrorIfDataIsInvalid() throws Exception { - // verify reply to invalid data packet is an error - protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { - - public void verify(IQ request, IQ response) { - assertEquals(XMPPError.Condition.bad_request.toString(), - request.getError().getCondition()); - } - - }); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build data packets - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, "AA=BB"); - Data data = new Data(dpe); - - // notify listener - listener.processPacket(data); - - protocol.verifyAll(); - - } - - /** - * If a data packet is received out of order the session should be closed. See XEP-0047 Section - * 2.2. - * - * @throws Exception should not happen - */ - @Test - public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - - // confirm data packet with invalid sequence - protocol.addResponse(resultIQ); - - // confirm close request - protocol.addResponse(resultIQ, Verification.requestTypeSET, - Verification.correspondingSenderReceiver); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build invalid packet with out of order sequence - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data); - Data data = new Data(dpe); - - // add data packets - listener.processPacket(data); - - // read until exception is thrown - try { - inputStream.read(); - fail("exception should be thrown"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("Packets out of sequence")); - } - - protocol.verifyAll(); - - } - - /** - * Test the input stream read(byte[], int, int) method. - * - * @throws Exception should not happen - */ - @Test - public void shouldReadAllReceivedData1() throws Exception { - // create random data - Random rand = new Random(); - byte[] controlData = new byte[3 * blockSize]; - rand.nextBytes(controlData); - - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // set data packet acknowledgment and notify listener - for (int i = 0; i < controlData.length / blockSize; i++) { - protocol.addResponse(resultIQ); - String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, - false); - DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); - Data data = new Data(dpe); - listener.processPacket(data); - } - - byte[] bytes = new byte[3 * blockSize]; - int read = 0; - read = inputStream.read(bytes, 0, blockSize); - assertEquals(blockSize, read); - read = inputStream.read(bytes, 10, blockSize); - assertEquals(blockSize, read); - read = inputStream.read(bytes, 20, blockSize); - assertEquals(blockSize, read); - - // verify data - for (int i = 0; i < bytes.length; i++) { - assertEquals(controlData[i], bytes[i]); - } - - protocol.verifyAll(); - - } - - /** - * Test the input stream read() method. - * - * @throws Exception should not happen - */ - @Test - public void shouldReadAllReceivedData2() throws Exception { - // create random data - Random rand = new Random(); - byte[] controlData = new byte[3 * blockSize]; - rand.nextBytes(controlData); - - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // set data packet acknowledgment and notify listener - for (int i = 0; i < controlData.length / blockSize; i++) { - protocol.addResponse(resultIQ); - String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, - false); - DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); - Data data = new Data(dpe); - listener.processPacket(data); - } - - // read data - byte[] bytes = new byte[3 * blockSize]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) inputStream.read(); - } - - // verify data - for (int i = 0; i < bytes.length; i++) { - assertEquals(controlData[i], bytes[i]); - } - - protocol.verifyAll(); - - } - - /** - * If the output stream is closed the input stream should not be closed as well. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotCloseBothStreamsIfInputStreamIsClosed() throws Exception { - // acknowledgment for data packet - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build data packet - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data = new Data(dpe); - - // add data packets - listener.processPacket(data); - - inputStream.close(); - - protocol.verifyAll(); - - try { - while (inputStream.read() != -1) { - } - inputStream.read(); - fail("should throw an exception"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("closed")); - } - - session.getOutputStream().flush(); - - } - - /** - * If the session is closed the input stream and output stream should be closed as well. - * - * @throws Exception should not happen - */ - @Test - public void shouldCloseBothStreamsIfSessionIsClosed() throws Exception { - // acknowledgment for data packet - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ); - - // acknowledgment for close request - protocol.addResponse(resultIQ, Verification.correspondingSenderReceiver, - Verification.requestTypeSET); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build data packet - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data = new Data(dpe); - - // add data packets - listener.processPacket(data); - - session.close(); - - protocol.verifyAll(); - - try { - while (inputStream.read() != -1) { - } - inputStream.read(); - fail("should throw an exception"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("closed")); - } - - try { - session.getOutputStream().flush(); - fail("should throw an exception"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("closed")); - } - - } - - /** - * If the input stream is closed concurrently there should be no deadlock. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotDeadlockIfInputStreamIsClosed() throws Exception { - // acknowledgment for data packet - IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - protocol.addResponse(resultIQ); - - // get IBB sessions data packet listener - InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, - initiatorJID); - final InputStream inputStream = session.getInputStream(); - PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - - // build data packet - String base64Data = StringUtils.encodeBase64("Data"); - DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); - Data data = new Data(dpe); - - // add data packets - listener.processPacket(data); - - Thread closer = new Thread(new Runnable() { - - public void run() { - try { - Thread.sleep(200); - inputStream.close(); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }); - closer.start(); - - try { - byte[] bytes = new byte[20]; - while (inputStream.read(bytes) != -1) { - } - inputStream.read(); - fail("should throw an exception"); - } - catch (IOException e) { - assertTrue(e.getMessage().contains("closed")); - } - - protocol.verifyAll(); - - } - -} +package org.jivesoftware.smackx.bytestreams.ibb; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.jivesoftware.util.Verification; +import org.junit.Before; +import org.junit.Test; +import org.powermock.reflect.Whitebox; + +/** + * Test for InBandBytestreamSession. + *

    + * Tests the basic behavior of an In-Band Bytestream session along with sending data encapsulated in + * IQ stanzas. + * + * @author Henning Staib + */ +public class InBandBytestreamSessionTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String sessionID = "session_id"; + + int blockSize = 10; + + // protocol verifier + Protocol protocol; + + // mocked XMPP connection + Connection connection; + + InBandBytestreamManager byteStreamManager; + + Open initBytestream; + + Verification incrementingSequence; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); + + // initialize InBandBytestreamManager to get the InitiationListener + byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // create a In-Band Bytestream open packet + initBytestream = new Open(sessionID, blockSize); + initBytestream.setFrom(initiatorJID); + initBytestream.setTo(targetJID); + + incrementingSequence = new Verification() { + + long lastSeq = 0; + + public void verify(Data request, IQ response) { + assertEquals(lastSeq++, request.getDataPacketExtension().getSeq()); + } + + }; + + } + + /** + * Test the output stream write(byte[]) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets1() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + + byte[] controlData = new byte[blockSize * 3]; + + OutputStream outputStream = session.getOutputStream(); + outputStream.write(controlData); + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream write(byte) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets2() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + + byte[] controlData = new byte[blockSize * 3]; + + OutputStream outputStream = session.getOutputStream(); + for (byte b : controlData) { + outputStream.write(b); + } + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream write(byte[], int, int) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThreeDataPackets3() throws Exception { + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + + byte[] controlData = new byte[(blockSize * 3) - 2]; + + OutputStream outputStream = session.getOutputStream(); + int off = 0; + for (int i = 1; i <= 7; i++) { + outputStream.write(controlData, off, i); + off += i; + } + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test the output stream flush() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendThirtyDataPackets() throws Exception { + byte[] controlData = new byte[blockSize * 3]; + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + for (int i = 0; i < controlData.length; i++) { + protocol.addResponse(resultIQ, incrementingSequence); + } + + OutputStream outputStream = session.getOutputStream(); + for (byte b : controlData) { + outputStream.write(b); + outputStream.flush(); + } + + protocol.verifyAll(); + + } + + /** + * Test successive calls to the output stream flush() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { + byte[] controlData = new byte[blockSize * 3]; + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + protocol.addResponse(resultIQ, incrementingSequence); + + OutputStream outputStream = session.getOutputStream(); + outputStream.write(controlData); + + outputStream.flush(); + outputStream.flush(); + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * Test that the data is correctly chunked. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendDataCorrectly() throws Exception { + // create random data + Random rand = new Random(); + final byte[] controlData = new byte[256 * blockSize]; + rand.nextBytes(controlData); + + // compares the data of each packet with the control data + Verification dataVerification = new Verification() { + + public void verify(Data request, IQ response) { + byte[] decodedData = request.getDataPacketExtension().getDecodedData(); + int seq = (int) request.getDataPacketExtension().getSeq(); + for (int i = 0; i < decodedData.length; i++) { + assertEquals(controlData[(seq * blockSize) + i], decodedData[i]); + } + } + + }; + + // set acknowledgments for the data packets + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + for (int i = 0; i < controlData.length / blockSize; i++) { + protocol.addResponse(resultIQ, incrementingSequence, dataVerification); + } + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + + OutputStream outputStream = session.getOutputStream(); + outputStream.write(controlData); + outputStream.flush(); + + protocol.verifyAll(); + + } + + /** + * If the input stream is closed the output stream should not be closed as well. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotCloseBothStreamsIfOutputStreamIsClosed() throws Exception { + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + OutputStream outputStream = session.getOutputStream(); + outputStream.close(); + + // verify data packet confirmation is of type RESULT + protocol.addResponse(null, Verification.requestTypeRESULT); + + // insert data to read + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data = new Data(dpe); + listener.processPacket(data); + + // verify no packet send + protocol.verifyAll(); + + try { + outputStream.flush(); + fail("should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("closed")); + } + + assertTrue(inputStream.read() != 0); + + } + + /** + * Valid data packets should be confirmed. + * + * @throws Exception should not happen + */ + @Test + public void shouldConfirmReceivedDataPacket() throws Exception { + // verify data packet confirmation is of type RESULT + protocol.addResponse(null, Verification.requestTypeRESULT); + + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data = new Data(dpe); + + listener.processPacket(data); + + protocol.verifyAll(); + + } + + /** + * If the data packet has a sequence that is already used an 'unexpected-request' error should + * be returned. See XEP-0047 Section 2.2. + * + * @throws Exception should not happen + */ + @Test + public void shouldReplyWithErrorIfAlreadyUsedSequenceIsReceived() throws Exception { + // verify reply to first valid data packet is of type RESULT + protocol.addResponse(null, Verification.requestTypeRESULT); + + // verify reply to invalid data packet is an error + protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { + + public void verify(IQ request, IQ response) { + assertEquals(XMPPError.Condition.unexpected_request.toString(), + request.getError().getCondition()); + } + + }); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build data packets + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data1 = new Data(dpe); + Data data2 = new Data(dpe); + + // notify listener + listener.processPacket(data1); + listener.processPacket(data2); + + protocol.verifyAll(); + + } + + /** + * If the data packet contains invalid Base64 encoding an 'bad-request' error should be + * returned. See XEP-0047 Section 2.2. + * + * @throws Exception should not happen + */ + @Test + public void shouldReplyWithErrorIfDataIsInvalid() throws Exception { + // verify reply to invalid data packet is an error + protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { + + public void verify(IQ request, IQ response) { + assertEquals(XMPPError.Condition.bad_request.toString(), + request.getError().getCondition()); + } + + }); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build data packets + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, "AA=BB"); + Data data = new Data(dpe); + + // notify listener + listener.processPacket(data); + + protocol.verifyAll(); + + } + + /** + * If a data packet is received out of order the session should be closed. See XEP-0047 Section + * 2.2. + * + * @throws Exception should not happen + */ + @Test + public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + + // confirm data packet with invalid sequence + protocol.addResponse(resultIQ); + + // confirm close request + protocol.addResponse(resultIQ, Verification.requestTypeSET, + Verification.correspondingSenderReceiver); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build invalid packet with out of order sequence + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data); + Data data = new Data(dpe); + + // add data packets + listener.processPacket(data); + + // read until exception is thrown + try { + inputStream.read(); + fail("exception should be thrown"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("Packets out of sequence")); + } + + protocol.verifyAll(); + + } + + /** + * Test the input stream read(byte[], int, int) method. + * + * @throws Exception should not happen + */ + @Test + public void shouldReadAllReceivedData1() throws Exception { + // create random data + Random rand = new Random(); + byte[] controlData = new byte[3 * blockSize]; + rand.nextBytes(controlData); + + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // set data packet acknowledgment and notify listener + for (int i = 0; i < controlData.length / blockSize; i++) { + protocol.addResponse(resultIQ); + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, + false); + DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); + Data data = new Data(dpe); + listener.processPacket(data); + } + + byte[] bytes = new byte[3 * blockSize]; + int read = 0; + read = inputStream.read(bytes, 0, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 10, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 20, blockSize); + assertEquals(blockSize, read); + + // verify data + for (int i = 0; i < bytes.length; i++) { + assertEquals(controlData[i], bytes[i]); + } + + protocol.verifyAll(); + + } + + /** + * Test the input stream read() method. + * + * @throws Exception should not happen + */ + @Test + public void shouldReadAllReceivedData2() throws Exception { + // create random data + Random rand = new Random(); + byte[] controlData = new byte[3 * blockSize]; + rand.nextBytes(controlData); + + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // set data packet acknowledgment and notify listener + for (int i = 0; i < controlData.length / blockSize; i++) { + protocol.addResponse(resultIQ); + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, + false); + DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); + Data data = new Data(dpe); + listener.processPacket(data); + } + + // read data + byte[] bytes = new byte[3 * blockSize]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) inputStream.read(); + } + + // verify data + for (int i = 0; i < bytes.length; i++) { + assertEquals(controlData[i], bytes[i]); + } + + protocol.verifyAll(); + + } + + /** + * If the output stream is closed the input stream should not be closed as well. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotCloseBothStreamsIfInputStreamIsClosed() throws Exception { + // acknowledgment for data packet + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build data packet + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data = new Data(dpe); + + // add data packets + listener.processPacket(data); + + inputStream.close(); + + protocol.verifyAll(); + + try { + while (inputStream.read() != -1) { + } + inputStream.read(); + fail("should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("closed")); + } + + session.getOutputStream().flush(); + + } + + /** + * If the session is closed the input stream and output stream should be closed as well. + * + * @throws Exception should not happen + */ + @Test + public void shouldCloseBothStreamsIfSessionIsClosed() throws Exception { + // acknowledgment for data packet + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ); + + // acknowledgment for close request + protocol.addResponse(resultIQ, Verification.correspondingSenderReceiver, + Verification.requestTypeSET); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build data packet + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data = new Data(dpe); + + // add data packets + listener.processPacket(data); + + session.close(); + + protocol.verifyAll(); + + try { + while (inputStream.read() != -1) { + } + inputStream.read(); + fail("should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("closed")); + } + + try { + session.getOutputStream().flush(); + fail("should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("closed")); + } + + } + + /** + * If the input stream is closed concurrently there should be no deadlock. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotDeadlockIfInputStreamIsClosed() throws Exception { + // acknowledgment for data packet + IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); + protocol.addResponse(resultIQ); + + // get IBB sessions data packet listener + InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, + initiatorJID); + final InputStream inputStream = session.getInputStream(); + PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); + + // build data packet + String base64Data = StringUtils.encodeBase64("Data"); + DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); + Data data = new Data(dpe); + + // add data packets + listener.processPacket(data); + + Thread closer = new Thread(new Runnable() { + + public void run() { + try { + Thread.sleep(200); + inputStream.close(); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }); + closer.start(); + + try { + byte[] bytes = new byte[20]; + while (inputStream.read(bytes) != -1) { + } + inputStream.read(); + fail("should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().contains("closed")); + } + + protocol.verifyAll(); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java index 84caabf28..03d7cef83 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java @@ -1,333 +1,333 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.bytestreams.BytestreamRequest; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.InitiationListener; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.powermock.reflect.Whitebox; - -/** - * Test for the InitiationListener class. - * - * @author Henning Staib - */ -public class InitiationListenerTest { - - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String sessionID = "session_id"; - - Connection connection; - InBandBytestreamManager byteStreamManager; - InitiationListener initiationListener; - Open initBytestream; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // mock connection - connection = mock(Connection.class); - - // initialize InBandBytestreamManager to get the InitiationListener - byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); - - // get the InitiationListener from InBandByteStreamManager - initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class); - - // create a In-Band Bytestream open packet - initBytestream = new Open(sessionID, 4096); - initBytestream.setFrom(initiatorJID); - initBytestream.setTo(targetJID); - - } - - /** - * If no listeners are registered for incoming In-Band Bytestream requests, all request should - * be rejected with an error. - * - * @throws Exception should not happen - */ - @Test - public void shouldRespondWithError() throws Exception { - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), - argument.getValue().getError().getCondition()); - - } - - /** - * Open request with a block size that exceeds the maximum block size should be replied with an - * resource-constraint error. - * - * @throws Exception should not happen - */ - @Test - public void shouldRejectRequestWithTooBigBlockSize() throws Exception { - byteStreamManager.setMaximumBlockSize(1024); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.resource_constraint.toString(), - argument.getValue().getError().getCondition()); - - } - - /** - * If a listener for all requests is registered it should be notified on incoming requests. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeListenerForAllRequests() throws Exception { - - // add listener - InBandBytestreamListener listener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert that listener is called for the correct request - assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); - - } - - /** - * If a listener for a specific user in registered it should be notified on incoming requests - * for that user. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeListenerForUser() throws Exception { - - // add listener - InBandBytestreamListener listener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); - - } - - /** - * If listener for a specific user is registered it should not be notified on incoming requests - * from other users. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotInvokeListenerForUser() throws Exception { - - // add listener for request of user "other_initiator" - InBandBytestreamListener listener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), - argument.getValue().getError().getCondition()); - } - - /** - * If a user specific listener and an all requests listener is registered only the user specific - * listener should be notified. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception { - - // add listener for all request - InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "initiator" - InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - - /** - * If a user specific listener and an all requests listener is registered only the all requests - * listener should be notified on an incoming request for another user. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception { - - // add listener for all request - InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "other_initiator" - InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_" - + initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - - /** - * If a request with a specific session ID should be ignored no listeners should be notified. - * - * @throws Exception should not happen - */ - @Test - public void shouldIgnoreInBandBytestreamRequestOnce() throws Exception { - - // add listener for all request - InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "initiator" - InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); - - // ignore session ID - byteStreamManager.ignoreBytestreamRequestOnce(sessionID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // run the listener with the initiation packet again - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is called on the second request with the - // same session ID - verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.BytestreamRequest; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.InitiationListener; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.powermock.reflect.Whitebox; + +/** + * Test for the InitiationListener class. + * + * @author Henning Staib + */ +public class InitiationListenerTest { + + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String sessionID = "session_id"; + + Connection connection; + InBandBytestreamManager byteStreamManager; + InitiationListener initiationListener; + Open initBytestream; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // mock connection + connection = mock(Connection.class); + + // initialize InBandBytestreamManager to get the InitiationListener + byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); + + // get the InitiationListener from InBandByteStreamManager + initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class); + + // create a In-Band Bytestream open packet + initBytestream = new Open(sessionID, 4096); + initBytestream.setFrom(initiatorJID); + initBytestream.setTo(targetJID); + + } + + /** + * If no listeners are registered for incoming In-Band Bytestream requests, all request should + * be rejected with an error. + * + * @throws Exception should not happen + */ + @Test + public void shouldRespondWithError() throws Exception { + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // capture reply to the In-Band Bytestream open request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), + argument.getValue().getError().getCondition()); + + } + + /** + * Open request with a block size that exceeds the maximum block size should be replied with an + * resource-constraint error. + * + * @throws Exception should not happen + */ + @Test + public void shouldRejectRequestWithTooBigBlockSize() throws Exception { + byteStreamManager.setMaximumBlockSize(1024); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // capture reply to the In-Band Bytestream open request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.resource_constraint.toString(), + argument.getValue().getError().getCondition()); + + } + + /** + * If a listener for all requests is registered it should be notified on incoming requests. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeListenerForAllRequests() throws Exception { + + // add listener + InBandBytestreamListener listener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert that listener is called for the correct request + assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); + + } + + /** + * If a listener for a specific user in registered it should be notified on incoming requests + * for that user. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeListenerForUser() throws Exception { + + // add listener + InBandBytestreamListener listener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); + + } + + /** + * If listener for a specific user is registered it should not be notified on incoming requests + * from other users. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotInvokeListenerForUser() throws Exception { + + // add listener for request of user "other_initiator" + InBandBytestreamListener listener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // capture reply to the In-Band Bytestream open request + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), + argument.getValue().getError().getCondition()); + } + + /** + * If a user specific listener and an all requests listener is registered only the user specific + * listener should be notified. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception { + + // add listener for all request + InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "initiator" + InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + + /** + * If a user specific listener and an all requests listener is registered only the all requests + * listener should be notified on an incoming request for another user. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception { + + // add listener for all request + InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "other_initiator" + InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_" + + initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + + /** + * If a request with a specific session ID should be ignored no listeners should be notified. + * + * @throws Exception should not happen + */ + @Test + public void shouldIgnoreInBandBytestreamRequestOnce() throws Exception { + + // add listener for all request + InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "initiator" + InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); + + // ignore session ID + byteStreamManager.ignoreBytestreamRequestOnce(sessionID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // run the listener with the initiation packet again + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is called on the second request with the + // same session ID + verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java index 59c02025a..ce253a68b 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java @@ -1,84 +1,84 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.*; - -import java.util.Properties; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Close; -import org.junit.Test; - -import com.jamesmurty.utils.XMLBuilder; - -/** - * Test for the Close class. - * - * @author Henning Staib - */ -public class CloseTest { - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArguments1() { - new Close(null); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArguments2() { - new Close(""); - } - - @Test - public void shouldBeOfIQTypeSET() { - Close close = new Close("sessionID"); - assertEquals(IQ.Type.SET, close.getType()); - } - - @Test - public void shouldSetAllFieldsCorrectly() { - Close close = new Close("sessionID"); - assertEquals("sessionID", close.getSessionID()); - } - - private static Properties outputProperties = new Properties(); - { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @Test - public void shouldReturnValidIQStanzaXML() throws Exception { - String control = XMLBuilder.create("iq") - .a("from", "romeo@montague.lit/orchard") - .a("to", "juliet@capulet.lit/balcony") - .a("id", "us71g45j") - .a("type", "set") - .e("close") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("sid", "i781hf64") - .asString(outputProperties); - - Close close = new Close("i781hf64"); - close.setFrom("romeo@montague.lit/orchard"); - close.setTo("juliet@capulet.lit/balcony"); - close.setPacketID("us71g45j"); - - assertXMLEqual(control, close.toXML()); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.*; + +import java.util.Properties; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Close; +import org.junit.Test; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * Test for the Close class. + * + * @author Henning Staib + */ +public class CloseTest { + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArguments1() { + new Close(null); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArguments2() { + new Close(""); + } + + @Test + public void shouldBeOfIQTypeSET() { + Close close = new Close("sessionID"); + assertEquals(IQ.Type.SET, close.getType()); + } + + @Test + public void shouldSetAllFieldsCorrectly() { + Close close = new Close("sessionID"); + assertEquals("sessionID", close.getSessionID()); + } + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void shouldReturnValidIQStanzaXML() throws Exception { + String control = XMLBuilder.create("iq") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "us71g45j") + .a("type", "set") + .e("close") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("sid", "i781hf64") + .asString(outputProperties); + + Close close = new Close("i781hf64"); + close.setFrom("romeo@montague.lit/orchard"); + close.setTo("juliet@capulet.lit/balcony"); + close.setPacketID("us71g45j"); + + assertXMLEqual(control, close.toXML()); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java index a5887dae9..888b8f58a 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java @@ -1,98 +1,98 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.*; - -import java.util.Properties; - -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; -import org.junit.Test; - -import com.jamesmurty.utils.XMLBuilder; - -/** - * Test for the DataPacketExtension class. - * - * @author Henning Staib - */ -public class DataPacketExtensionTest { - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument1() { - new DataPacketExtension(null, 0, "data"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument2() { - new DataPacketExtension("", 0, "data"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument3() { - new DataPacketExtension("sessionID", -1, "data"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument4() { - new DataPacketExtension("sessionID", 70000, "data"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument5() { - new DataPacketExtension("sessionID", 0, null); - } - - @Test - public void shouldSetAllFieldsCorrectly() { - DataPacketExtension data = new DataPacketExtension("sessionID", 0, "data"); - assertEquals("sessionID", data.getSessionID()); - assertEquals(0, data.getSeq()); - assertEquals("data", data.getData()); - } - - @Test - public void shouldReturnNullIfDataIsInvalid() { - // pad character is not at end of data - DataPacketExtension data = new DataPacketExtension("sessionID", 0, "BBBB=CCC"); - assertNull(data.getDecodedData()); - - // invalid Base64 character - data = new DataPacketExtension("sessionID", 0, new String(new byte[] { 123 })); - assertNull(data.getDecodedData()); - } - - private static Properties outputProperties = new Properties(); - { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @Test - public void shouldReturnValidIQStanzaXML() throws Exception { - String control = XMLBuilder.create("data") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("seq", "0") - .a("sid", "i781hf64") - .t("DATA") - .asString(outputProperties); - - DataPacketExtension data = new DataPacketExtension("i781hf64", 0, "DATA"); - assertXMLEqual(control, data.toXML()); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.*; + +import java.util.Properties; + +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; +import org.junit.Test; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * Test for the DataPacketExtension class. + * + * @author Henning Staib + */ +public class DataPacketExtensionTest { + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument1() { + new DataPacketExtension(null, 0, "data"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument2() { + new DataPacketExtension("", 0, "data"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument3() { + new DataPacketExtension("sessionID", -1, "data"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument4() { + new DataPacketExtension("sessionID", 70000, "data"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument5() { + new DataPacketExtension("sessionID", 0, null); + } + + @Test + public void shouldSetAllFieldsCorrectly() { + DataPacketExtension data = new DataPacketExtension("sessionID", 0, "data"); + assertEquals("sessionID", data.getSessionID()); + assertEquals(0, data.getSeq()); + assertEquals("data", data.getData()); + } + + @Test + public void shouldReturnNullIfDataIsInvalid() { + // pad character is not at end of data + DataPacketExtension data = new DataPacketExtension("sessionID", 0, "BBBB=CCC"); + assertNull(data.getDecodedData()); + + // invalid Base64 character + data = new DataPacketExtension("sessionID", 0, new String(new byte[] { 123 })); + assertNull(data.getDecodedData()); + } + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void shouldReturnValidIQStanzaXML() throws Exception { + String control = XMLBuilder.create("data") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("seq", "0") + .a("sid", "i781hf64") + .t("DATA") + .asString(outputProperties); + + DataPacketExtension data = new DataPacketExtension("i781hf64", 0, "DATA"); + assertXMLEqual(control, data.toXML()); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java index 2db8bc5d1..9a44b68cf 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java @@ -1,89 +1,89 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.*; -import static org.mockito.Mockito.*; - -import java.util.Properties; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; -import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; -import org.junit.Test; - -import com.jamesmurty.utils.XMLBuilder; - -/** - * Test for the Data class. - * - * @author Henning Staib - */ -public class DataTest { - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArgument() { - new Data(null); - } - - @Test - public void shouldBeOfIQTypeSET() { - DataPacketExtension dpe = mock(DataPacketExtension.class); - Data data = new Data(dpe); - assertEquals(IQ.Type.SET, data.getType()); - } - - private static Properties outputProperties = new Properties(); - { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @Test - public void shouldReturnValidIQStanzaXML() throws Exception { - String encodedData = StringUtils.encodeBase64("Test"); - - String control = XMLBuilder.create("iq") - .a("from", "romeo@montague.lit/orchard") - .a("to", "juliet@capulet.lit/balcony") - .a("id", "kr91n475") - .a("type", "set") - .e("data") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("seq", "0") - .a("sid", "i781hf64") - .t(encodedData) - .asString(outputProperties); - - DataPacketExtension dpe = mock(DataPacketExtension.class); - String dataTag = XMLBuilder.create("data") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("seq", "0") - .a("sid", "i781hf64") - .t(encodedData) - .asString(outputProperties); - when(dpe.toXML()).thenReturn(dataTag); - Data data = new Data(dpe); - data.setFrom("romeo@montague.lit/orchard"); - data.setTo("juliet@capulet.lit/balcony"); - data.setPacketID("kr91n475"); - - assertXMLEqual(control, data.toXML()); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.*; +import static org.mockito.Mockito.*; + +import java.util.Properties; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Data; +import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; +import org.junit.Test; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * Test for the Data class. + * + * @author Henning Staib + */ +public class DataTest { + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArgument() { + new Data(null); + } + + @Test + public void shouldBeOfIQTypeSET() { + DataPacketExtension dpe = mock(DataPacketExtension.class); + Data data = new Data(dpe); + assertEquals(IQ.Type.SET, data.getType()); + } + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void shouldReturnValidIQStanzaXML() throws Exception { + String encodedData = StringUtils.encodeBase64("Test"); + + String control = XMLBuilder.create("iq") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "kr91n475") + .a("type", "set") + .e("data") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("seq", "0") + .a("sid", "i781hf64") + .t(encodedData) + .asString(outputProperties); + + DataPacketExtension dpe = mock(DataPacketExtension.class); + String dataTag = XMLBuilder.create("data") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("seq", "0") + .a("sid", "i781hf64") + .t(encodedData) + .asString(outputProperties); + when(dpe.toXML()).thenReturn(dataTag); + Data data = new Data(dpe); + data.setFrom("romeo@montague.lit/orchard"); + data.setTo("juliet@capulet.lit/balcony"); + data.setPacketID("kr91n475"); + + assertXMLEqual(control, data.toXML()); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java index e5a79f798..cd8bbb074 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java @@ -1,107 +1,107 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.*; - -import java.util.Properties; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.junit.Test; - -import com.jamesmurty.utils.XMLBuilder; - -/** - * Test for the Open class. - * - * @author Henning Staib - */ -public class OpenTest { - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArguments1() { - new Open(null, 1); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArguments2() { - new Open("", 1); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotInstantiateWithInvalidArguments3() { - new Open("sessionID", -1); - } - - @Test - public void shouldSetIQStanzaAsDefault() { - Open open = new Open("sessionID", 4096); - assertEquals(StanzaType.IQ, open.getStanza()); - } - - @Test - public void shouldUseMessageStanzaIfGiven() { - Open open = new Open("sessionID", 4096, StanzaType.MESSAGE); - assertEquals(StanzaType.MESSAGE, open.getStanza()); - } - - @Test - public void shouldBeOfIQTypeSET() { - Open open = new Open("sessionID", 4096); - assertEquals(IQ.Type.SET, open.getType()); - } - - @Test - public void shouldSetAllFieldsCorrectly() { - Open open = new Open("sessionID", 4096, StanzaType.MESSAGE); - assertEquals("sessionID", open.getSessionID()); - assertEquals(4096, open.getBlockSize()); - assertEquals(StanzaType.MESSAGE, open.getStanza()); - } - - private static Properties outputProperties = new Properties(); - { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @Test - public void shouldReturnValidIQStanzaXML() throws Exception { - String control = XMLBuilder.create("iq") - .a("from", "romeo@montague.lit/orchard") - .a("to", "juliet@capulet.lit/balcony") - .a("id", "jn3h8g65") - .a("type", "set") - .e("open") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("block-size", "4096") - .a("sid", "i781hf64") - .a("stanza", "iq") - .asString(outputProperties); - - Open open = new Open("i781hf64", 4096, StanzaType.IQ); - open.setFrom("romeo@montague.lit/orchard"); - open.setTo("juliet@capulet.lit/balcony"); - open.setPacketID("jn3h8g65"); - - assertXMLEqual(control, open.toXML()); - } - - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static junit.framework.Assert.*; +import static org.custommonkey.xmlunit.XMLAssert.*; + +import java.util.Properties; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.junit.Test; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * Test for the Open class. + * + * @author Henning Staib + */ +public class OpenTest { + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArguments1() { + new Open(null, 1); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArguments2() { + new Open("", 1); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotInstantiateWithInvalidArguments3() { + new Open("sessionID", -1); + } + + @Test + public void shouldSetIQStanzaAsDefault() { + Open open = new Open("sessionID", 4096); + assertEquals(StanzaType.IQ, open.getStanza()); + } + + @Test + public void shouldUseMessageStanzaIfGiven() { + Open open = new Open("sessionID", 4096, StanzaType.MESSAGE); + assertEquals(StanzaType.MESSAGE, open.getStanza()); + } + + @Test + public void shouldBeOfIQTypeSET() { + Open open = new Open("sessionID", 4096); + assertEquals(IQ.Type.SET, open.getType()); + } + + @Test + public void shouldSetAllFieldsCorrectly() { + Open open = new Open("sessionID", 4096, StanzaType.MESSAGE); + assertEquals("sessionID", open.getSessionID()); + assertEquals(4096, open.getBlockSize()); + assertEquals(StanzaType.MESSAGE, open.getStanza()); + } + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void shouldReturnValidIQStanzaXML() throws Exception { + String control = XMLBuilder.create("iq") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "jn3h8g65") + .a("type", "set") + .e("open") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("block-size", "4096") + .a("sid", "i781hf64") + .a("stanza", "iq") + .asString(outputProperties); + + Open open = new Open("i781hf64", 4096, StanzaType.IQ); + open.setFrom("romeo@montague.lit/orchard"); + open.setTo("juliet@capulet.lit/balcony"); + open.setPacketID("jn3h8g65"); + + assertXMLEqual(control, open.toXML()); + } + + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProviderTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProviderTest.java index 03b108c8c..4ec1eceda 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProviderTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/provider/OpenIQProviderTest.java @@ -1,90 +1,90 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; - -import java.io.IOException; -import java.io.StringReader; -import java.util.Properties; - -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; -import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider; -import org.junit.Test; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import com.jamesmurty.utils.XMLBuilder; - -/** - * Test for the OpenIQProvider class. - * - * @author Henning Staib - */ -public class OpenIQProviderTest { - - private static Properties outputProperties = new Properties(); - { - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - } - - @Test - public void shouldCorrectlyParseIQStanzaAttribute() throws Exception { - String control = XMLBuilder.create("open") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("block-size", "4096") - .a("sid", "i781hf64") - .a("stanza", "iq") - .asString(outputProperties); - - OpenIQProvider oip = new OpenIQProvider(); - Open open = (Open) oip.parseIQ(getParser(control)); - - assertEquals(StanzaType.IQ, open.getStanza()); - } - - @Test - public void shouldCorrectlyParseMessageStanzaAttribute() throws Exception { - String control = XMLBuilder.create("open") - .a("xmlns", "http://jabber.org/protocol/ibb") - .a("block-size", "4096") - .a("sid", "i781hf64") - .a("stanza", "message") - .asString(outputProperties); - - OpenIQProvider oip = new OpenIQProvider(); - Open open = (Open) oip.parseIQ(getParser(control)); - - assertEquals(StanzaType.MESSAGE, open.getStanza()); - } - - private XmlPullParser getParser(String control) throws XmlPullParserException, - IOException { - XmlPullParser parser = new MXParser(); - parser.setInput(new StringReader(control)); - while (true) { - if (parser.next() == XmlPullParser.START_TAG - && parser.getName().equals("open")) { - break; - } - } - return parser; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Properties; + +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager.StanzaType; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; +import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider; +import org.junit.Test; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * Test for the OpenIQProvider class. + * + * @author Henning Staib + */ +public class OpenIQProviderTest { + + private static Properties outputProperties = new Properties(); + { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void shouldCorrectlyParseIQStanzaAttribute() throws Exception { + String control = XMLBuilder.create("open") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("block-size", "4096") + .a("sid", "i781hf64") + .a("stanza", "iq") + .asString(outputProperties); + + OpenIQProvider oip = new OpenIQProvider(); + Open open = (Open) oip.parseIQ(getParser(control)); + + assertEquals(StanzaType.IQ, open.getStanza()); + } + + @Test + public void shouldCorrectlyParseMessageStanzaAttribute() throws Exception { + String control = XMLBuilder.create("open") + .a("xmlns", "http://jabber.org/protocol/ibb") + .a("block-size", "4096") + .a("sid", "i781hf64") + .a("stanza", "message") + .asString(outputProperties); + + OpenIQProvider oip = new OpenIQProvider(); + Open open = (Open) oip.parseIQ(getParser(control)); + + assertEquals(StanzaType.MESSAGE, open.getStanza()); + } + + private XmlPullParser getParser(String control) throws XmlPullParserException, + IOException { + XmlPullParser parser = new MXParser(); + parser.setInput(new StringReader(control)); + while (true) { + if (parser.next() == XmlPullParser.START_TAG + && parser.getName().equals("open")) { + break; + } + } + return parser; + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java index 3fe925e28..f4ad302e9 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java @@ -1,311 +1,311 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.bytestreams.BytestreamRequest; -import org.jivesoftware.smackx.bytestreams.socks5.InitiationListener; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.powermock.reflect.Whitebox; - -/** - * Test for the InitiationListener class. - * - * @author Henning Staib - */ -public class InitiationListenerTest { - - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String proxyJID = "proxy.xmpp-server"; - String proxyAddress = "127.0.0.1"; - String sessionID = "session_id"; - - Connection connection; - Socks5BytestreamManager byteStreamManager; - InitiationListener initiationListener; - Bytestream initBytestream; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // mock connection - connection = mock(Connection.class); - - // create service discovery manager for mocked connection - ServiceDiscoveryManager.getInstanceFor(connection); - - // initialize Socks5ByteStreamManager to get the InitiationListener - byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // get the InitiationListener from Socks5ByteStreamManager - initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class); - - // create a SOCKS5 Bytestream initiation packet - initBytestream = Socks5PacketUtils.createBytestreamInitiation(initiatorJID, targetJID, - sessionID); - initBytestream.addStreamHost(proxyJID, proxyAddress, 7777); - - } - - /** - * If no listeners are registered for incoming SOCKS5 Bytestream requests, all request should be - * rejected with an error. - * - * @throws Exception should not happen - */ - @Test - public void shouldRespondWithError() throws Exception { - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the SOCKS5 Bytestream initiation - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), - argument.getValue().getError().getCondition()); - - } - - /** - * If a listener for all requests is registered it should be notified on incoming requests. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeListenerForAllRequests() throws Exception { - - // add listener - Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert that listener is called for the correct request - assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); - - } - - /** - * If a listener for a specific user in registered it should be notified on incoming requests - * for that user. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeListenerForUser() throws Exception { - - // add listener - Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); - - } - - /** - * If listener for a specific user is registered it should not be notified on incoming requests - * from other users. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotInvokeListenerForUser() throws Exception { - - // add listener for request of user "other_initiator" - Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // capture reply to the SOCKS5 Bytestream initiation - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendPacket(argument.capture()); - - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.ERROR, argument.getValue().getType()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), - argument.getValue().getError().getCondition()); - } - - /** - * If a user specific listener and an all requests listener is registered only the user specific - * listener should be notified. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception { - - // add listener for all request - Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "initiator" - Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is called once - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - - /** - * If a user specific listener and an all requests listener is registered only the all requests - * listener should be notified on an incoming request for another user. - * - * @throws Exception should not happen - */ - @Test - public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception { - - // add listener for all request - Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "other_initiator" - Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_" - + initiatorJID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - - /** - * If a request with a specific session ID should be ignored no listeners should be notified. - * - * @throws Exception should not happen - */ - @Test - public void shouldIgnoreSocks5BytestreamRequestOnce() throws Exception { - - // add listener for all request - Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(allRequestsListener); - - // add listener for request of user "initiator" - Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); - byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); - - // ignore session ID - byteStreamManager.ignoreBytestreamRequestOnce(sessionID); - - // run the listener with the initiation packet - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is not called - ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - // run the listener with the initiation packet again - initiationListener.processPacket(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // assert user request listener is called on the second request with the same session ID - verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); - - // assert all requests listener is not called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.BytestreamRequest; +import org.jivesoftware.smackx.bytestreams.socks5.InitiationListener; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.powermock.reflect.Whitebox; + +/** + * Test for the InitiationListener class. + * + * @author Henning Staib + */ +public class InitiationListenerTest { + + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String proxyJID = "proxy.xmpp-server"; + String proxyAddress = "127.0.0.1"; + String sessionID = "session_id"; + + Connection connection; + Socks5BytestreamManager byteStreamManager; + InitiationListener initiationListener; + Bytestream initBytestream; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // mock connection + connection = mock(Connection.class); + + // create service discovery manager for mocked connection + ServiceDiscoveryManager.getInstanceFor(connection); + + // initialize Socks5ByteStreamManager to get the InitiationListener + byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // get the InitiationListener from Socks5ByteStreamManager + initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class); + + // create a SOCKS5 Bytestream initiation packet + initBytestream = Socks5PacketUtils.createBytestreamInitiation(initiatorJID, targetJID, + sessionID); + initBytestream.addStreamHost(proxyJID, proxyAddress, 7777); + + } + + /** + * If no listeners are registered for incoming SOCKS5 Bytestream requests, all request should be + * rejected with an error. + * + * @throws Exception should not happen + */ + @Test + public void shouldRespondWithError() throws Exception { + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // capture reply to the SOCKS5 Bytestream initiation + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), + argument.getValue().getError().getCondition()); + + } + + /** + * If a listener for all requests is registered it should be notified on incoming requests. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeListenerForAllRequests() throws Exception { + + // add listener + Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert that listener is called for the correct request + assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); + + } + + /** + * If a listener for a specific user in registered it should be notified on incoming requests + * for that user. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeListenerForUser() throws Exception { + + // add listener + Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom()); + + } + + /** + * If listener for a specific user is registered it should not be notified on incoming requests + * from other users. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotInvokeListenerForUser() throws Exception { + + // add listener for request of user "other_initiator" + Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // capture reply to the SOCKS5 Bytestream initiation + ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); + verify(connection).sendPacket(argument.capture()); + + // assert that reply is the correct error packet + assertEquals(initiatorJID, argument.getValue().getTo()); + assertEquals(IQ.Type.ERROR, argument.getValue().getType()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), + argument.getValue().getError().getCondition()); + } + + /** + * If a user specific listener and an all requests listener is registered only the user specific + * listener should be notified. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception { + + // add listener for all request + Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "initiator" + Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is called once + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + + /** + * If a user specific listener and an all requests listener is registered only the all requests + * listener should be notified on an incoming request for another user. + * + * @throws Exception should not happen + */ + @Test + public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception { + + // add listener for all request + Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "other_initiator" + Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_" + + initiatorJID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + + /** + * If a request with a specific session ID should be ignored no listeners should be notified. + * + * @throws Exception should not happen + */ + @Test + public void shouldIgnoreSocks5BytestreamRequestOnce() throws Exception { + + // add listener for all request + Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(allRequestsListener); + + // add listener for request of user "initiator" + Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class); + byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID); + + // ignore session ID + byteStreamManager.ignoreBytestreamRequestOnce(sessionID); + + // run the listener with the initiation packet + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is not called + ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + // run the listener with the initiation packet again + initiationListener.processPacket(initBytestream); + + // wait because packet is processed in an extra thread + Thread.sleep(200); + + // assert user request listener is called on the second request with the same session ID + verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture()); + + // assert all requests listener is not called + byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); + verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java index 893b16ff9..1004df0b0 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java @@ -1,1098 +1,1098 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ConnectException; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.packet.IQ.Type; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; -import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.jivesoftware.util.Verification; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Test for Socks5BytestreamManager. - * - * @author Henning Staib - */ -public class Socks5ByteStreamManagerTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String proxyJID = "proxy.xmpp-server"; - String proxyAddress = "127.0.0.1"; - String sessionID = "session_id"; - - // protocol verifier - Protocol protocol; - - // mocked XMPP connection - Connection connection; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); - - } - - /** - * Test that {@link Socks5BytestreamManager#getBytestreamManager(Connection)} returns one - * bytestream manager for every connection - */ - @Test - public void shouldHaveOneManagerForEveryConnection() { - - // mock two connections - Connection connection1 = mock(Connection.class); - Connection connection2 = mock(Connection.class); - - /* - * create service discovery managers for the connections because the - * ConnectionCreationListener is not called when creating mocked connections - */ - ServiceDiscoveryManager.getInstanceFor(connection1); - ServiceDiscoveryManager.getInstanceFor(connection2); - - // get bytestream manager for the first connection twice - Socks5BytestreamManager conn1ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection1); - Socks5BytestreamManager conn1ByteStreamManager2 = Socks5BytestreamManager.getBytestreamManager(connection1); - - // get bytestream manager for second connection - Socks5BytestreamManager conn2ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection2); - - // assertions - assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2); - assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1); - - } - - /** - * The SOCKS5 Bytestream feature should be removed form the service discovery manager if Socks5 - * bytestream feature is disabled. - */ - @Test - public void shouldDisableService() { - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); - - assertTrue(discoveryManager.includesFeature(Socks5BytestreamManager.NAMESPACE)); - - byteStreamManager.disableService(); - - assertFalse(discoveryManager.includesFeature(Socks5BytestreamManager.NAMESPACE)); - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String)} should throw an exception - * if the given target does not support SOCKS5 Bytestream. - */ - @Test - public void shouldFailIfTargetDoesNotSupportSocks5() { - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - try { - // build empty discover info as reply if targets features are queried - DiscoverInfo discoverInfo = new DiscoverInfo(); - protocol.addResponse(discoverInfo); - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains("doesn't support SOCKS5 Bytestream")); - } - catch (IOException e) { - fail(e.getMessage()); - } - catch (InterruptedException e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if XMPP - * server doesn't return any proxies. - */ - @Test - public void shouldFailIfNoSocks5ProxyFound1() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items with no proxy items - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - - // return the item with no proxy if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - protocol.verifyAll(); - assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if no - * proxy is a SOCKS5 proxy. - */ - @Test - public void shouldFailIfNoSocks5ProxyFound2() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about NOT being a Socks5 - // proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("noproxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - protocol.verifyAll(); - assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if no - * SOCKS5 proxy can be found. If it turns out that a proxy is not a SOCKS5 proxy it should not - * be queried again. - */ - @Test - public void shouldBlacklistNonSocks5Proxies() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about NOT being a Socks5 - // proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("noproxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - protocol.verifyAll(); - assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - /* retry to establish SOCKS5 Bytestream */ - - // add responses for service discovery again - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - /* - * #verifyAll() tests if the number of requests and responses corresponds and should - * fail if the invalid proxy is queried again - */ - protocol.verifyAll(); - assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the - * target does not accept a SOCKS5 Bytestream. See XEP-0065 Section 5.2 A2 - */ - @Test - public void shouldFailIfTargetDoesNotAcceptSocks5Bytestream() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about being a SOCKS5 proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("proxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the socks5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build a socks5 stream host info containing the address and the port of the - // proxy - Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, - initiatorJID); - streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build error packet to reject SOCKS5 Bytestream - XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable); - IQ rejectPacket = new IQ() { - - public String getChildElementXML() { - return null; - } - - }; - rejectPacket.setType(Type.ERROR); - rejectPacket.setFrom(targetJID); - rejectPacket.setTo(initiatorJID); - rejectPacket.setError(xmppError); - - // return error packet as response to the bytestream initiation - protocol.addResponse(rejectPacket, Verification.correspondingSenderReceiver, - Verification.requestTypeSET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - protocol.verifyAll(); - assertEquals(xmppError, e.getXMPPError()); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the - * proxy used by target is invalid. - */ - @Test - public void shouldFailIfTargetUsesInvalidSocks5Proxy() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about being a SOCKS5 proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("proxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the socks5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build a socks5 stream host info containing the address and the port of the - // proxy - Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, - initiatorJID); - streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build used stream host response with unknown proxy - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost("invalid.proxy"); - - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, Verification.correspondingSenderReceiver, - Verification.requestTypeSET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - protocol.verifyAll(); - assertTrue(e.getMessage().contains("Remote user responded with unknown host")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if - * initiator can not connect to the SOCKS5 proxy used by target. - */ - @Test - public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about being a SOCKS5 proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("proxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the socks5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build a socks5 stream host info containing the address and the port of the - // proxy - Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, - initiatorJID); - streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build used stream host response - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost(proxyJID); - - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, new Verification() { - - public void verify(Bytestream request, Bytestream response) { - // verify SOCKS5 Bytestream request - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(1, request.getStreamHosts().size()); - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - try { - - // start SOCKS5 Bytestream - byteStreamManager.establishSession(targetJID, sessionID); - - fail("exception should be thrown"); - } - catch (IOException e) { - // initiator can't connect to proxy because it is not running - protocol.verifyAll(); - assertEquals(ConnectException.class, e.getClass()); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should successfully - * negotiate and return a SOCKS5 Bytestream connection. - * - * @throws Exception should not happen - */ - @Test - public void shouldNegotiateSocks5BytestreamAndTransferData() throws Exception { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - Item item = new Item(proxyJID); - discoverItems.addItem(item); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about being a SOCKS5 proxy - DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity = new Identity("proxy", proxyJID, "bytestreams"); - proxyInfo.addIdentity(identity); - - // return the socks5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build a socks5 stream host info containing the address and the port of the - // proxy - Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, - initiatorJID); - streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build used stream host response - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost(proxyJID); - - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, new Verification() { - - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(1, request.getStreamHosts().size()); - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - // build response to proxy activation - IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID, - initiatorJID); - - // return proxy activation response if proxy should be activated - protocol.addResponse(activationResponse, new Verification() { - - public void verify(Bytestream request, IQ response) { - assertEquals(targetJID, request.getToActivate().getTarget()); - } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // finally call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); - - byte[] result = new byte[3]; - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - } - - /** - * If multiple network addresses are added to the local SOCKS5 proxy, all of them should be - * contained in the SOCKS5 Bytestream request. - * - * @throws Exception should not happen - */ - @Test - public void shouldUseMultipleAddressesForLocalSocks5Proxy() throws Exception { - - // enable clients local SOCKS5 proxy on port 7778 - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - SmackConfiguration.setLocalSocks5ProxyPort(7778); - - // start a local SOCKS5 proxy - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); - socks5Proxy.start(); - assertTrue(socks5Proxy.isRunning()); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ - - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing no proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - - // return the discover item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build used stream host response - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost(initiatorJID); // local proxy used - - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, new Verification() { - - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(2, request.getStreamHosts().size()); - StreamHost streamHost1 = (StreamHost) request.getStreamHosts().toArray()[0]; - assertEquals(response.getUsedHost().getJID(), streamHost1.getJID()); - StreamHost streamHost2 = (StreamHost) request.getStreamHosts().toArray()[1]; - assertEquals(response.getUsedHost().getJID(), streamHost2.getJID()); - assertEquals("localAddress", streamHost2.getAddress()); - } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // connect to proxy as target - socks5Proxy.addTransfer(digest); - StreamHost streamHost = new StreamHost(targetJID, socks5Proxy.getLocalAddresses().get(0)); - streamHost.setPort(socks5Proxy.getPort()); - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - InputStream inputStream = socks5Client.getSocket(2000).getInputStream(); - - // add another network address before establishing SOCKS5 Bytestream - socks5Proxy.addLocalAddress("localAddress"); - - // finally call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); - - byte[] result = new byte[3]; - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - // reset proxy settings - socks5Proxy.stop(); - socks5Proxy.removeLocalAddress("localAddress"); - SmackConfiguration.setLocalSocks5ProxyPort(7777); - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} the first time - * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy and should - * prioritize this proxy for a second SOCKS5 Bytestream negotiation. - * - * @throws Exception should not happen - */ - @Test - public void shouldPrioritizeSecondSocks5ProxyOnSecondAttempt() throws Exception { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - assertTrue(byteStreamManager.isProxyPrioritizationEnabled()); - - Verification streamHostUsedVerification1 = new Verification() { - - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(2, request.getStreamHosts().size()); - // verify that the used stream host is the second in list - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } - - }; - createResponses(streamHostUsedVerification1); - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); - - byte[] result = new byte[3]; - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - Verification streamHostUsedVerification2 = new Verification() { - - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(2, request.getStreamHosts().size()); - // verify that the used stream host is the first in list - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } - - }; - createResponses(streamHostUsedVerification2); - - // call the method that should be tested again - outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - outputStream.write(data); - - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - } - - /** - * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} the first time - * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy. The second - * negotiation should run in the same manner if prioritization is disabled. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled() throws Exception { - - // disable clients local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - byteStreamManager.setProxyPrioritizationEnabled(false); - - assertFalse(byteStreamManager.isProxyPrioritizationEnabled()); - - Verification streamHostUsedVerification = new Verification() { - - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(2, request.getStreamHosts().size()); - // verify that the used stream host is the second in list - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } - - }; - createResponses(streamHostUsedVerification); - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); - - byte[] result = new byte[3]; - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - createResponses(streamHostUsedVerification); - - // call the method that should be tested again - outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - outputStream.write(data); - - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - byteStreamManager.setProxyPrioritizationEnabled(true); - - } - - private void createResponses(Verification streamHostUsedVerification) { - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); - - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover items containing a proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); - discoverItems.addItem(new Item("proxy2.xmpp-server")); - discoverItems.addItem(new Item(proxyJID)); - - // return the proxy item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - /* - * build discover info for proxy "proxy2.xmpp-server" containing information about being a - * SOCKS5 proxy - */ - DiscoverInfo proxyInfo1 = Socks5PacketUtils.createDiscoverInfo("proxy2.xmpp-server", - initiatorJID); - Identity identity1 = new Identity("proxy", "proxy2.xmpp-server", "bytestreams"); - identity1.setType("bytestreams"); - proxyInfo1.addIdentity(identity1); - - // return the SOCKS5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo1, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build discover info for proxy containing information about being a SOCKS5 proxy - DiscoverInfo proxyInfo2 = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); - Identity identity2 = new Identity("proxy", proxyJID, "bytestreams"); - proxyInfo2.addIdentity(identity2); - - // return the SOCKS5 bytestream proxy identity if proxy is queried - protocol.addResponse(proxyInfo2, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - /* - * build a SOCKS5 stream host info for "proxy2.xmpp-server" containing the address and the - * port of the proxy - */ - Bytestream streamHostInfo1 = Socks5PacketUtils.createBytestreamResponse( - "proxy2.xmpp-server", initiatorJID); - streamHostInfo1.addStreamHost("proxy2.xmpp-server", proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo1, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build a SOCKS5 stream host info containing the address and the port of the proxy - Bytestream streamHostInfo2 = Socks5PacketUtils.createBytestreamResponse(proxyJID, - initiatorJID); - streamHostInfo2.addStreamHost(proxyJID, proxyAddress, 7778); - - // return stream host info if it is queried - protocol.addResponse(streamHostInfo2, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); - - // build used stream host response - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost(proxyJID); - - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, streamHostUsedVerification, - Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - // build response to proxy activation - IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID, - initiatorJID); - - // return proxy activation response if proxy should be activated - protocol.addResponse(activationResponse, new Verification() { - - public void verify(Bytestream request, IQ response) { - assertEquals(targetJID, request.getToActivate().getTarget()); - } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - } - - /** - * Stop eventually started local SOCKS5 test proxy. - */ - @After - public void cleanUp() { - Socks5TestProxy.stopProxy(); - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ConnectException; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; +import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.jivesoftware.util.Verification; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test for Socks5BytestreamManager. + * + * @author Henning Staib + */ +public class Socks5ByteStreamManagerTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String proxyJID = "proxy.xmpp-server"; + String proxyAddress = "127.0.0.1"; + String sessionID = "session_id"; + + // protocol verifier + Protocol protocol; + + // mocked XMPP connection + Connection connection; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); + + } + + /** + * Test that {@link Socks5BytestreamManager#getBytestreamManager(Connection)} returns one + * bytestream manager for every connection + */ + @Test + public void shouldHaveOneManagerForEveryConnection() { + + // mock two connections + Connection connection1 = mock(Connection.class); + Connection connection2 = mock(Connection.class); + + /* + * create service discovery managers for the connections because the + * ConnectionCreationListener is not called when creating mocked connections + */ + ServiceDiscoveryManager.getInstanceFor(connection1); + ServiceDiscoveryManager.getInstanceFor(connection2); + + // get bytestream manager for the first connection twice + Socks5BytestreamManager conn1ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection1); + Socks5BytestreamManager conn1ByteStreamManager2 = Socks5BytestreamManager.getBytestreamManager(connection1); + + // get bytestream manager for second connection + Socks5BytestreamManager conn2ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection2); + + // assertions + assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2); + assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1); + + } + + /** + * The SOCKS5 Bytestream feature should be removed form the service discovery manager if Socks5 + * bytestream feature is disabled. + */ + @Test + public void shouldDisableService() { + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); + + assertTrue(discoveryManager.includesFeature(Socks5BytestreamManager.NAMESPACE)); + + byteStreamManager.disableService(); + + assertFalse(discoveryManager.includesFeature(Socks5BytestreamManager.NAMESPACE)); + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String)} should throw an exception + * if the given target does not support SOCKS5 Bytestream. + */ + @Test + public void shouldFailIfTargetDoesNotSupportSocks5() { + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + try { + // build empty discover info as reply if targets features are queried + DiscoverInfo discoverInfo = new DiscoverInfo(); + protocol.addResponse(discoverInfo); + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains("doesn't support SOCKS5 Bytestream")); + } + catch (IOException e) { + fail(e.getMessage()); + } + catch (InterruptedException e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if XMPP + * server doesn't return any proxies. + */ + @Test + public void shouldFailIfNoSocks5ProxyFound1() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items with no proxy items + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + + // return the item with no proxy if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + protocol.verifyAll(); + assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if no + * proxy is a SOCKS5 proxy. + */ + @Test + public void shouldFailIfNoSocks5ProxyFound2() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about NOT being a Socks5 + // proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("noproxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + protocol.verifyAll(); + assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if no + * SOCKS5 proxy can be found. If it turns out that a proxy is not a SOCKS5 proxy it should not + * be queried again. + */ + @Test + public void shouldBlacklistNonSocks5Proxies() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about NOT being a Socks5 + // proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("noproxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + protocol.verifyAll(); + assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + /* retry to establish SOCKS5 Bytestream */ + + // add responses for service discovery again + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + /* + * #verifyAll() tests if the number of requests and responses corresponds and should + * fail if the invalid proxy is queried again + */ + protocol.verifyAll(); + assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the + * target does not accept a SOCKS5 Bytestream. See XEP-0065 Section 5.2 A2 + */ + @Test + public void shouldFailIfTargetDoesNotAcceptSocks5Bytestream() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about being a SOCKS5 proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("proxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the socks5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build a socks5 stream host info containing the address and the port of the + // proxy + Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, + initiatorJID); + streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build error packet to reject SOCKS5 Bytestream + XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable); + IQ rejectPacket = new IQ() { + + public String getChildElementXML() { + return null; + } + + }; + rejectPacket.setType(Type.ERROR); + rejectPacket.setFrom(targetJID); + rejectPacket.setTo(initiatorJID); + rejectPacket.setError(xmppError); + + // return error packet as response to the bytestream initiation + protocol.addResponse(rejectPacket, Verification.correspondingSenderReceiver, + Verification.requestTypeSET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + protocol.verifyAll(); + assertEquals(xmppError, e.getXMPPError()); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the + * proxy used by target is invalid. + */ + @Test + public void shouldFailIfTargetUsesInvalidSocks5Proxy() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about being a SOCKS5 proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("proxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the socks5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build a socks5 stream host info containing the address and the port of the + // proxy + Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, + initiatorJID); + streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build used stream host response with unknown proxy + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost("invalid.proxy"); + + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, Verification.correspondingSenderReceiver, + Verification.requestTypeSET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + protocol.verifyAll(); + assertTrue(e.getMessage().contains("Remote user responded with unknown host")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if + * initiator can not connect to the SOCKS5 proxy used by target. + */ + @Test + public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about being a SOCKS5 proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("proxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the socks5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build a socks5 stream host info containing the address and the port of the + // proxy + Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, + initiatorJID); + streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build used stream host response + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost(proxyJID); + + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, new Verification() { + + public void verify(Bytestream request, Bytestream response) { + // verify SOCKS5 Bytestream request + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(1, request.getStreamHosts().size()); + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } + + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + try { + + // start SOCKS5 Bytestream + byteStreamManager.establishSession(targetJID, sessionID); + + fail("exception should be thrown"); + } + catch (IOException e) { + // initiator can't connect to proxy because it is not running + protocol.verifyAll(); + assertEquals(ConnectException.class, e.getClass()); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should successfully + * negotiate and return a SOCKS5 Bytestream connection. + * + * @throws Exception should not happen + */ + @Test + public void shouldNegotiateSocks5BytestreamAndTransferData() throws Exception { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + Item item = new Item(proxyJID); + discoverItems.addItem(item); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about being a SOCKS5 proxy + DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity = new Identity("proxy", proxyJID, "bytestreams"); + proxyInfo.addIdentity(identity); + + // return the socks5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build a socks5 stream host info containing the address and the port of the + // proxy + Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, + initiatorJID); + streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build used stream host response + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost(proxyJID); + + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, new Verification() { + + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(1, request.getStreamHosts().size()); + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } + + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + // build response to proxy activation + IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID, + initiatorJID); + + // return proxy activation response if proxy should be activated + protocol.addResponse(activationResponse, new Verification() { + + public void verify(Bytestream request, IQ response) { + assertEquals(targetJID, request.getToActivate().getTarget()); + } + + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + socks5Proxy.start(); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // finally call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); + + byte[] result = new byte[3]; + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + } + + /** + * If multiple network addresses are added to the local SOCKS5 proxy, all of them should be + * contained in the SOCKS5 Bytestream request. + * + * @throws Exception should not happen + */ + @Test + public void shouldUseMultipleAddressesForLocalSocks5Proxy() throws Exception { + + // enable clients local SOCKS5 proxy on port 7778 + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + SmackConfiguration.setLocalSocks5ProxyPort(7778); + + // start a local SOCKS5 proxy + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + socks5Proxy.start(); + assertTrue(socks5Proxy.isRunning()); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ + + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing no proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + + // return the discover item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build used stream host response + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost(initiatorJID); // local proxy used + + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, new Verification() { + + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(2, request.getStreamHosts().size()); + StreamHost streamHost1 = (StreamHost) request.getStreamHosts().toArray()[0]; + assertEquals(response.getUsedHost().getJID(), streamHost1.getJID()); + StreamHost streamHost2 = (StreamHost) request.getStreamHosts().toArray()[1]; + assertEquals(response.getUsedHost().getJID(), streamHost2.getJID()); + assertEquals("localAddress", streamHost2.getAddress()); + } + + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // connect to proxy as target + socks5Proxy.addTransfer(digest); + StreamHost streamHost = new StreamHost(targetJID, socks5Proxy.getLocalAddresses().get(0)); + streamHost.setPort(socks5Proxy.getPort()); + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + InputStream inputStream = socks5Client.getSocket(2000).getInputStream(); + + // add another network address before establishing SOCKS5 Bytestream + socks5Proxy.addLocalAddress("localAddress"); + + // finally call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); + + byte[] result = new byte[3]; + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + // reset proxy settings + socks5Proxy.stop(); + socks5Proxy.removeLocalAddress("localAddress"); + SmackConfiguration.setLocalSocks5ProxyPort(7777); + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} the first time + * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy and should + * prioritize this proxy for a second SOCKS5 Bytestream negotiation. + * + * @throws Exception should not happen + */ + @Test + public void shouldPrioritizeSecondSocks5ProxyOnSecondAttempt() throws Exception { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + assertTrue(byteStreamManager.isProxyPrioritizationEnabled()); + + Verification streamHostUsedVerification1 = new Verification() { + + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(2, request.getStreamHosts().size()); + // verify that the used stream host is the second in list + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } + + }; + createResponses(streamHostUsedVerification1); + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + socks5Proxy.start(); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); + + byte[] result = new byte[3]; + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + Verification streamHostUsedVerification2 = new Verification() { + + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(2, request.getStreamHosts().size()); + // verify that the used stream host is the first in list + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } + + }; + createResponses(streamHostUsedVerification2); + + // call the method that should be tested again + outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + outputStream.write(data); + + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + } + + /** + * Invoking {@link Socks5BytestreamManager#establishSession(String, String)} the first time + * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy. The second + * negotiation should run in the same manner if prioritization is disabled. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled() throws Exception { + + // disable clients local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setProxyPrioritizationEnabled(false); + + assertFalse(byteStreamManager.isProxyPrioritizationEnabled()); + + Verification streamHostUsedVerification = new Verification() { + + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(2, request.getStreamHosts().size()); + // verify that the used stream host is the second in list + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } + + }; + createResponses(streamHostUsedVerification); + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + socks5Proxy.start(); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); + + byte[] result = new byte[3]; + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + createResponses(streamHostUsedVerification); + + // call the method that should be tested again + outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + outputStream.write(data); + + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + + byteStreamManager.setProxyPrioritizationEnabled(true); + + } + + private void createResponses(Verification streamHostUsedVerification) { + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Socks5BytestreamManager.NAMESPACE); + + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover items containing a proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); + discoverItems.addItem(new Item("proxy2.xmpp-server")); + discoverItems.addItem(new Item(proxyJID)); + + // return the proxy item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + /* + * build discover info for proxy "proxy2.xmpp-server" containing information about being a + * SOCKS5 proxy + */ + DiscoverInfo proxyInfo1 = Socks5PacketUtils.createDiscoverInfo("proxy2.xmpp-server", + initiatorJID); + Identity identity1 = new Identity("proxy", "proxy2.xmpp-server", "bytestreams"); + identity1.setType("bytestreams"); + proxyInfo1.addIdentity(identity1); + + // return the SOCKS5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo1, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build discover info for proxy containing information about being a SOCKS5 proxy + DiscoverInfo proxyInfo2 = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID); + Identity identity2 = new Identity("proxy", proxyJID, "bytestreams"); + proxyInfo2.addIdentity(identity2); + + // return the SOCKS5 bytestream proxy identity if proxy is queried + protocol.addResponse(proxyInfo2, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + /* + * build a SOCKS5 stream host info for "proxy2.xmpp-server" containing the address and the + * port of the proxy + */ + Bytestream streamHostInfo1 = Socks5PacketUtils.createBytestreamResponse( + "proxy2.xmpp-server", initiatorJID); + streamHostInfo1.addStreamHost("proxy2.xmpp-server", proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo1, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build a SOCKS5 stream host info containing the address and the port of the proxy + Bytestream streamHostInfo2 = Socks5PacketUtils.createBytestreamResponse(proxyJID, + initiatorJID); + streamHostInfo2.addStreamHost(proxyJID, proxyAddress, 7778); + + // return stream host info if it is queried + protocol.addResponse(streamHostInfo2, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); + + // build used stream host response + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost(proxyJID); + + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, streamHostUsedVerification, + Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + // build response to proxy activation + IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID, + initiatorJID); + + // return proxy activation response if proxy should be activated + protocol.addResponse(activationResponse, new Verification() { + + public void verify(Bytestream request, IQ response) { + assertEquals(targetJID, request.getToActivate().getTarget()); + } + + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); + + } + + /** + * Stop eventually started local SOCKS5 test proxy. + */ + @After + public void cleanUp() { + Socks5TestProxy.stopProxy(); + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java index 6f41def30..2dda5b9ab 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java @@ -1,432 +1,432 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; - -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; -import java.net.Socket; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.SmackConfiguration; -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.smackx.bytestreams.socks5.Socks5BytestreamManager; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the Socks5BytestreamRequest class. - * - * @author Henning Staib - */ -public class Socks5ByteStreamRequestTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String proxyJID = "proxy.xmpp-server"; - String proxyAddress = "127.0.0.1"; - String sessionID = "session_id"; - - Protocol protocol; - - Connection connection; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, targetJID, xmppServer); - - } - - /** - * Accepting a SOCKS5 Bytestream request should fail if the request doesn't contain any Socks5 - * proxies. - * - * @throws Exception should not happen - */ - @Test - public void shouldFailIfRequestHasNoStreamHosts() throws Exception { - - try { - - // build SOCKS5 Bytestream initialization request with no SOCKS5 proxies - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( - byteStreamManager, bytestreamInitialization); - - // accept the stream (this is the call that is tested here) - byteStreamRequest.accept(); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); - } - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertTrue(IQ.class.isInstance(targetResponse)); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - ((IQ) targetResponse).getError().getCondition()); - - } - - /** - * Accepting a SOCKS5 Bytestream request should fail if target is not able to connect to any of - * the provided SOCKS5 proxies. - * - * @throws Exception - */ - @Test - public void shouldFailIfRequestHasInvalidStreamHosts() throws Exception { - - try { - - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - // add proxy that is not running - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( - byteStreamManager, bytestreamInitialization); - - // accept the stream (this is the call that is tested here) - byteStreamRequest.accept(); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); - } - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertTrue(IQ.class.isInstance(targetResponse)); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - ((IQ) targetResponse).getError().getCondition()); - - } - - /** - * Target should not try to connect to SOCKS5 proxies that already failed twice. - * - * @throws Exception should not happen - */ - @Test - public void shouldBlacklistInvalidProxyAfter2Failures() throws Exception { - - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778); - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // try to connect several times - for (int i = 0; i < 2; i++) { - try { - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( - byteStreamManager, bytestreamInitialization); - - // set timeouts - byteStreamRequest.setTotalConnectTimeout(600); - byteStreamRequest.setMinimumConnectTimeout(300); - - // accept the stream (this is the call that is tested here) - byteStreamRequest.accept(); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains( - "Could not establish socket with any provided host")); - } - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertTrue(IQ.class.isInstance(targetResponse)); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - ((IQ) targetResponse).getError().getCondition()); - } - - // create test data for stream - byte[] data = new byte[] { 1, 2, 3 }; - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7779); - - assertTrue(socks5Proxy.isRunning()); - - // add a valid SOCKS5 proxy - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); - - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); - - // set timeouts - byteStreamRequest.setTotalConnectTimeout(600); - byteStreamRequest.setMinimumConnectTimeout(300); - - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); - - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); - - } - - /** - * Target should not not blacklist any SOCKS5 proxies regardless of failing connections. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotBlacklistInvalidProxy() throws Exception { - - // disable blacklisting - Socks5BytestreamRequest.setConnectFailureThreshold(0); - - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778); - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // try to connect several times - for (int i = 0; i < 10; i++) { - try { - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( - byteStreamManager, bytestreamInitialization); - - // set timeouts - byteStreamRequest.setTotalConnectTimeout(600); - byteStreamRequest.setMinimumConnectTimeout(300); - - // accept the stream (this is the call that is tested here) - byteStreamRequest.accept(); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains( - "Could not establish socket with any provided host")); - } - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertTrue(IQ.class.isInstance(targetResponse)); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), - ((IQ) targetResponse).getError().getCondition()); - } - - // enable blacklisting - Socks5BytestreamRequest.setConnectFailureThreshold(2); - - } - - /** - * If the SOCKS5 Bytestream request contains multiple SOCKS5 proxies and the first one doesn't - * respond, the connection attempt to this proxy should not consume the whole timeout for - * connecting to the proxies. - * - * @throws Exception should not happen - */ - @Test - public void shouldNotTimeoutIfFirstSocks5ProxyDoesNotRespond() throws Exception { - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - - // create a fake SOCKS5 proxy that doesn't respond to a request - ServerSocket serverSocket = new ServerSocket(7779); - - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); - - // create test data for stream - byte[] data = new byte[] { 1, 2, 3 }; - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); - - // set timeouts - byteStreamRequest.setTotalConnectTimeout(2000); - byteStreamRequest.setMinimumConnectTimeout(1000); - - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); - - // assert that client tries to connect to dumb SOCKS5 proxy - Socket socket = serverSocket.accept(); - assertNotNull(socket); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); - - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); - - serverSocket.close(); - - } - - /** - * Accepting the SOCKS5 Bytestream request should be successfully. - * - * @throws Exception should not happen - */ - @Test - public void shouldAcceptSocks5BytestreamRequestAndReceiveData() throws Exception { - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); - - // create test data for stream - byte[] data = new byte[] { 1, 2, 3 }; - - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); - - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); - - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Packet targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); - - } - - /** - * Stop eventually started local SOCKS5 test proxy. - */ - @After - public void cleanUp() { - Socks5TestProxy.stopProxy(); - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.SmackConfiguration; +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.smackx.bytestreams.socks5.Socks5BytestreamManager; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the Socks5BytestreamRequest class. + * + * @author Henning Staib + */ +public class Socks5ByteStreamRequestTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String proxyJID = "proxy.xmpp-server"; + String proxyAddress = "127.0.0.1"; + String sessionID = "session_id"; + + Protocol protocol; + + Connection connection; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, targetJID, xmppServer); + + } + + /** + * Accepting a SOCKS5 Bytestream request should fail if the request doesn't contain any Socks5 + * proxies. + * + * @throws Exception should not happen + */ + @Test + public void shouldFailIfRequestHasNoStreamHosts() throws Exception { + + try { + + // build SOCKS5 Bytestream initialization request with no SOCKS5 proxies + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( + byteStreamManager, bytestreamInitialization); + + // accept the stream (this is the call that is tested here) + byteStreamRequest.accept(); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); + } + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertTrue(IQ.class.isInstance(targetResponse)); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + ((IQ) targetResponse).getError().getCondition()); + + } + + /** + * Accepting a SOCKS5 Bytestream request should fail if target is not able to connect to any of + * the provided SOCKS5 proxies. + * + * @throws Exception + */ + @Test + public void shouldFailIfRequestHasInvalidStreamHosts() throws Exception { + + try { + + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + // add proxy that is not running + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( + byteStreamManager, bytestreamInitialization); + + // accept the stream (this is the call that is tested here) + byteStreamRequest.accept(); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); + } + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertTrue(IQ.class.isInstance(targetResponse)); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + ((IQ) targetResponse).getError().getCondition()); + + } + + /** + * Target should not try to connect to SOCKS5 proxies that already failed twice. + * + * @throws Exception should not happen + */ + @Test + public void shouldBlacklistInvalidProxyAfter2Failures() throws Exception { + + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778); + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // try to connect several times + for (int i = 0; i < 2; i++) { + try { + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( + byteStreamManager, bytestreamInitialization); + + // set timeouts + byteStreamRequest.setTotalConnectTimeout(600); + byteStreamRequest.setMinimumConnectTimeout(300); + + // accept the stream (this is the call that is tested here) + byteStreamRequest.accept(); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains( + "Could not establish socket with any provided host")); + } + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertTrue(IQ.class.isInstance(targetResponse)); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + ((IQ) targetResponse).getError().getCondition()); + } + + // create test data for stream + byte[] data = new byte[] { 1, 2, 3 }; + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7779); + + assertTrue(socks5Proxy.isRunning()); + + // add a valid SOCKS5 proxy + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); + + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); + + // set timeouts + byteStreamRequest.setTotalConnectTimeout(600); + byteStreamRequest.setMinimumConnectTimeout(300); + + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); + + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + + } + + /** + * Target should not not blacklist any SOCKS5 proxies regardless of failing connections. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotBlacklistInvalidProxy() throws Exception { + + // disable blacklisting + Socks5BytestreamRequest.setConnectFailureThreshold(0); + + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778); + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // try to connect several times + for (int i = 0; i < 10; i++) { + try { + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest( + byteStreamManager, bytestreamInitialization); + + // set timeouts + byteStreamRequest.setTotalConnectTimeout(600); + byteStreamRequest.setMinimumConnectTimeout(300); + + // accept the stream (this is the call that is tested here) + byteStreamRequest.accept(); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains( + "Could not establish socket with any provided host")); + } + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertTrue(IQ.class.isInstance(targetResponse)); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType()); + assertEquals(XMPPError.Condition.item_not_found.toString(), + ((IQ) targetResponse).getError().getCondition()); + } + + // enable blacklisting + Socks5BytestreamRequest.setConnectFailureThreshold(2); + + } + + /** + * If the SOCKS5 Bytestream request contains multiple SOCKS5 proxies and the first one doesn't + * respond, the connection attempt to this proxy should not consume the whole timeout for + * connecting to the proxies. + * + * @throws Exception should not happen + */ + @Test + public void shouldNotTimeoutIfFirstSocks5ProxyDoesNotRespond() throws Exception { + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + + // create a fake SOCKS5 proxy that doesn't respond to a request + ServerSocket serverSocket = new ServerSocket(7779); + + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); + + // create test data for stream + byte[] data = new byte[] { 1, 2, 3 }; + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); + + // set timeouts + byteStreamRequest.setTotalConnectTimeout(2000); + byteStreamRequest.setMinimumConnectTimeout(1000); + + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); + + // assert that client tries to connect to dumb SOCKS5 proxy + Socket socket = serverSocket.accept(); + assertNotNull(socket); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); + + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + + serverSocket.close(); + + } + + /** + * Accepting the SOCKS5 Bytestream request should be successfully. + * + * @throws Exception should not happen + */ + @Test + public void shouldAcceptSocks5BytestreamRequestAndReceiveData() throws Exception { + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); + + // create test data for stream + byte[] data = new byte[] { 1, 2, 3 }; + + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); + + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); + + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Packet targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + + } + + /** + * Stop eventually started local SOCKS5 test proxy. + */ + @After + public void cleanUp() { + Socks5TestProxy.stopProxy(); + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java index 630e653eb..a6bcf3abd 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java @@ -1,313 +1,313 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; - -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.packet.IQ.Type; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiator; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; -import org.jivesoftware.util.ConnectionUtils; -import org.jivesoftware.util.Protocol; -import org.jivesoftware.util.Verification; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Test for Socks5ClientForInitiator class. - * - * @author Henning Staib - */ -public class Socks5ClientForInitiatorTest { - - // settings - String initiatorJID = "initiator@xmpp-server/Smack"; - String targetJID = "target@xmpp-server/Smack"; - String xmppServer = "xmpp-server"; - String proxyJID = "proxy.xmpp-server"; - String proxyAddress = "127.0.0.1"; - int proxyPort = 7890; - String sessionID = "session_id"; - - // protocol verifier - Protocol protocol; - - // mocked XMPP connection - Connection connection; - - /** - * Initialize fields used in the tests. - */ - @Before - public void setup() { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); - - } - - /** - * If the target is not connected to the local SOCKS5 proxy an exception should be thrown. - * - * @throws Exception should not happen - */ - @Test - public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception { - - // start a local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyPort(proxyPort); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); - socks5Proxy.start(); - - // build stream host information for local SOCKS5 proxy - StreamHost streamHost = new StreamHost(connection.getUser(), - socks5Proxy.getLocalAddresses().get(0)); - streamHost.setPort(socks5Proxy.getPort()); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - - try { - socks5Client.getSocket(10000); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy")); - protocol.verifyAll(); // assert no XMPP messages were sent - } - - socks5Proxy.stop(); - - } - - /** - * Initiator and target should successfully connect to the local SOCKS5 proxy. - * - * @throws Exception should not happen - */ - @Test - public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception { - - // start a local SOCKS5 proxy - SmackConfiguration.setLocalSocks5ProxyPort(proxyPort); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); - socks5Proxy.start(); - - // test data - final byte[] data = new byte[] { 1, 2, 3 }; - - // create digest - final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // allow connection of target with this digest - socks5Proxy.addTransfer(digest); - - // build stream host information - final StreamHost streamHost = new StreamHost(connection.getUser(), - socks5Proxy.getLocalAddresses().get(0)); - streamHost.setPort(socks5Proxy.getPort()); - - // target connects to local SOCKS5 proxy - Thread targetThread = new Thread() { - - @Override - public void run() { - try { - Socks5Client targetClient = new Socks5Client(streamHost, digest); - Socket socket = targetClient.getSocket(10000); - socket.getOutputStream().write(data); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetThread.start(); - - Thread.sleep(200); - - // initiator connects - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - - Socket socket = socks5Client.getSocket(10000); - - // verify test data - InputStream in = socket.getInputStream(); - for (int i = 0; i < data.length; i++) { - assertEquals(data[i], in.read()); - } - - targetThread.join(); - - protocol.verifyAll(); // assert no XMPP messages were sent - - socks5Proxy.removeTransfer(digest); - socks5Proxy.stop(); - - } - - /** - * If the initiator can connect to a SOCKS5 proxy but activating the stream fails an exception - * should be thrown. - * - * @throws Exception should not happen - */ - @Test - public void shouldFailIfActivateSocks5ProxyFails() throws Exception { - - // build error response as reply to the stream activation - XMPPError xmppError = new XMPPError(XMPPError.Condition.interna_server_error); - IQ error = new IQ() { - - public String getChildElementXML() { - return null; - } - - }; - error.setType(Type.ERROR); - error.setFrom(proxyJID); - error.setTo(initiatorJID); - error.setError(xmppError); - - protocol.addResponse(error, Verification.correspondingSenderReceiver, - Verification.requestTypeSET); - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); - socks5Proxy.start(); - - StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); - streamHost.setPort(socks5Proxy.getPort()); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - - try { - - socks5Client.getSocket(10000); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains("activating SOCKS5 Bytestream failed")); - protocol.verifyAll(); - } - - socks5Proxy.stop(); - } - - /** - * Target and initiator should successfully connect to a "remote" SOCKS5 proxy and the initiator - * activates the bytestream. - * - * @throws Exception should not happen - */ - @Test - public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception { - - // build activation confirmation response - IQ activationResponse = new IQ() { - - @Override - public String getChildElementXML() { - return null; - } - - }; - activationResponse.setFrom(proxyJID); - activationResponse.setTo(initiatorJID); - activationResponse.setType(IQ.Type.RESULT); - - protocol.addResponse(activationResponse, Verification.correspondingSenderReceiver, - Verification.requestTypeSET, new Verification() { - - public void verify(Bytestream request, IQ response) { - // verify that the correct stream should be activated - assertNotNull(request.getToActivate()); - assertEquals(targetJID, request.getToActivate().getTarget()); - } - - }); - - // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); - socks5Proxy.start(); - - StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); - streamHost.setPort(socks5Proxy.getPort()); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - - Socket initiatorSocket = socks5Client.getSocket(10000); - InputStream in = initiatorSocket.getInputStream(); - - Socket targetSocket = socks5Proxy.getSocket(digest); - OutputStream out = targetSocket.getOutputStream(); - - // verify test data - for (int i = 0; i < 10; i++) { - out.write(i); - assertEquals(i, in.read()); - } - - protocol.verifyAll(); - - initiatorSocket.close(); - targetSocket.close(); - socks5Proxy.stop(); - - } - - /** - * Reset default port for local SOCKS5 proxy. - */ - @After - public void cleanup() { - SmackConfiguration.setLocalSocks5ProxyPort(7777); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiator; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; +import org.jivesoftware.util.ConnectionUtils; +import org.jivesoftware.util.Protocol; +import org.jivesoftware.util.Verification; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test for Socks5ClientForInitiator class. + * + * @author Henning Staib + */ +public class Socks5ClientForInitiatorTest { + + // settings + String initiatorJID = "initiator@xmpp-server/Smack"; + String targetJID = "target@xmpp-server/Smack"; + String xmppServer = "xmpp-server"; + String proxyJID = "proxy.xmpp-server"; + String proxyAddress = "127.0.0.1"; + int proxyPort = 7890; + String sessionID = "session_id"; + + // protocol verifier + Protocol protocol; + + // mocked XMPP connection + Connection connection; + + /** + * Initialize fields used in the tests. + */ + @Before + public void setup() { + + // build protocol verifier + protocol = new Protocol(); + + // create mocked XMPP connection + connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); + + } + + /** + * If the target is not connected to the local SOCKS5 proxy an exception should be thrown. + * + * @throws Exception should not happen + */ + @Test + public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception { + + // start a local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyPort(proxyPort); + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + socks5Proxy.start(); + + // build stream host information for local SOCKS5 proxy + StreamHost streamHost = new StreamHost(connection.getUser(), + socks5Proxy.getLocalAddresses().get(0)); + streamHost.setPort(socks5Proxy.getPort()); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + try { + socks5Client.getSocket(10000); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy")); + protocol.verifyAll(); // assert no XMPP messages were sent + } + + socks5Proxy.stop(); + + } + + /** + * Initiator and target should successfully connect to the local SOCKS5 proxy. + * + * @throws Exception should not happen + */ + @Test + public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception { + + // start a local SOCKS5 proxy + SmackConfiguration.setLocalSocks5ProxyPort(proxyPort); + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + socks5Proxy.start(); + + // test data + final byte[] data = new byte[] { 1, 2, 3 }; + + // create digest + final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // allow connection of target with this digest + socks5Proxy.addTransfer(digest); + + // build stream host information + final StreamHost streamHost = new StreamHost(connection.getUser(), + socks5Proxy.getLocalAddresses().get(0)); + streamHost.setPort(socks5Proxy.getPort()); + + // target connects to local SOCKS5 proxy + Thread targetThread = new Thread() { + + @Override + public void run() { + try { + Socks5Client targetClient = new Socks5Client(streamHost, digest); + Socket socket = targetClient.getSocket(10000); + socket.getOutputStream().write(data); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetThread.start(); + + Thread.sleep(200); + + // initiator connects + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + Socket socket = socks5Client.getSocket(10000); + + // verify test data + InputStream in = socket.getInputStream(); + for (int i = 0; i < data.length; i++) { + assertEquals(data[i], in.read()); + } + + targetThread.join(); + + protocol.verifyAll(); // assert no XMPP messages were sent + + socks5Proxy.removeTransfer(digest); + socks5Proxy.stop(); + + } + + /** + * If the initiator can connect to a SOCKS5 proxy but activating the stream fails an exception + * should be thrown. + * + * @throws Exception should not happen + */ + @Test + public void shouldFailIfActivateSocks5ProxyFails() throws Exception { + + // build error response as reply to the stream activation + XMPPError xmppError = new XMPPError(XMPPError.Condition.interna_server_error); + IQ error = new IQ() { + + public String getChildElementXML() { + return null; + } + + }; + error.setType(Type.ERROR); + error.setFrom(proxyJID); + error.setTo(initiatorJID); + error.setError(xmppError); + + protocol.addResponse(error, Verification.correspondingSenderReceiver, + Verification.requestTypeSET); + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); + socks5Proxy.start(); + + StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); + streamHost.setPort(socks5Proxy.getPort()); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + try { + + socks5Client.getSocket(10000); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains("activating SOCKS5 Bytestream failed")); + protocol.verifyAll(); + } + + socks5Proxy.stop(); + } + + /** + * Target and initiator should successfully connect to a "remote" SOCKS5 proxy and the initiator + * activates the bytestream. + * + * @throws Exception should not happen + */ + @Test + public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception { + + // build activation confirmation response + IQ activationResponse = new IQ() { + + @Override + public String getChildElementXML() { + return null; + } + + }; + activationResponse.setFrom(proxyJID); + activationResponse.setTo(initiatorJID); + activationResponse.setType(IQ.Type.RESULT); + + protocol.addResponse(activationResponse, Verification.correspondingSenderReceiver, + Verification.requestTypeSET, new Verification() { + + public void verify(Bytestream request, IQ response) { + // verify that the correct stream should be activated + assertNotNull(request.getToActivate()); + assertEquals(targetJID, request.getToActivate().getTarget()); + } + + }); + + // start a local SOCKS5 proxy + Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); + socks5Proxy.start(); + + StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); + streamHost.setPort(socks5Proxy.getPort()); + + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + Socket initiatorSocket = socks5Client.getSocket(10000); + InputStream in = initiatorSocket.getInputStream(); + + Socket targetSocket = socks5Proxy.getSocket(digest); + OutputStream out = targetSocket.getOutputStream(); + + // verify test data + for (int i = 0; i < 10; i++) { + out.write(i); + assertEquals(i, in.read()); + } + + protocol.verifyAll(); + + initiatorSocket.close(); + targetSocket.close(); + socks5Proxy.stop(); + + } + + /** + * Reset default port for local SOCKS5 proxy. + */ + @After + public void cleanup() { + SmackConfiguration.setLocalSocks5ProxyPort(7777); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientTest.java index 6d578e1ab..f6cf65e79 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientTest.java @@ -1,335 +1,335 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.net.ServerSocket; -import java.net.Socket; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Test for Socks5Client class. - * - * @author Henning Staib - */ -public class Socks5ClientTest { - - // settings - private String serverAddress = "127.0.0.1"; - private int serverPort = 7890; - private String proxyJID = "proxy.xmpp-server"; - private String digest = "digest"; - private ServerSocket serverSocket; - - /** - * Initialize fields used in the tests. - * - * @throws Exception should not happen - */ - @Before - public void setup() throws Exception { - // create SOCKS5 proxy server socket - serverSocket = new ServerSocket(serverPort); - } - - /** - * A SOCKS5 client MUST close connection if server doesn't accept any of the given - * authentication methods. (See RFC1928 Section 3) - * - * @throws Exception should not happen - */ - @Test - public void shouldCloseSocketIfServerDoesNotAcceptAuthenticationMethod() throws Exception { - - // start thread to connect to SOCKS5 proxy - Thread serverThread = new Thread() { - - @Override - public void run() { - StreamHost streamHost = new StreamHost(proxyJID, serverAddress); - streamHost.setPort(serverPort); - - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - - try { - - socks5Client.getSocket(10000); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains( - "establishing connection to SOCKS5 proxy failed")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - }; - serverThread.start(); - - // accept connection form client - Socket socket = serverSocket.accept(); - DataInputStream in = new DataInputStream(socket.getInputStream()); - DataOutputStream out = new DataOutputStream(socket.getOutputStream()); - - // validate authentication request - assertEquals((byte) 0x05, (byte) in.read()); // version - assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods - assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method - - // respond that no authentication method is accepted - out.write(new byte[] { (byte) 0x05, (byte) 0xFF }); - out.flush(); - - // wait for client to shutdown - serverThread.join(); - - // assert socket is closed - assertEquals(-1, in.read()); - - } - - /** - * The SOCKS5 client should close connection if server replies in an unsupported way. - * - * @throws Exception should not happen - */ - @Test - public void shouldCloseSocketIfServerRepliesInUnsupportedWay() throws Exception { - - // start thread to connect to SOCKS5 proxy - Thread serverThread = new Thread() { - - @Override - public void run() { - StreamHost streamHost = new StreamHost(proxyJID, serverAddress); - streamHost.setPort(serverPort); - - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - try { - socks5Client.getSocket(10000); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains( - "establishing connection to SOCKS5 proxy failed")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - }; - serverThread.start(); - - // accept connection from client - Socket socket = serverSocket.accept(); - DataInputStream in = new DataInputStream(socket.getInputStream()); - DataOutputStream out = new DataOutputStream(socket.getOutputStream()); - - // validate authentication request - assertEquals((byte) 0x05, (byte) in.read()); // version - assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods - assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method - - // respond that no no-authentication method is used - out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); - out.flush(); - - Socks5Utils.receiveSocks5Message(in); - - // reply with unsupported address type - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 }); - out.flush(); - - // wait for client to shutdown - serverThread.join(); - - // assert socket is closed - assertEquals(-1, in.read()); - - } - - /** - * The SOCKS5 client should close connection if server replies with an error. - * - * @throws Exception should not happen - */ - @Test - public void shouldCloseSocketIfServerRepliesWithError() throws Exception { - - // start thread to connect to SOCKS5 proxy - Thread serverThread = new Thread() { - - @Override - public void run() { - StreamHost streamHost = new StreamHost(proxyJID, serverAddress); - streamHost.setPort(serverPort); - - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - try { - socks5Client.getSocket(10000); - - fail("exception should be thrown"); - } - catch (XMPPException e) { - assertTrue(e.getMessage().contains( - "establishing connection to SOCKS5 proxy failed")); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - }; - serverThread.start(); - - Socket socket = serverSocket.accept(); - DataInputStream in = new DataInputStream(socket.getInputStream()); - DataOutputStream out = new DataOutputStream(socket.getOutputStream()); - - // validate authentication request - assertEquals((byte) 0x05, (byte) in.read()); // version - assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods - assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method - - // respond that no no-authentication method is used - out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); - out.flush(); - - Socks5Utils.receiveSocks5Message(in); - - // reply with full SOCKS5 message with an error code (01 = general SOCKS server - // failure) - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x03 }); - byte[] address = digest.getBytes(); - out.write(address.length); - out.write(address); - out.write(new byte[] { (byte) 0x00, (byte) 0x00 }); - out.flush(); - - // wait for client to shutdown - serverThread.join(); - - // assert socket is closed - assertEquals(-1, in.read()); - - } - - /** - * The SOCKS5 client should successfully connect to the SOCKS5 server - * - * @throws Exception should not happen - */ - @Test - public void shouldSuccessfullyConnectToSocks5Server() throws Exception { - - // start thread to connect to SOCKS5 proxy - Thread serverThread = new Thread() { - - @Override - public void run() { - StreamHost streamHost = new StreamHost(proxyJID, serverAddress); - streamHost.setPort(serverPort); - - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - - try { - Socket socket = socks5Client.getSocket(10000); - assertNotNull(socket); - socket.getOutputStream().write(123); - socket.close(); - } - catch (Exception e) { - fail(e.getMessage()); - } - - } - - }; - serverThread.start(); - - Socket socket = serverSocket.accept(); - DataInputStream in = new DataInputStream(socket.getInputStream()); - DataOutputStream out = new DataOutputStream(socket.getOutputStream()); - - // validate authentication request - assertEquals((byte) 0x05, (byte) in.read()); // version - assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods - assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method - - // respond that no no-authentication method is used - out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); - out.flush(); - - byte[] address = digest.getBytes(); - - assertEquals((byte) 0x05, (byte) in.read()); // version - assertEquals((byte) 0x01, (byte) in.read()); // connect request - assertEquals((byte) 0x00, (byte) in.read()); // reserved byte (always 0) - assertEquals((byte) 0x03, (byte) in.read()); // address type (domain) - assertEquals(address.length, (byte) in.read()); // address length - for (int i = 0; i < address.length; i++) { - assertEquals(address[i], (byte) in.read()); // address - } - assertEquals((byte) 0x00, (byte) in.read()); // port - assertEquals((byte) 0x00, (byte) in.read()); - - // reply with success SOCKS5 message - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03 }); - out.write(address.length); - out.write(address); - out.write(new byte[] { (byte) 0x00, (byte) 0x00 }); - out.flush(); - - // wait for client to shutdown - serverThread.join(); - - // verify data sent from client - assertEquals(123, in.read()); - - // assert socket is closed - assertEquals(-1, in.read()); - - } - - /** - * Close fake SOCKS5 proxy. - * - * @throws Exception should not happen - */ - @After - public void cleanup() throws Exception { - serverSocket.close(); - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.*; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.net.ServerSocket; +import java.net.Socket; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test for Socks5Client class. + * + * @author Henning Staib + */ +public class Socks5ClientTest { + + // settings + private String serverAddress = "127.0.0.1"; + private int serverPort = 7890; + private String proxyJID = "proxy.xmpp-server"; + private String digest = "digest"; + private ServerSocket serverSocket; + + /** + * Initialize fields used in the tests. + * + * @throws Exception should not happen + */ + @Before + public void setup() throws Exception { + // create SOCKS5 proxy server socket + serverSocket = new ServerSocket(serverPort); + } + + /** + * A SOCKS5 client MUST close connection if server doesn't accept any of the given + * authentication methods. (See RFC1928 Section 3) + * + * @throws Exception should not happen + */ + @Test + public void shouldCloseSocketIfServerDoesNotAcceptAuthenticationMethod() throws Exception { + + // start thread to connect to SOCKS5 proxy + Thread serverThread = new Thread() { + + @Override + public void run() { + StreamHost streamHost = new StreamHost(proxyJID, serverAddress); + streamHost.setPort(serverPort); + + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + + try { + + socks5Client.getSocket(10000); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains( + "establishing connection to SOCKS5 proxy failed")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + }; + serverThread.start(); + + // accept connection form client + Socket socket = serverSocket.accept(); + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + // validate authentication request + assertEquals((byte) 0x05, (byte) in.read()); // version + assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods + assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method + + // respond that no authentication method is accepted + out.write(new byte[] { (byte) 0x05, (byte) 0xFF }); + out.flush(); + + // wait for client to shutdown + serverThread.join(); + + // assert socket is closed + assertEquals(-1, in.read()); + + } + + /** + * The SOCKS5 client should close connection if server replies in an unsupported way. + * + * @throws Exception should not happen + */ + @Test + public void shouldCloseSocketIfServerRepliesInUnsupportedWay() throws Exception { + + // start thread to connect to SOCKS5 proxy + Thread serverThread = new Thread() { + + @Override + public void run() { + StreamHost streamHost = new StreamHost(proxyJID, serverAddress); + streamHost.setPort(serverPort); + + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + try { + socks5Client.getSocket(10000); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains( + "establishing connection to SOCKS5 proxy failed")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + }; + serverThread.start(); + + // accept connection from client + Socket socket = serverSocket.accept(); + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + // validate authentication request + assertEquals((byte) 0x05, (byte) in.read()); // version + assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods + assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method + + // respond that no no-authentication method is used + out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); + out.flush(); + + Socks5Utils.receiveSocks5Message(in); + + // reply with unsupported address type + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 }); + out.flush(); + + // wait for client to shutdown + serverThread.join(); + + // assert socket is closed + assertEquals(-1, in.read()); + + } + + /** + * The SOCKS5 client should close connection if server replies with an error. + * + * @throws Exception should not happen + */ + @Test + public void shouldCloseSocketIfServerRepliesWithError() throws Exception { + + // start thread to connect to SOCKS5 proxy + Thread serverThread = new Thread() { + + @Override + public void run() { + StreamHost streamHost = new StreamHost(proxyJID, serverAddress); + streamHost.setPort(serverPort); + + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + try { + socks5Client.getSocket(10000); + + fail("exception should be thrown"); + } + catch (XMPPException e) { + assertTrue(e.getMessage().contains( + "establishing connection to SOCKS5 proxy failed")); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + }; + serverThread.start(); + + Socket socket = serverSocket.accept(); + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + // validate authentication request + assertEquals((byte) 0x05, (byte) in.read()); // version + assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods + assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method + + // respond that no no-authentication method is used + out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); + out.flush(); + + Socks5Utils.receiveSocks5Message(in); + + // reply with full SOCKS5 message with an error code (01 = general SOCKS server + // failure) + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x03 }); + byte[] address = digest.getBytes(); + out.write(address.length); + out.write(address); + out.write(new byte[] { (byte) 0x00, (byte) 0x00 }); + out.flush(); + + // wait for client to shutdown + serverThread.join(); + + // assert socket is closed + assertEquals(-1, in.read()); + + } + + /** + * The SOCKS5 client should successfully connect to the SOCKS5 server + * + * @throws Exception should not happen + */ + @Test + public void shouldSuccessfullyConnectToSocks5Server() throws Exception { + + // start thread to connect to SOCKS5 proxy + Thread serverThread = new Thread() { + + @Override + public void run() { + StreamHost streamHost = new StreamHost(proxyJID, serverAddress); + streamHost.setPort(serverPort); + + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + + try { + Socket socket = socks5Client.getSocket(10000); + assertNotNull(socket); + socket.getOutputStream().write(123); + socket.close(); + } + catch (Exception e) { + fail(e.getMessage()); + } + + } + + }; + serverThread.start(); + + Socket socket = serverSocket.accept(); + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + // validate authentication request + assertEquals((byte) 0x05, (byte) in.read()); // version + assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods + assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method + + // respond that no no-authentication method is used + out.write(new byte[] { (byte) 0x05, (byte) 0x00 }); + out.flush(); + + byte[] address = digest.getBytes(); + + assertEquals((byte) 0x05, (byte) in.read()); // version + assertEquals((byte) 0x01, (byte) in.read()); // connect request + assertEquals((byte) 0x00, (byte) in.read()); // reserved byte (always 0) + assertEquals((byte) 0x03, (byte) in.read()); // address type (domain) + assertEquals(address.length, (byte) in.read()); // address length + for (int i = 0; i < address.length; i++) { + assertEquals(address[i], (byte) in.read()); // address + } + assertEquals((byte) 0x00, (byte) in.read()); // port + assertEquals((byte) 0x00, (byte) in.read()); + + // reply with success SOCKS5 message + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03 }); + out.write(address.length); + out.write(address); + out.write(new byte[] { (byte) 0x00, (byte) 0x00 }); + out.flush(); + + // wait for client to shutdown + serverThread.join(); + + // verify data sent from client + assertEquals(123, in.read()); + + // assert socket is closed + assertEquals(-1, in.read()); + + } + + /** + * Close fake SOCKS5 proxy. + * + * @throws Exception should not happen + */ + @After + public void cleanup() throws Exception { + serverSocket.close(); + } +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5PacketUtils.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5PacketUtils.java index 85da7cb1b..390793e8c 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5PacketUtils.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5PacketUtils.java @@ -1,122 +1,122 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.packet.IQ; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; - -/** - * A collection of utility methods to create XMPP packets. - * - * @author Henning Staib - */ -public class Socks5PacketUtils { - - /** - * Returns a SOCKS5 Bytestream initialization request packet. The Request doesn't contain any - * SOCKS5 proxies. - * - * @param from the initiator - * @param to the target - * @param sessionID the session ID - * @return SOCKS5 Bytestream initialization request packet - */ - public static Bytestream createBytestreamInitiation(String from, String to, String sessionID) { - Bytestream bytestream = new Bytestream(); - bytestream.getPacketID(); - bytestream.setFrom(from); - bytestream.setTo(to); - bytestream.setSessionID(sessionID); - bytestream.setType(IQ.Type.SET); - return bytestream; - } - - /** - * Returns a response to a SOCKS5 Bytestream initialization request. The packet doesn't contain - * the uses-host information. - * - * @param from the target - * @param to the initiator - * @return response to a SOCKS5 Bytestream initialization request - */ - public static Bytestream createBytestreamResponse(String from, String to) { - Bytestream streamHostInfo = new Bytestream(); - streamHostInfo.getPacketID(); - streamHostInfo.setFrom(from); - streamHostInfo.setTo(to); - streamHostInfo.setType(IQ.Type.RESULT); - return streamHostInfo; - } - - /** - * Returns a response to an item discovery request. The packet doesn't contain any items. - * - * @param from the XMPP server - * @param to the XMPP client - * @return response to an item discovery request - */ - public static DiscoverItems createDiscoverItems(String from, String to) { - DiscoverItems discoverItems = new DiscoverItems(); - discoverItems.getPacketID(); - discoverItems.setFrom(from); - discoverItems.setTo(to); - discoverItems.setType(IQ.Type.RESULT); - return discoverItems; - } - - /** - * Returns a response to an info discovery request. The packet doesn't contain any infos. - * - * @param from the target - * @param to the initiator - * @return response to an info discovery request - */ - public static DiscoverInfo createDiscoverInfo(String from, String to) { - DiscoverInfo discoverInfo = new DiscoverInfo(); - discoverInfo.getPacketID(); - discoverInfo.setFrom(from); - discoverInfo.setTo(to); - discoverInfo.setType(IQ.Type.RESULT); - return discoverInfo; - } - - /** - * Returns a response IQ for a activation request to the proxy. - * - * @param from JID of the proxy - * @param to JID of the client who wants to activate the SOCKS5 Bytestream - * @return response IQ for a activation request to the proxy - */ - public static IQ createActivationConfirmation(String from, String to) { - IQ response = new IQ() { - - @Override - public String getChildElementXML() { - return null; - } - - }; - response.getPacketID(); - response.setFrom(from); - response.setTo(to); - response.setType(IQ.Type.RESULT); - return response; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; + +/** + * A collection of utility methods to create XMPP packets. + * + * @author Henning Staib + */ +public class Socks5PacketUtils { + + /** + * Returns a SOCKS5 Bytestream initialization request packet. The Request doesn't contain any + * SOCKS5 proxies. + * + * @param from the initiator + * @param to the target + * @param sessionID the session ID + * @return SOCKS5 Bytestream initialization request packet + */ + public static Bytestream createBytestreamInitiation(String from, String to, String sessionID) { + Bytestream bytestream = new Bytestream(); + bytestream.getPacketID(); + bytestream.setFrom(from); + bytestream.setTo(to); + bytestream.setSessionID(sessionID); + bytestream.setType(IQ.Type.SET); + return bytestream; + } + + /** + * Returns a response to a SOCKS5 Bytestream initialization request. The packet doesn't contain + * the uses-host information. + * + * @param from the target + * @param to the initiator + * @return response to a SOCKS5 Bytestream initialization request + */ + public static Bytestream createBytestreamResponse(String from, String to) { + Bytestream streamHostInfo = new Bytestream(); + streamHostInfo.getPacketID(); + streamHostInfo.setFrom(from); + streamHostInfo.setTo(to); + streamHostInfo.setType(IQ.Type.RESULT); + return streamHostInfo; + } + + /** + * Returns a response to an item discovery request. The packet doesn't contain any items. + * + * @param from the XMPP server + * @param to the XMPP client + * @return response to an item discovery request + */ + public static DiscoverItems createDiscoverItems(String from, String to) { + DiscoverItems discoverItems = new DiscoverItems(); + discoverItems.getPacketID(); + discoverItems.setFrom(from); + discoverItems.setTo(to); + discoverItems.setType(IQ.Type.RESULT); + return discoverItems; + } + + /** + * Returns a response to an info discovery request. The packet doesn't contain any infos. + * + * @param from the target + * @param to the initiator + * @return response to an info discovery request + */ + public static DiscoverInfo createDiscoverInfo(String from, String to) { + DiscoverInfo discoverInfo = new DiscoverInfo(); + discoverInfo.getPacketID(); + discoverInfo.setFrom(from); + discoverInfo.setTo(to); + discoverInfo.setType(IQ.Type.RESULT); + return discoverInfo; + } + + /** + * Returns a response IQ for a activation request to the proxy. + * + * @param from JID of the proxy + * @param to JID of the client who wants to activate the SOCKS5 Bytestream + * @return response IQ for a activation request to the proxy + */ + public static IQ createActivationConfirmation(String from, String to) { + IQ response = new IQ() { + + @Override + public String getChildElementXML() { + return null; + } + + }; + response.getPacketID(); + response.setFrom(from); + response.setTo(to); + response.setType(IQ.Type.RESULT); + return response; + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java index 31793d40b..62e83bc7b 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java @@ -1,374 +1,374 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.io.InputStream; -import java.io.OutputStream; -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.List; - -import org.jivesoftware.smack.SmackConfiguration; -import org.junit.After; -import org.junit.Test; - -/** - * Test for Socks5Proxy class. - * - * @author Henning Staib - */ -public class Socks5ProxyTest { - - /** - * The SOCKS5 proxy should be a singleton used by all XMPP connections - */ - @Test - public void shouldBeASingleton() { - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - - Socks5Proxy proxy1 = Socks5Proxy.getSocks5Proxy(); - Socks5Proxy proxy2 = Socks5Proxy.getSocks5Proxy(); - - assertNotNull(proxy1); - assertNotNull(proxy2); - assertSame(proxy1, proxy2); - } - - /** - * The SOCKS5 proxy should not be started if disabled by configuration. - */ - @Test - public void shouldNotBeRunningIfDisabled() { - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - assertFalse(proxy.isRunning()); - } - - /** - * The SOCKS5 proxy should use a free port above the one configured. - * - * @throws Exception should not happen - */ - @Test - public void shouldUseFreePortOnNegativeValues() throws Exception { - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - assertFalse(proxy.isRunning()); - - ServerSocket serverSocket = new ServerSocket(0); - SmackConfiguration.setLocalSocks5ProxyPort(-serverSocket.getLocalPort()); - - proxy.start(); - - assertTrue(proxy.isRunning()); - - serverSocket.close(); - - assertTrue(proxy.getPort() > serverSocket.getLocalPort()); - - } - - /** - * When inserting new network addresses to the proxy the order should remain in the order they - * were inserted. - */ - @Test - public void shouldPreserveAddressOrderOnInsertions() { - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - List addresses = new ArrayList(proxy.getLocalAddresses()); - addresses.add("1"); - addresses.add("2"); - addresses.add("3"); - for (String address : addresses) { - proxy.addLocalAddress(address); - } - - List localAddresses = proxy.getLocalAddresses(); - for (int i = 0; i < addresses.size(); i++) { - assertEquals(addresses.get(i), localAddresses.get(i)); - } - } - - /** - * When replacing network addresses of the proxy the order should remain in the order if the - * given list. - */ - @Test - public void shouldPreserveAddressOrderOnReplace() { - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - List addresses = new ArrayList(proxy.getLocalAddresses()); - addresses.add("1"); - addresses.add("2"); - addresses.add("3"); - - proxy.replaceLocalAddresses(addresses); - - List localAddresses = proxy.getLocalAddresses(); - for (int i = 0; i < addresses.size(); i++) { - assertEquals(addresses.get(i), localAddresses.get(i)); - } - } - - /** - * Inserting the same address multiple times should not cause the proxy to return this address - * multiple times. - */ - @Test - public void shouldNotReturnMultipleSameAddress() { - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - - proxy.addLocalAddress("same"); - proxy.addLocalAddress("same"); - proxy.addLocalAddress("same"); - - assertEquals(2, proxy.getLocalAddresses().size()); - } - - /** - * There should be only one thread executing the SOCKS5 proxy process. - */ - @Test - public void shouldOnlyStartOneServerThread() { - int threadCount = Thread.activeCount(); - - SmackConfiguration.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - proxy.start(); - - assertTrue(proxy.isRunning()); - assertEquals(threadCount + 1, Thread.activeCount()); - - proxy.start(); - - assertTrue(proxy.isRunning()); - assertEquals(threadCount + 1, Thread.activeCount()); - - proxy.stop(); - - assertFalse(proxy.isRunning()); - assertEquals(threadCount, Thread.activeCount()); - - proxy.start(); - - assertTrue(proxy.isRunning()); - assertEquals(threadCount + 1, Thread.activeCount()); - - proxy.stop(); - - } - - /** - * If the SOCKS5 proxy accepts a connection that is not a SOCKS5 connection it should close the - * corresponding socket. - * - * @throws Exception should not happen - */ - @Test - public void shouldCloseSocketIfNoSocks5Request() throws Exception { - SmackConfiguration.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - proxy.start(); - - Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); - - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { 1, 2, 3 }); - - int res; - try { - res = socket.getInputStream().read(); - } catch (SocketException e) { - res = -1; - } - - assertEquals(-1, res); - - proxy.stop(); - - } - - /** - * The SOCKS5 proxy should reply with an error message if no supported authentication methods - * are given in the SOCKS5 request. - * - * @throws Exception should not happen - */ - @Test - public void shouldRespondWithErrorIfNoSupportedAuthenticationMethod() throws Exception { - SmackConfiguration.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - proxy.start(); - - Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); - - OutputStream out = socket.getOutputStream(); - - // request username/password-authentication - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 }); - - InputStream in = socket.getInputStream(); - - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0xFF, (byte) in.read()); - - assertEquals(-1, in.read()); - - proxy.stop(); - - } - - /** - * The SOCKS5 proxy should respond with an error message if the client is not allowed to connect - * with the proxy. - * - * @throws Exception should not happen - */ - @Test - public void shouldRespondWithErrorIfConnectionIsNotAllowed() throws Exception { - SmackConfiguration.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - proxy.start(); - - Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); - - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); - - InputStream in = socket.getInputStream(); - - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - // send valid SOCKS5 message - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, - (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); - - // verify error message - assertEquals((byte) 0x05, (byte) in.read()); - assertFalse((byte) 0x00 == (byte) in.read()); // something other than 0 == success - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x03, (byte) in.read()); - assertEquals((byte) 0x01, (byte) in.read()); - assertEquals((byte) 0xAA, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - assertEquals(-1, in.read()); - - proxy.stop(); - - } - - /** - * A Client should successfully establish a connection to the SOCKS5 proxy. - * - * @throws Exception should not happen - */ - @Test - public void shouldSuccessfullyEstablishConnection() throws Exception { - SmackConfiguration.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - proxy.start(); - - assertTrue(proxy.isRunning()); - - String digest = new String(new byte[] { (byte) 0xAA }); - - // add digest to allow connection - proxy.addTransfer(digest); - - Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); - - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); - - InputStream in = socket.getInputStream(); - - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - // send valid SOCKS5 message - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, - (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); - - // verify response - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); // success - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x03, (byte) in.read()); - assertEquals((byte) 0x01, (byte) in.read()); - assertEquals((byte) 0xAA, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - Thread.sleep(200); - - Socket remoteSocket = proxy.getSocket(digest); - - // remove digest - proxy.removeTransfer(digest); - - // test stream - OutputStream remoteOut = remoteSocket.getOutputStream(); - byte[] data = new byte[] { 1, 2, 3, 4, 5 }; - remoteOut.write(data); - remoteOut.flush(); - - for (int i = 0; i < data.length; i++) { - assertEquals(data[i], in.read()); - } - - remoteSocket.close(); - - assertEquals(-1, in.read()); - - proxy.stop(); - - } - - /** - * Reset SOCKS5 proxy settings. - */ - @After - public void cleanup() { - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - SmackConfiguration.setLocalSocks5ProxyPort(7777); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); - try { - String address = InetAddress.getLocalHost().getHostAddress(); - List addresses = new ArrayList(); - addresses.add(address); - socks5Proxy.replaceLocalAddresses(addresses); - } - catch (UnknownHostException e) { - // ignore - } - - socks5Proxy.stop(); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.io.OutputStream; +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.List; + +import org.jivesoftware.smack.SmackConfiguration; +import org.junit.After; +import org.junit.Test; + +/** + * Test for Socks5Proxy class. + * + * @author Henning Staib + */ +public class Socks5ProxyTest { + + /** + * The SOCKS5 proxy should be a singleton used by all XMPP connections + */ + @Test + public void shouldBeASingleton() { + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + + Socks5Proxy proxy1 = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy2 = Socks5Proxy.getSocks5Proxy(); + + assertNotNull(proxy1); + assertNotNull(proxy2); + assertSame(proxy1, proxy2); + } + + /** + * The SOCKS5 proxy should not be started if disabled by configuration. + */ + @Test + public void shouldNotBeRunningIfDisabled() { + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + assertFalse(proxy.isRunning()); + } + + /** + * The SOCKS5 proxy should use a free port above the one configured. + * + * @throws Exception should not happen + */ + @Test + public void shouldUseFreePortOnNegativeValues() throws Exception { + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + assertFalse(proxy.isRunning()); + + ServerSocket serverSocket = new ServerSocket(0); + SmackConfiguration.setLocalSocks5ProxyPort(-serverSocket.getLocalPort()); + + proxy.start(); + + assertTrue(proxy.isRunning()); + + serverSocket.close(); + + assertTrue(proxy.getPort() > serverSocket.getLocalPort()); + + } + + /** + * When inserting new network addresses to the proxy the order should remain in the order they + * were inserted. + */ + @Test + public void shouldPreserveAddressOrderOnInsertions() { + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + List addresses = new ArrayList(proxy.getLocalAddresses()); + addresses.add("1"); + addresses.add("2"); + addresses.add("3"); + for (String address : addresses) { + proxy.addLocalAddress(address); + } + + List localAddresses = proxy.getLocalAddresses(); + for (int i = 0; i < addresses.size(); i++) { + assertEquals(addresses.get(i), localAddresses.get(i)); + } + } + + /** + * When replacing network addresses of the proxy the order should remain in the order if the + * given list. + */ + @Test + public void shouldPreserveAddressOrderOnReplace() { + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + List addresses = new ArrayList(proxy.getLocalAddresses()); + addresses.add("1"); + addresses.add("2"); + addresses.add("3"); + + proxy.replaceLocalAddresses(addresses); + + List localAddresses = proxy.getLocalAddresses(); + for (int i = 0; i < addresses.size(); i++) { + assertEquals(addresses.get(i), localAddresses.get(i)); + } + } + + /** + * Inserting the same address multiple times should not cause the proxy to return this address + * multiple times. + */ + @Test + public void shouldNotReturnMultipleSameAddress() { + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + + proxy.addLocalAddress("same"); + proxy.addLocalAddress("same"); + proxy.addLocalAddress("same"); + + assertEquals(2, proxy.getLocalAddresses().size()); + } + + /** + * There should be only one thread executing the SOCKS5 proxy process. + */ + @Test + public void shouldOnlyStartOneServerThread() { + int threadCount = Thread.activeCount(); + + SmackConfiguration.setLocalSocks5ProxyPort(7890); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + proxy.start(); + + assertTrue(proxy.isRunning()); + assertEquals(threadCount + 1, Thread.activeCount()); + + proxy.start(); + + assertTrue(proxy.isRunning()); + assertEquals(threadCount + 1, Thread.activeCount()); + + proxy.stop(); + + assertFalse(proxy.isRunning()); + assertEquals(threadCount, Thread.activeCount()); + + proxy.start(); + + assertTrue(proxy.isRunning()); + assertEquals(threadCount + 1, Thread.activeCount()); + + proxy.stop(); + + } + + /** + * If the SOCKS5 proxy accepts a connection that is not a SOCKS5 connection it should close the + * corresponding socket. + * + * @throws Exception should not happen + */ + @Test + public void shouldCloseSocketIfNoSocks5Request() throws Exception { + SmackConfiguration.setLocalSocks5ProxyPort(7890); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + proxy.start(); + + Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); + + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { 1, 2, 3 }); + + int res; + try { + res = socket.getInputStream().read(); + } catch (SocketException e) { + res = -1; + } + + assertEquals(-1, res); + + proxy.stop(); + + } + + /** + * The SOCKS5 proxy should reply with an error message if no supported authentication methods + * are given in the SOCKS5 request. + * + * @throws Exception should not happen + */ + @Test + public void shouldRespondWithErrorIfNoSupportedAuthenticationMethod() throws Exception { + SmackConfiguration.setLocalSocks5ProxyPort(7890); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + proxy.start(); + + Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); + + OutputStream out = socket.getOutputStream(); + + // request username/password-authentication + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 }); + + InputStream in = socket.getInputStream(); + + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0xFF, (byte) in.read()); + + assertEquals(-1, in.read()); + + proxy.stop(); + + } + + /** + * The SOCKS5 proxy should respond with an error message if the client is not allowed to connect + * with the proxy. + * + * @throws Exception should not happen + */ + @Test + public void shouldRespondWithErrorIfConnectionIsNotAllowed() throws Exception { + SmackConfiguration.setLocalSocks5ProxyPort(7890); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + proxy.start(); + + Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); + + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); + + InputStream in = socket.getInputStream(); + + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + // send valid SOCKS5 message + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, + (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); + + // verify error message + assertEquals((byte) 0x05, (byte) in.read()); + assertFalse((byte) 0x00 == (byte) in.read()); // something other than 0 == success + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x03, (byte) in.read()); + assertEquals((byte) 0x01, (byte) in.read()); + assertEquals((byte) 0xAA, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + assertEquals(-1, in.read()); + + proxy.stop(); + + } + + /** + * A Client should successfully establish a connection to the SOCKS5 proxy. + * + * @throws Exception should not happen + */ + @Test + public void shouldSuccessfullyEstablishConnection() throws Exception { + SmackConfiguration.setLocalSocks5ProxyPort(7890); + Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + proxy.start(); + + assertTrue(proxy.isRunning()); + + String digest = new String(new byte[] { (byte) 0xAA }); + + // add digest to allow connection + proxy.addTransfer(digest); + + Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort()); + + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); + + InputStream in = socket.getInputStream(); + + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + // send valid SOCKS5 message + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, + (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); + + // verify response + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); // success + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x03, (byte) in.read()); + assertEquals((byte) 0x01, (byte) in.read()); + assertEquals((byte) 0xAA, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + Thread.sleep(200); + + Socket remoteSocket = proxy.getSocket(digest); + + // remove digest + proxy.removeTransfer(digest); + + // test stream + OutputStream remoteOut = remoteSocket.getOutputStream(); + byte[] data = new byte[] { 1, 2, 3, 4, 5 }; + remoteOut.write(data); + remoteOut.flush(); + + for (int i = 0; i < data.length; i++) { + assertEquals(data[i], in.read()); + } + + remoteSocket.close(); + + assertEquals(-1, in.read()); + + proxy.stop(); + + } + + /** + * Reset SOCKS5 proxy settings. + */ + @After + public void cleanup() { + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + SmackConfiguration.setLocalSocks5ProxyPort(7777); + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + try { + String address = InetAddress.getLocalHost().getHostAddress(); + List addresses = new ArrayList(); + addresses.add(address); + socks5Proxy.replaceLocalAddresses(addresses); + } + catch (UnknownHostException e) { + // ignore + } + + socks5Proxy.stop(); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java index 8ed2b45dc..62d59d81d 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java @@ -1,289 +1,289 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; - -/** - * Simple SOCKS5 proxy for testing purposes. It is almost the same as the Socks5Proxy class but the - * port can be configured more easy and it all connections are allowed. - * - * @author Henning Staib - */ -public class Socks5TestProxy { - - /* SOCKS5 proxy singleton */ - private static Socks5TestProxy 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 connectionMap = new ConcurrentHashMap(); - - /* port of the test proxy */ - private int port = 7777; - - /** - * Private constructor. - */ - private Socks5TestProxy(int port) { - this.serverProcess = new Socks5ServerProcess(); - this.port = port; - } - - /** - * Returns the local SOCKS5 proxy server - * - * @param port of the test proxy - * @return the local SOCKS5 proxy server - */ - public static synchronized Socks5TestProxy getProxy(int port) { - if (socks5Server == null) { - socks5Server = new Socks5TestProxy(port); - socks5Server.start(); - } - return socks5Server; - } - - /** - * Stops the test proxy - */ - public static synchronized void stopProxy() { - if (socks5Server != null) { - socks5Server.stop(); - socks5Server = null; - } - } - - /** - * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing. - */ - public synchronized void start() { - if (isRunning()) { - return; - } - try { - this.serverSocket = new ServerSocket(this.port); - this.serverThread = new Thread(this.serverProcess); - this.serverThread.start(); - } - catch (IOException e) { - e.printStackTrace(); - // do nothing - } - } - - /** - * 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 - e.printStackTrace(); - } - - if (this.serverThread != null && this.serverThread.isAlive()) { - try { - this.serverThread.interrupt(); - this.serverThread.join(); - } - catch (InterruptedException e) { - // do nothing - e.printStackTrace(); - } - } - this.serverThread = null; - this.serverSocket = null; - - } - - /** - * Returns the host address of the local SOCKS5 proxy server. - * - * @return the host address of the local SOCKS5 proxy server - */ - public String getAddress() { - try { - return InetAddress.getLocalHost().getHostAddress(); - } - catch (UnknownHostException e) { - return null; - } - } - - /** - * 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. - * - * @param digest identifying the connection - * @return socket or null if there is no socket for the given digest - */ - public Socket getSocket(String digest) { - return this.connectionMap.get(digest); - } - - /** - * Returns true if the local SOCKS5 proxy server is running, otherwise false. - * - * @return true if the local SOCKS5 proxy server is running, otherwise false - */ - public boolean isRunning() { - return this.serverSocket != null; - } - - /** - * Implementation of a simplified SOCKS5 proxy server. - * - * @author Henning Staib - */ - class Socks5ServerProcess implements Runnable { - - public void run() { - while (true) { - Socket socket = null; - - try { - - if (Socks5TestProxy.this.serverSocket.isClosed() - || Thread.currentThread().isInterrupted()) { - return; - } - - // accept connection - socket = Socks5TestProxy.this.serverSocket.accept(); - - // initialize connection - establishConnection(socket); - - } - catch (SocketException e) { - /* do nothing */ - } - catch (Exception e) { - try { - e.printStackTrace(); - 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]); - - connectionRequest[1] = (byte) 0x00; // set return status to 0 (success) - out.write(connectionRequest); - out.flush(); - - // store connection - Socks5TestProxy.this.connectionMap.put(responseDigest, socket); - } - - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; + +/** + * Simple SOCKS5 proxy for testing purposes. It is almost the same as the Socks5Proxy class but the + * port can be configured more easy and it all connections are allowed. + * + * @author Henning Staib + */ +public class Socks5TestProxy { + + /* SOCKS5 proxy singleton */ + private static Socks5TestProxy 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 connectionMap = new ConcurrentHashMap(); + + /* port of the test proxy */ + private int port = 7777; + + /** + * Private constructor. + */ + private Socks5TestProxy(int port) { + this.serverProcess = new Socks5ServerProcess(); + this.port = port; + } + + /** + * Returns the local SOCKS5 proxy server + * + * @param port of the test proxy + * @return the local SOCKS5 proxy server + */ + public static synchronized Socks5TestProxy getProxy(int port) { + if (socks5Server == null) { + socks5Server = new Socks5TestProxy(port); + socks5Server.start(); + } + return socks5Server; + } + + /** + * Stops the test proxy + */ + public static synchronized void stopProxy() { + if (socks5Server != null) { + socks5Server.stop(); + socks5Server = null; + } + } + + /** + * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing. + */ + public synchronized void start() { + if (isRunning()) { + return; + } + try { + this.serverSocket = new ServerSocket(this.port); + this.serverThread = new Thread(this.serverProcess); + this.serverThread.start(); + } + catch (IOException e) { + e.printStackTrace(); + // do nothing + } + } + + /** + * 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 + e.printStackTrace(); + } + + if (this.serverThread != null && this.serverThread.isAlive()) { + try { + this.serverThread.interrupt(); + this.serverThread.join(); + } + catch (InterruptedException e) { + // do nothing + e.printStackTrace(); + } + } + this.serverThread = null; + this.serverSocket = null; + + } + + /** + * Returns the host address of the local SOCKS5 proxy server. + * + * @return the host address of the local SOCKS5 proxy server + */ + public String getAddress() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) { + return null; + } + } + + /** + * 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. + * + * @param digest identifying the connection + * @return socket or null if there is no socket for the given digest + */ + public Socket getSocket(String digest) { + return this.connectionMap.get(digest); + } + + /** + * Returns true if the local SOCKS5 proxy server is running, otherwise false. + * + * @return true if the local SOCKS5 proxy server is running, otherwise false + */ + public boolean isRunning() { + return this.serverSocket != null; + } + + /** + * Implementation of a simplified SOCKS5 proxy server. + * + * @author Henning Staib + */ + class Socks5ServerProcess implements Runnable { + + public void run() { + while (true) { + Socket socket = null; + + try { + + if (Socks5TestProxy.this.serverSocket.isClosed() + || Thread.currentThread().isInterrupted()) { + return; + } + + // accept connection + socket = Socks5TestProxy.this.serverSocket.accept(); + + // initialize connection + establishConnection(socket); + + } + catch (SocketException e) { + /* do nothing */ + } + catch (Exception e) { + try { + e.printStackTrace(); + 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]); + + connectionRequest[1] = (byte) 0x00; // set return status to 0 (success) + out.write(connectionRequest); + out.flush(); + + // store connection + Socks5TestProxy.this.connectionMap.put(responseDigest, socket); + } + + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/muc/ConnectionDetachedPacketCollectorTest.java b/extensions/src/test/java/org/jivesoftware/smackx/muc/ConnectionDetachedPacketCollectorTest.java index 79a9f721e..a65bd1921 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/muc/ConnectionDetachedPacketCollectorTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/muc/ConnectionDetachedPacketCollectorTest.java @@ -1,195 +1,195 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.muc; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import org.jivesoftware.smack.packet.Packet; -import org.junit.Test; - -public class ConnectionDetachedPacketCollectorTest -{ - - @Test - public void verifyRollover() - { - ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(5); - - for (int i=0; i<6; i++) - { - Packet testPacket = new TestPacket(i); - collector.processPacket(testPacket); - } - - // Assert that '0' has rolled off - assertEquals("1", collector.nextResult().getPacketID()); - assertEquals("2", collector.nextResult().getPacketID()); - assertEquals("3", collector.nextResult().getPacketID()); - assertEquals("4", collector.nextResult().getPacketID()); - assertEquals("5", collector.pollResult().getPacketID()); - assertNull(collector.pollResult()); - - for (int i=10; i<15; i++) - { - Packet testPacket = new TestPacket(i); - collector.processPacket(testPacket); - } - - assertEquals("10", collector.nextResult().getPacketID()); - assertEquals("11", collector.nextResult().getPacketID()); - assertEquals("12", collector.nextResult().getPacketID()); - assertEquals("13", collector.nextResult().getPacketID()); - assertEquals("14", collector.pollResult().getPacketID()); - assertNull(collector.pollResult()); - - assertNull(collector.nextResult(1000)); - } - - /** - * Although this doesn't guarentee anything due to the nature of threading, it can - * potentially catch problems. - */ - @Test - public void verifyThreadSafety() - { - int insertCount = 500; - final ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(insertCount); - - Thread consumer1 = new Thread(new Runnable() - { - @Override - public void run() - { - try - { - while (true) - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - Packet packet = collector.nextResult(); -// System.out.println(Thread.currentThread().getName() + " packet: " + packet); - } - } - catch (RuntimeException re) - { - if (re.getCause() instanceof InterruptedException) - { -// System.out.println(Thread.currentThread().getName() + " has been interupted"); - } - } - } - }); - consumer1.setName("consumer 1"); - - Thread consumer2 = new Thread(new Runnable() - { - @Override - public void run() - { - Packet p = null; - - do - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - p = collector.nextResult(1); -// System.out.println(Thread.currentThread().getName() + " packet: " + p); - } - while (p != null); - } - }); - consumer2.setName("consumer 2"); - - Thread consumer3 = new Thread(new Runnable() - { - @Override - public void run() - { - Packet p = null; - - do - { - try - { - Thread.sleep(3); - } - catch (InterruptedException e) - { - } - p = collector.pollResult(); -// System.out.println(Thread.currentThread().getName() + " packet: " + p); - } - while (p != null); - } - }); - consumer3.setName("consumer 3"); - - consumer1.start(); - consumer2.start(); - consumer3.start(); - - for(int i=0; i" + getPacketID() + ""; - } - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.muc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.jivesoftware.smack.packet.Packet; +import org.junit.Test; + +public class ConnectionDetachedPacketCollectorTest +{ + + @Test + public void verifyRollover() + { + ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(5); + + for (int i=0; i<6; i++) + { + Packet testPacket = new TestPacket(i); + collector.processPacket(testPacket); + } + + // Assert that '0' has rolled off + assertEquals("1", collector.nextResult().getPacketID()); + assertEquals("2", collector.nextResult().getPacketID()); + assertEquals("3", collector.nextResult().getPacketID()); + assertEquals("4", collector.nextResult().getPacketID()); + assertEquals("5", collector.pollResult().getPacketID()); + assertNull(collector.pollResult()); + + for (int i=10; i<15; i++) + { + Packet testPacket = new TestPacket(i); + collector.processPacket(testPacket); + } + + assertEquals("10", collector.nextResult().getPacketID()); + assertEquals("11", collector.nextResult().getPacketID()); + assertEquals("12", collector.nextResult().getPacketID()); + assertEquals("13", collector.nextResult().getPacketID()); + assertEquals("14", collector.pollResult().getPacketID()); + assertNull(collector.pollResult()); + + assertNull(collector.nextResult(1000)); + } + + /** + * Although this doesn't guarentee anything due to the nature of threading, it can + * potentially catch problems. + */ + @Test + public void verifyThreadSafety() + { + int insertCount = 500; + final ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(insertCount); + + Thread consumer1 = new Thread(new Runnable() + { + @Override + public void run() + { + try + { + while (true) + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + Packet packet = collector.nextResult(); +// System.out.println(Thread.currentThread().getName() + " packet: " + packet); + } + } + catch (RuntimeException re) + { + if (re.getCause() instanceof InterruptedException) + { +// System.out.println(Thread.currentThread().getName() + " has been interupted"); + } + } + } + }); + consumer1.setName("consumer 1"); + + Thread consumer2 = new Thread(new Runnable() + { + @Override + public void run() + { + Packet p = null; + + do + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + p = collector.nextResult(1); +// System.out.println(Thread.currentThread().getName() + " packet: " + p); + } + while (p != null); + } + }); + consumer2.setName("consumer 2"); + + Thread consumer3 = new Thread(new Runnable() + { + @Override + public void run() + { + Packet p = null; + + do + { + try + { + Thread.sleep(3); + } + catch (InterruptedException e) + { + } + p = collector.pollResult(); +// System.out.println(Thread.currentThread().getName() + " packet: " + p); + } + while (p != null); + } + }); + consumer3.setName("consumer 3"); + + consumer1.start(); + consumer2.start(); + consumer3.start(); + + for(int i=0; i" + getPacketID() + ""; + } + } +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/ping/KeepaliveTest.java b/extensions/src/test/java/org/jivesoftware/smackx/ping/KeepaliveTest.java index 487f8a09f..00cd61eef 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/ping/KeepaliveTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/ping/KeepaliveTest.java @@ -1,173 +1,173 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// TODO this should become PingManagerTest - -//package org.jivesoftware.smackx.ping; -// -//import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; -//import static org.junit.Assert.assertTrue; -// -//import java.util.Properties; -//import java.util.concurrent.CountDownLatch; -//import java.util.concurrent.TimeUnit; -// -//import org.jivesoftware.smack.Connection; -//import org.jivesoftware.smack.DummyConnection; -//import org.jivesoftware.smack.PacketInterceptor; -//import org.jivesoftware.smack.PacketListener; -//import org.jivesoftware.smack.SmackConfiguration; -//import org.jivesoftware.smack.ThreadedDummyConnection; -//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.pingx.packet.Ping; -//import org.jivesoftware.smackx.ping.PingFailedListener; -//import org.junit.After; -//import org.junit.Before; -//import org.junit.Test; -// -//public class KeepaliveTest { -// private static final long PING_MINIMUM = 1000; -// private static String TO = "juliet@capulet.lit/balcony"; -// private static String ID = "s2c1"; -// -// private static Properties outputProperties = new Properties(); -// { -// outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); -// } -// -// private int originalTimeout; -// -// @Before -// public void resetProperties() -// { -// SmackConfiguration.setKeepAliveInterval(-1); -// originalTimeout = SmackConfiguration.getPacketReplyTimeout(); -// SmackConfiguration.setPacketReplyTimeout(1000); -// } -// -// @After -// public void restoreProperties() -// { -// SmackConfiguration.setPacketReplyTimeout(originalTimeout); -// } -// /* -// * Stanza copied from spec -// */ -// @Test -// public void validatePingStanzaXML() throws Exception { -// // @formatter:off -// String control = "" -// + ""; -// // @formatter:on -// -// Ping ping = new Ping(TO); -// ping.setPacketID(ID); -// -// assertXMLEqual(control, ping.toXML()); -// } -// -// @Test -// public void serverPingFailSingleConnection() throws Exception { -// DummyConnection connection = getConnection(); -// CountDownLatch latch = new CountDownLatch(2); -// addInterceptor(connection, latch); -// addPingFailedListener(connection, latch); -// -// // Time based testing kind of sucks, but this should be reliable on a DummyConnection since there -// // is no actual server involved. This will provide enough time to ping and wait for the lack of response. -// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); -// } -// -// @Test -// public void serverPingSuccessfulSingleConnection() throws Exception { -// ThreadedDummyConnection connection = getThreadedConnection(); -// final CountDownLatch latch = new CountDownLatch(1); -// -// connection.addPacketListener(new PacketListener() { -// @Override -// public void processPacket(Packet packet) { -// latch.countDown(); -// } -// }, new IQTypeFilter(IQ.Type.RESULT)); -// -// // Time based testing kind of sucks, but this should be reliable on a DummyConnection since there -// // is no actual server involved. This will provide enough time to ping and wait for the lack of response. -// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); -// } -// -// @Test -// public void serverPingFailMultipleConnection() throws Exception { -// CountDownLatch latch = new CountDownLatch(6); -// SmackConfiguration.setPacketReplyTimeout(15000); -// -// DummyConnection con1 = getConnection(); -// addInterceptor(con1, latch); -// addPingFailedListener(con1, latch); -// -// DummyConnection con2 = getConnection(); -// addInterceptor(con2, latch); -// addPingFailedListener(con2, latch); -// -// DummyConnection con3 = getConnection(); -// addInterceptor(con3, latch); -// addPingFailedListener(con2, latch); -// -// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); -// } -// -// private void addPingFailedListener(DummyConnection con, final CountDownLatch latch) { -// KeepAliveManager manager = KeepAliveManager.getInstanceFor(con); -// manager.addPingFailedListener(new PingFailedListener() { -// @Override -// public void pingFailed() { -// latch.countDown(); -// } -// }); -// } -// -// private DummyConnection getConnection() { -// DummyConnection con = new DummyConnection(); -// KeepAliveManager mgr = KeepAliveManager.getInstanceFor(con); -// mgr.setPingInterval(PING_MINIMUM); -// -// return con; -// } -// -// private ThreadedDummyConnection getThreadedConnection() { -// ThreadedDummyConnection con = new ThreadedDummyConnection(); -// KeepAliveManager mgr = KeepAliveManager.getInstanceFor(con); -// mgr.setPingInterval(PING_MINIMUM); -// -// return con; -// } -// -// private void addInterceptor(final Connection con, final CountDownLatch latch) { -// con.addPacketInterceptor(new PacketInterceptor() { -// @Override -// public void interceptPacket(Packet packet) { -// con.removePacketInterceptor(this); -// latch.countDown(); -// } -// }, new PacketTypeFilter(Ping.class)); -// } -// -// private long getWaitTime() { -// return PING_MINIMUM + SmackConfiguration.getPacketReplyTimeout() + 3000; -// } -//} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// TODO this should become PingManagerTest + +//package org.jivesoftware.smackx.ping; +// +//import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +//import static org.junit.Assert.assertTrue; +// +//import java.util.Properties; +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.TimeUnit; +// +//import org.jivesoftware.smack.Connection; +//import org.jivesoftware.smack.DummyConnection; +//import org.jivesoftware.smack.PacketInterceptor; +//import org.jivesoftware.smack.PacketListener; +//import org.jivesoftware.smack.SmackConfiguration; +//import org.jivesoftware.smack.ThreadedDummyConnection; +//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.pingx.packet.Ping; +//import org.jivesoftware.smackx.ping.PingFailedListener; +//import org.junit.After; +//import org.junit.Before; +//import org.junit.Test; +// +//public class KeepaliveTest { +// private static final long PING_MINIMUM = 1000; +// private static String TO = "juliet@capulet.lit/balcony"; +// private static String ID = "s2c1"; +// +// private static Properties outputProperties = new Properties(); +// { +// outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); +// } +// +// private int originalTimeout; +// +// @Before +// public void resetProperties() +// { +// SmackConfiguration.setKeepAliveInterval(-1); +// originalTimeout = SmackConfiguration.getPacketReplyTimeout(); +// SmackConfiguration.setPacketReplyTimeout(1000); +// } +// +// @After +// public void restoreProperties() +// { +// SmackConfiguration.setPacketReplyTimeout(originalTimeout); +// } +// /* +// * Stanza copied from spec +// */ +// @Test +// public void validatePingStanzaXML() throws Exception { +// // @formatter:off +// String control = "" +// + ""; +// // @formatter:on +// +// Ping ping = new Ping(TO); +// ping.setPacketID(ID); +// +// assertXMLEqual(control, ping.toXML()); +// } +// +// @Test +// public void serverPingFailSingleConnection() throws Exception { +// DummyConnection connection = getConnection(); +// CountDownLatch latch = new CountDownLatch(2); +// addInterceptor(connection, latch); +// addPingFailedListener(connection, latch); +// +// // Time based testing kind of sucks, but this should be reliable on a DummyConnection since there +// // is no actual server involved. This will provide enough time to ping and wait for the lack of response. +// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); +// } +// +// @Test +// public void serverPingSuccessfulSingleConnection() throws Exception { +// ThreadedDummyConnection connection = getThreadedConnection(); +// final CountDownLatch latch = new CountDownLatch(1); +// +// connection.addPacketListener(new PacketListener() { +// @Override +// public void processPacket(Packet packet) { +// latch.countDown(); +// } +// }, new IQTypeFilter(IQ.Type.RESULT)); +// +// // Time based testing kind of sucks, but this should be reliable on a DummyConnection since there +// // is no actual server involved. This will provide enough time to ping and wait for the lack of response. +// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); +// } +// +// @Test +// public void serverPingFailMultipleConnection() throws Exception { +// CountDownLatch latch = new CountDownLatch(6); +// SmackConfiguration.setPacketReplyTimeout(15000); +// +// DummyConnection con1 = getConnection(); +// addInterceptor(con1, latch); +// addPingFailedListener(con1, latch); +// +// DummyConnection con2 = getConnection(); +// addInterceptor(con2, latch); +// addPingFailedListener(con2, latch); +// +// DummyConnection con3 = getConnection(); +// addInterceptor(con3, latch); +// addPingFailedListener(con2, latch); +// +// assertTrue(latch.await(getWaitTime(), TimeUnit.MILLISECONDS)); +// } +// +// private void addPingFailedListener(DummyConnection con, final CountDownLatch latch) { +// KeepAliveManager manager = KeepAliveManager.getInstanceFor(con); +// manager.addPingFailedListener(new PingFailedListener() { +// @Override +// public void pingFailed() { +// latch.countDown(); +// } +// }); +// } +// +// private DummyConnection getConnection() { +// DummyConnection con = new DummyConnection(); +// KeepAliveManager mgr = KeepAliveManager.getInstanceFor(con); +// mgr.setPingInterval(PING_MINIMUM); +// +// return con; +// } +// +// private ThreadedDummyConnection getThreadedConnection() { +// ThreadedDummyConnection con = new ThreadedDummyConnection(); +// KeepAliveManager mgr = KeepAliveManager.getInstanceFor(con); +// mgr.setPingInterval(PING_MINIMUM); +// +// return con; +// } +// +// private void addInterceptor(final Connection con, final CountDownLatch latch) { +// con.addPacketInterceptor(new PacketInterceptor() { +// @Override +// public void interceptPacket(Packet packet) { +// con.removePacketInterceptor(this); +// latch.countDown(); +// } +// }, new PacketTypeFilter(Ping.class)); +// } +// +// private long getWaitTime() { +// return PING_MINIMUM + SmackConfiguration.getPacketReplyTimeout() + 3000; +// } +//} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/provider/ProviderConfigTest.java b/extensions/src/test/java/org/jivesoftware/smackx/provider/ProviderConfigTest.java index 796b30cc0..f9325fa9b 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/provider/ProviderConfigTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/provider/ProviderConfigTest.java @@ -1,74 +1,74 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collection; - -import junit.framework.Assert; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.provider.ExtensionProviderInfo; -import org.jivesoftware.smack.provider.IQProvider; -import org.jivesoftware.smack.provider.IQProviderInfo; -import org.jivesoftware.smack.provider.ProviderFileLoader; -import org.jivesoftware.smack.provider.ProviderLoader; -import org.jivesoftware.smack.provider.ProviderManager; -import org.jivesoftware.smack.util.FileUtils; -import org.junit.Ignore; -import org.junit.Test; -import org.xmlpull.v1.XmlPullParser; - -@SuppressWarnings("deprecation") -public class ProviderConfigTest { - - @Test - public void addGenericLoaderProvider() { - ProviderManager.getInstance().addLoader(new ProviderLoader() { - - @Override - public Collection getIQProviderInfo() { - ArrayList l = new ArrayList(1); - l.add(new IQProviderInfo("provider", "test:provider", new TestIQProvider())); - return l; - } - - @Override - public Collection getExtensionProviderInfo() { - return null; - } - }); - - Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:provider")); - } - - @Ignore // gradle migration - @Test - public void addClasspathFileLoaderProvider() throws Exception{ - ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:org/jivesoftware/smackx/provider/test.providers", null))); - Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:file_provider")); - } - - public static class TestIQProvider implements IQProvider { - - @Override - public IQ parseIQ(XmlPullParser parser) throws Exception { - return null; - } - - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collection; + +import junit.framework.Assert; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.ExtensionProviderInfo; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.IQProviderInfo; +import org.jivesoftware.smack.provider.ProviderFileLoader; +import org.jivesoftware.smack.provider.ProviderLoader; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.util.FileUtils; +import org.junit.Ignore; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +@SuppressWarnings("deprecation") +public class ProviderConfigTest { + + @Test + public void addGenericLoaderProvider() { + ProviderManager.getInstance().addLoader(new ProviderLoader() { + + @Override + public Collection getIQProviderInfo() { + ArrayList l = new ArrayList(1); + l.add(new IQProviderInfo("provider", "test:provider", new TestIQProvider())); + return l; + } + + @Override + public Collection getExtensionProviderInfo() { + return null; + } + }); + + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:provider")); + } + + @Ignore // gradle migration + @Test + public void addClasspathFileLoaderProvider() throws Exception{ + ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:org/jivesoftware/smackx/provider/test.providers", null))); + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:file_provider")); + } + + public static class TestIQProvider implements IQProvider { + + @Override + public IQ parseIQ(XmlPullParser parser) throws Exception { + return null; + } + + } +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/provider/test.providers b/extensions/src/test/java/org/jivesoftware/smackx/provider/test.providers index 90bafc4f7..4c6ef142b 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/provider/test.providers +++ b/extensions/src/test/java/org/jivesoftware/smackx/provider/test.providers @@ -1,11 +1,11 @@ - - - - - - provider - test:file_provider - org.jivesoftware.smackx.provider.ProviderConfigTest$TestIQProvider - - - + + + + + + provider + test:file_provider + org.jivesoftware.smackx.provider.ProviderConfigTest$TestIQProvider + + + diff --git a/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java b/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java index ca83fae96..02f285c38 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java @@ -14,78 +14,78 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import static org.junit.Assert.assertEquals; +package org.jivesoftware.smackx.pubsub; -import org.jivesoftware.smack.SmackConfiguration; +import static org.junit.Assert.assertEquals; + +import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.ThreadedDummyConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.packet.XMPPError.Condition; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.packet.XMPPError.Condition; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; -import org.jivesoftware.smackx.pubsub.packet.PubSub; -import org.junit.Assert; -import org.junit.Test; +import org.jivesoftware.smackx.pubsub.packet.PubSub; +import org.junit.Assert; +import org.junit.Test; /** * * @author Robin Collier * - */ -public class ConfigureFormTest -{ - @Test - public void checkChildrenAssocPolicy() - { - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setChildrenAssociationPolicy(ChildrenAssociationPolicy.owners); - assertEquals(ChildrenAssociationPolicy.owners, form.getChildrenAssociationPolicy()); - } - - @Test - public void getConfigFormWithInsufficientPriviliges() throws XMPPException - { - ThreadedDummyConnection con = new ThreadedDummyConnection(); - PubSubManager mgr = new PubSubManager(con); - DiscoverInfo info = new DiscoverInfo(); - Identity ident = new Identity("pubsub", null, "leaf"); - info.addIdentity(ident); - con.addIQReply(info); - - Node node = mgr.getNode("princely_musings"); - - PubSub errorIq = new PubSub(); - XMPPError error = new XMPPError(Condition.forbidden); - errorIq.setError(error); - con.addIQReply(errorIq); - - try - { - node.getNodeConfiguration(); - } - catch (XMPPException e) - { - Assert.assertEquals(XMPPError.Type.AUTH, e.getXMPPError().getType()); - } - } - - @Test (expected=XMPPException.class) - public void getConfigFormWithTimeout() throws XMPPException - { - ThreadedDummyConnection con = new ThreadedDummyConnection(); - PubSubManager mgr = new PubSubManager(con); - DiscoverInfo info = new DiscoverInfo(); - Identity ident = new Identity("pubsub", null, "leaf"); - info.addIdentity(ident); - con.addIQReply(info); - - Node node = mgr.getNode("princely_musings"); - + */ +public class ConfigureFormTest +{ + @Test + public void checkChildrenAssocPolicy() + { + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setChildrenAssociationPolicy(ChildrenAssociationPolicy.owners); + assertEquals(ChildrenAssociationPolicy.owners, form.getChildrenAssociationPolicy()); + } + + @Test + public void getConfigFormWithInsufficientPriviliges() throws XMPPException + { + ThreadedDummyConnection con = new ThreadedDummyConnection(); + PubSubManager mgr = new PubSubManager(con); + DiscoverInfo info = new DiscoverInfo(); + Identity ident = new Identity("pubsub", null, "leaf"); + info.addIdentity(ident); + con.addIQReply(info); + + Node node = mgr.getNode("princely_musings"); + + PubSub errorIq = new PubSub(); + XMPPError error = new XMPPError(Condition.forbidden); + errorIq.setError(error); + con.addIQReply(errorIq); + + try + { + node.getNodeConfiguration(); + } + catch (XMPPException e) + { + Assert.assertEquals(XMPPError.Type.AUTH, e.getXMPPError().getType()); + } + } + + @Test (expected=XMPPException.class) + public void getConfigFormWithTimeout() throws XMPPException + { + ThreadedDummyConnection con = new ThreadedDummyConnection(); + PubSubManager mgr = new PubSubManager(con); + DiscoverInfo info = new DiscoverInfo(); + Identity ident = new Identity("pubsub", null, "leaf"); + info.addIdentity(ident); + con.addIQReply(info); + + Node node = mgr.getNode("princely_musings"); + SmackConfiguration.setPacketReplyTimeout(100); con.setTimeout(); - - node.getNodeConfiguration(); - } -} + + node.getNodeConfiguration(); + } +} diff --git a/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java b/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java index e6b193ea0..b0b3ac382 100644 --- a/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java +++ b/extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java @@ -14,8 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - +package org.jivesoftware.smackx.pubsub; + import static org.custommonkey.xmlunit.XMLAssert.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -36,66 +36,66 @@ import org.xmlpull.v1.XmlPullParser; * * @author Robin Collier * - */ -public class ItemValidationTest -{ - private ThreadedDummyConnection connection; - - @Before - public void setUp() throws Exception - { - // Uncomment this to enable debug output - // Connection.DEBUG_ENABLED = true; - - connection = new ThreadedDummyConnection(); - connection.connect(); - connection.login("me", "secret"); - } - - @After - public void tearDown() throws Exception - { - if (connection != null) - connection.disconnect(); - } - - @Test - public void verifyBasicItem() throws Exception - { - Item simpleItem = new Item(); - String simpleCtrl = ""; - assertXMLEqual(simpleCtrl, simpleItem.toXML()); - - Item idItem = new Item("uniqueid"); - String idCtrl = ""; - assertXMLEqual(idCtrl, idItem.toXML()); - - Item itemWithNodeId = new Item("testId", "testNode"); - String nodeIdCtrl = ""; - assertXMLEqual(nodeIdCtrl, itemWithNodeId.toXML()); - } - - @Test - public void verifyPayloadItem() throws Exception - { - SimplePayload payload = new SimplePayload(null, null, "This is the payload"); - - PayloadItem simpleItem = new PayloadItem(payload); - String simpleCtrl = "" + payload.toXML() + ""; - assertXMLEqual(simpleCtrl, simpleItem.toXML()); - - PayloadItem idItem = new PayloadItem("uniqueid", payload); - String idCtrl = "" + payload.toXML() + ""; - assertXMLEqual(idCtrl, idItem.toXML()); - - PayloadItem itemWithNodeId = new PayloadItem("testId", "testNode", payload); - String nodeIdCtrl = "" + payload.toXML() + ""; - assertXMLEqual(nodeIdCtrl, itemWithNodeId.toXML()); - } + */ +public class ItemValidationTest +{ + private ThreadedDummyConnection connection; - @Ignore // gradle migration - @Test - public void parseBasicItem() throws Exception + @Before + public void setUp() throws Exception + { + // Uncomment this to enable debug output + // Connection.DEBUG_ENABLED = true; + + connection = new ThreadedDummyConnection(); + connection.connect(); + connection.login("me", "secret"); + } + + @After + public void tearDown() throws Exception + { + if (connection != null) + connection.disconnect(); + } + + @Test + public void verifyBasicItem() throws Exception + { + Item simpleItem = new Item(); + String simpleCtrl = ""; + assertXMLEqual(simpleCtrl, simpleItem.toXML()); + + Item idItem = new Item("uniqueid"); + String idCtrl = ""; + assertXMLEqual(idCtrl, idItem.toXML()); + + Item itemWithNodeId = new Item("testId", "testNode"); + String nodeIdCtrl = ""; + assertXMLEqual(nodeIdCtrl, itemWithNodeId.toXML()); + } + + @Test + public void verifyPayloadItem() throws Exception + { + SimplePayload payload = new SimplePayload(null, null, "This is the payload"); + + PayloadItem simpleItem = new PayloadItem(payload); + String simpleCtrl = "" + payload.toXML() + ""; + assertXMLEqual(simpleCtrl, simpleItem.toXML()); + + PayloadItem idItem = new PayloadItem("uniqueid", payload); + String idCtrl = "" + payload.toXML() + ""; + assertXMLEqual(idCtrl, idItem.toXML()); + + PayloadItem itemWithNodeId = new PayloadItem("testId", "testNode", payload); + String nodeIdCtrl = "" + payload.toXML() + ""; + assertXMLEqual(nodeIdCtrl, itemWithNodeId.toXML()); + } + + @Ignore // gradle migration + @Test + public void parseBasicItem() throws Exception { XmlPullParser parser = TestUtils.getMessageParser( "" + @@ -119,7 +119,7 @@ public class ItemValidationTest PacketExtension itemExt = ((ItemsExtension)event.getExtensions().get(0)).items.get(0); assertTrue(itemExt instanceof Item); assertEquals("testid1", ((Item)itemExt).getId()); - } + } @Ignore // gradle migration @Test @@ -239,4 +239,4 @@ public class ItemValidationTest assertXMLEqual(itemContent, ((SimplePayload)item.getPayload()).toXML()); } -} +} diff --git a/extensions/src/test/java/org/jivesoftware/util/ConnectionUtils.java b/extensions/src/test/java/org/jivesoftware/util/ConnectionUtils.java index 33ab75caf..45c1ea8de 100644 --- a/extensions/src/test/java/org/jivesoftware/util/ConnectionUtils.java +++ b/extensions/src/test/java/org/jivesoftware/util/ConnectionUtils.java @@ -1,97 +1,97 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.util; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.jivesoftware.smack.PacketCollector; -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.filter.PacketFilter; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -/** - * A collection of utility methods to create mocked XMPP connections. - * - * @author Henning Staib - */ -public class ConnectionUtils { - - /** - * Creates a mocked XMPP connection that stores every packet that is send over this - * connection in the given protocol instance and returns the predefined answer packets - * form the protocol instance. - *

    - * This mocked connection can used to collect packets that require a reply using a - * PacketCollector. - * - *

    -     * 
    -     *   PacketCollector collector = connection.createPacketCollector(new PacketFilter());
    -     *   connection.sendPacket(packet);
    -     *   Packet reply = collector.nextResult();
    -     * 
    -     * 
    - * - * @param protocol protocol helper containing answer packets - * @param initiatorJID the user associated to the XMPP connection - * @param xmppServer the XMPP server associated to the XMPP connection - * @return a mocked XMPP connection - */ - public static Connection createMockedConnection(final Protocol protocol, - String initiatorJID, String xmppServer) { - - // mock XMPP connection - Connection connection = mock(Connection.class); - when(connection.getUser()).thenReturn(initiatorJID); - when(connection.getServiceName()).thenReturn(xmppServer); - - // mock packet collector - PacketCollector collector = mock(PacketCollector.class); - when(connection.createPacketCollector(isA(PacketFilter.class))).thenReturn( - collector); - Answer addIncoming = new Answer() { - - public Object answer(InvocationOnMock invocation) throws Throwable { - protocol.getRequests().add((Packet) invocation.getArguments()[0]); - return null; - } - }; - - // mock send method - doAnswer(addIncoming).when(connection).sendPacket(isA(Packet.class)); - Answer answer = new Answer() { - - public Packet answer(InvocationOnMock invocation) throws Throwable { - return protocol.getResponses().poll(); - } - }; - - // mock nextResult method - when(collector.nextResult(anyInt())).thenAnswer(answer); - when(collector.nextResult()).thenAnswer(answer); - - // initialize service discovery manager for this connection - ServiceDiscoveryManager.getInstanceFor(connection); - - return connection; - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.util; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +/** + * A collection of utility methods to create mocked XMPP connections. + * + * @author Henning Staib + */ +public class ConnectionUtils { + + /** + * Creates a mocked XMPP connection that stores every packet that is send over this + * connection in the given protocol instance and returns the predefined answer packets + * form the protocol instance. + *

    + * This mocked connection can used to collect packets that require a reply using a + * PacketCollector. + * + *

    +     * 
    +     *   PacketCollector collector = connection.createPacketCollector(new PacketFilter());
    +     *   connection.sendPacket(packet);
    +     *   Packet reply = collector.nextResult();
    +     * 
    +     * 
    + * + * @param protocol protocol helper containing answer packets + * @param initiatorJID the user associated to the XMPP connection + * @param xmppServer the XMPP server associated to the XMPP connection + * @return a mocked XMPP connection + */ + public static Connection createMockedConnection(final Protocol protocol, + String initiatorJID, String xmppServer) { + + // mock XMPP connection + Connection connection = mock(Connection.class); + when(connection.getUser()).thenReturn(initiatorJID); + when(connection.getServiceName()).thenReturn(xmppServer); + + // mock packet collector + PacketCollector collector = mock(PacketCollector.class); + when(connection.createPacketCollector(isA(PacketFilter.class))).thenReturn( + collector); + Answer addIncoming = new Answer() { + + public Object answer(InvocationOnMock invocation) throws Throwable { + protocol.getRequests().add((Packet) invocation.getArguments()[0]); + return null; + } + }; + + // mock send method + doAnswer(addIncoming).when(connection).sendPacket(isA(Packet.class)); + Answer answer = new Answer() { + + public Packet answer(InvocationOnMock invocation) throws Throwable { + return protocol.getResponses().poll(); + } + }; + + // mock nextResult method + when(collector.nextResult(anyInt())).thenAnswer(answer); + when(collector.nextResult()).thenAnswer(answer); + + // initialize service discovery manager for this connection + ServiceDiscoveryManager.getInstanceFor(connection); + + return connection; + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/util/Protocol.java b/extensions/src/test/java/org/jivesoftware/util/Protocol.java index e234c154c..c1da05d0f 100644 --- a/extensions/src/test/java/org/jivesoftware/util/Protocol.java +++ b/extensions/src/test/java/org/jivesoftware/util/Protocol.java @@ -1,198 +1,198 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.util; - -import static org.junit.Assert.*; - -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.jivesoftware.smack.packet.Packet; - -/** - * This class can be used in conjunction with a mocked XMPP connection ( - * {@link ConnectionUtils#createMockedConnection(Protocol, String, String)}) to - * verify a XMPP protocol. This can be accomplished in the following was: - *
      - *
    • add responses to packets sent over the mocked XMPP connection by the - * method to test in the order the tested method awaits them
    • - *
    • call the method to test
    • - *
    • call {@link #verifyAll()} to run assertions on the request/response pairs - *
    • - *
    - * Example: - * - *
    - * 
    - * public void methodToTest() {
    - *   Packet packet = new Packet(); // create an XMPP packet
    - *   PacketCollector collector = connection.createPacketCollector(new PacketIDFilter());
    - *   connection.sendPacket(packet);
    - *   Packet reply = collector.nextResult();
    - * }
    - * 
    - * public void testMethod() {
    - *   // create protocol
    - *   Protocol protocol = new Protocol();
    - *   // create mocked connection
    - *   Connection connection = ConnectionUtils.createMockedConnection(protocol, "user@xmpp-server", "xmpp-server");
    - *   
    - *   // add reply packet to protocol
    - *   Packet reply = new Packet();
    - *   protocol.add(reply);
    - *   
    - *   // call method to test
    - *   methodToTest();
    - *   
    - *   // verify protocol
    - *   protocol.verifyAll();
    - * }
    - * 
    - * 
    - * - * Additionally to adding the response to the protocol instance you can pass - * verifications that will be executed when {@link #verifyAll()} is invoked. - * (See {@link Verification} for more details.) - *

    - * If the {@link #printProtocol} flag is set to true {@link #verifyAll()} will - * also print out the XML messages in the order they are sent to the console. - * This may be useful to inspect the whole protocol "by hand". - * - * @author Henning Staib - */ -public class Protocol { - - /** - * Set to true to print XML messages to the console while - * verifying the protocol. - */ - public boolean printProtocol = false; - - // responses to requests are taken form this queue - Queue responses = new LinkedList(); - - // list of verifications - List[]> verificationList = new ArrayList[]>(); - - // list of requests - List requests = new ArrayList(); - - // list of all responses - List responsesList = new ArrayList(); - - /** - * Adds a responses and all verifications for the request/response pair to - * the protocol. - * - * @param response the response for a request - * @param verifications verifications for request/response pair - */ - public void addResponse(Packet response, Verification... verifications) { - responses.offer(response); - verificationList.add(verifications); - responsesList.add(response); - } - - /** - * Verifies the request/response pairs by checking if their numbers match - * and executes the verification for each pair. - */ - @SuppressWarnings("unchecked") - public void verifyAll() { - assertEquals(requests.size(), responsesList.size()); - - if (printProtocol) - System.out.println("=================== Start ===============\n"); - - for (int i = 0; i < requests.size(); i++) { - Packet request = requests.get(i); - Packet response = responsesList.get(i); - - if (printProtocol) { - System.out.println("------------------- Request -------------\n"); - System.out.println(prettyFormat(request.toXML())); - System.out.println("------------------- Response ------------\n"); - if (response != null) { - System.out.println(prettyFormat(response.toXML())); - } - else { - System.out.println("No response"); - } - } - - Verification[] verifications = (Verification[]) verificationList.get(i); - if (verifications != null) { - for (Verification verification : verifications) { - verification.verify(request, response); - } - } - } - if (printProtocol) - System.out.println("=================== End =================\n"); - } - - /** - * Returns the responses queue. - * - * @return the responses queue - */ - protected Queue getResponses() { - return responses; - } - - /** - * Returns a list of all collected requests. - * - * @return list of requests - */ - public List getRequests() { - return requests; - } - - private String prettyFormat(String input, int indent) { - try { - Source xmlInput = new StreamSource(new StringReader(input)); - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", - String.valueOf(indent)); - transformer.transform(xmlInput, xmlOutput); - return xmlOutput.getWriter().toString(); - } - catch (Exception e) { - return "error while formatting the XML: " + e.getMessage(); - } - } - - private String prettyFormat(String input) { - return prettyFormat(input, 2); - } - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.util; + +import static org.junit.Assert.*; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.jivesoftware.smack.packet.Packet; + +/** + * This class can be used in conjunction with a mocked XMPP connection ( + * {@link ConnectionUtils#createMockedConnection(Protocol, String, String)}) to + * verify a XMPP protocol. This can be accomplished in the following was: + *

      + *
    • add responses to packets sent over the mocked XMPP connection by the + * method to test in the order the tested method awaits them
    • + *
    • call the method to test
    • + *
    • call {@link #verifyAll()} to run assertions on the request/response pairs + *
    • + *
    + * Example: + * + *
    + * 
    + * public void methodToTest() {
    + *   Packet packet = new Packet(); // create an XMPP packet
    + *   PacketCollector collector = connection.createPacketCollector(new PacketIDFilter());
    + *   connection.sendPacket(packet);
    + *   Packet reply = collector.nextResult();
    + * }
    + * 
    + * public void testMethod() {
    + *   // create protocol
    + *   Protocol protocol = new Protocol();
    + *   // create mocked connection
    + *   Connection connection = ConnectionUtils.createMockedConnection(protocol, "user@xmpp-server", "xmpp-server");
    + *   
    + *   // add reply packet to protocol
    + *   Packet reply = new Packet();
    + *   protocol.add(reply);
    + *   
    + *   // call method to test
    + *   methodToTest();
    + *   
    + *   // verify protocol
    + *   protocol.verifyAll();
    + * }
    + * 
    + * 
    + * + * Additionally to adding the response to the protocol instance you can pass + * verifications that will be executed when {@link #verifyAll()} is invoked. + * (See {@link Verification} for more details.) + *

    + * If the {@link #printProtocol} flag is set to true {@link #verifyAll()} will + * also print out the XML messages in the order they are sent to the console. + * This may be useful to inspect the whole protocol "by hand". + * + * @author Henning Staib + */ +public class Protocol { + + /** + * Set to true to print XML messages to the console while + * verifying the protocol. + */ + public boolean printProtocol = false; + + // responses to requests are taken form this queue + Queue responses = new LinkedList(); + + // list of verifications + List[]> verificationList = new ArrayList[]>(); + + // list of requests + List requests = new ArrayList(); + + // list of all responses + List responsesList = new ArrayList(); + + /** + * Adds a responses and all verifications for the request/response pair to + * the protocol. + * + * @param response the response for a request + * @param verifications verifications for request/response pair + */ + public void addResponse(Packet response, Verification... verifications) { + responses.offer(response); + verificationList.add(verifications); + responsesList.add(response); + } + + /** + * Verifies the request/response pairs by checking if their numbers match + * and executes the verification for each pair. + */ + @SuppressWarnings("unchecked") + public void verifyAll() { + assertEquals(requests.size(), responsesList.size()); + + if (printProtocol) + System.out.println("=================== Start ===============\n"); + + for (int i = 0; i < requests.size(); i++) { + Packet request = requests.get(i); + Packet response = responsesList.get(i); + + if (printProtocol) { + System.out.println("------------------- Request -------------\n"); + System.out.println(prettyFormat(request.toXML())); + System.out.println("------------------- Response ------------\n"); + if (response != null) { + System.out.println(prettyFormat(response.toXML())); + } + else { + System.out.println("No response"); + } + } + + Verification[] verifications = (Verification[]) verificationList.get(i); + if (verifications != null) { + for (Verification verification : verifications) { + verification.verify(request, response); + } + } + } + if (printProtocol) + System.out.println("=================== End =================\n"); + } + + /** + * Returns the responses queue. + * + * @return the responses queue + */ + protected Queue getResponses() { + return responses; + } + + /** + * Returns a list of all collected requests. + * + * @return list of requests + */ + public List getRequests() { + return requests; + } + + private String prettyFormat(String input, int indent) { + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", + String.valueOf(indent)); + transformer.transform(xmlInput, xmlOutput); + return xmlOutput.getWriter().toString(); + } + catch (Exception e) { + return "error while formatting the XML: " + e.getMessage(); + } + } + + private String prettyFormat(String input) { + return prettyFormat(input, 2); + } + +} diff --git a/extensions/src/test/java/org/jivesoftware/util/Verification.java b/extensions/src/test/java/org/jivesoftware/util/Verification.java index b22d99e10..9c7de80d8 100644 --- a/extensions/src/test/java/org/jivesoftware/util/Verification.java +++ b/extensions/src/test/java/org/jivesoftware/util/Verification.java @@ -1,100 +1,100 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.util; - -import static org.junit.Assert.*; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Packet; - -/** - * Implement this interface to verify a request/response pair. - *

    - * For convenience there are some useful predefined implementations. - * - * @param class of the request - * @param class of the response - * - * @author Henning Staib - */ -public interface Verification { - - /** - * Verifies that the "To" field of the request corresponds with the "From" field of - * the response. - */ - public static Verification correspondingSenderReceiver = new Verification() { - - public void verify(Packet request, Packet response) { - assertEquals(response.getFrom(), request.getTo()); - } - - }; - - /** - * Verifies that the type of the request is a GET. - */ - public static Verification requestTypeGET = new Verification() { - - public void verify(IQ request, Packet response) { - assertEquals(IQ.Type.GET, request.getType()); - } - - }; - - /** - * Verifies that the type of the request is a SET. - */ - public static Verification requestTypeSET = new Verification() { - - public void verify(IQ request, Packet response) { - assertEquals(IQ.Type.SET, request.getType()); - } - - }; - - /** - * Verifies that the type of the request is a RESULT. - */ - public static Verification requestTypeRESULT = new Verification() { - - public void verify(IQ request, Packet response) { - assertEquals(IQ.Type.RESULT, request.getType()); - } - - }; - - /** - * Verifies that the type of the request is an ERROR. - */ - public static Verification requestTypeERROR = new Verification() { - - public void verify(IQ request, Packet response) { - assertEquals(IQ.Type.ERROR, request.getType()); - } - - }; - - /** - * Implement this method to make assertions of the request/response pairs. - * - * @param request the request collected by the mocked XMPP connection - * @param response the response added to the protocol instance - */ - public void verify(T request, S response); - -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.util; + +import static org.junit.Assert.*; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; + +/** + * Implement this interface to verify a request/response pair. + *

    + * For convenience there are some useful predefined implementations. + * + * @param class of the request + * @param class of the response + * + * @author Henning Staib + */ +public interface Verification { + + /** + * Verifies that the "To" field of the request corresponds with the "From" field of + * the response. + */ + public static Verification correspondingSenderReceiver = new Verification() { + + public void verify(Packet request, Packet response) { + assertEquals(response.getFrom(), request.getTo()); + } + + }; + + /** + * Verifies that the type of the request is a GET. + */ + public static Verification requestTypeGET = new Verification() { + + public void verify(IQ request, Packet response) { + assertEquals(IQ.Type.GET, request.getType()); + } + + }; + + /** + * Verifies that the type of the request is a SET. + */ + public static Verification requestTypeSET = new Verification() { + + public void verify(IQ request, Packet response) { + assertEquals(IQ.Type.SET, request.getType()); + } + + }; + + /** + * Verifies that the type of the request is a RESULT. + */ + public static Verification requestTypeRESULT = new Verification() { + + public void verify(IQ request, Packet response) { + assertEquals(IQ.Type.RESULT, request.getType()); + } + + }; + + /** + * Verifies that the type of the request is an ERROR. + */ + public static Verification requestTypeERROR = new Verification() { + + public void verify(IQ request, Packet response) { + assertEquals(IQ.Type.ERROR, request.getType()); + } + + }; + + /** + * Implement this method to make assertions of the request/response pairs. + * + * @param request the request collected by the mocked XMPP connection + * @param response the response added to the protocol instance + */ + public void verify(T request, S response); + +} diff --git a/integration-test/org/jivesoftware/smack/PrivacyClient.java b/integration-test/org/jivesoftware/smack/PrivacyClient.java index 6e4a70025..b9c3dd3cd 100644 --- a/integration-test/org/jivesoftware/smack/PrivacyClient.java +++ b/integration-test/org/jivesoftware/smack/PrivacyClient.java @@ -12,45 +12,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack; - -import java.util.List; - -import org.jivesoftware.smack.packet.Privacy; -import org.jivesoftware.smack.packet.PrivacyItem; - -/** - * This class supports automated tests about privacy communication from the - * server to the client. - * - * @author Francisco Vives - */ - -public class PrivacyClient implements PrivacyListListener { - /** - * holds if the receiver list was modified - */ - private boolean wasModified = false; - - /** - * holds a privacy to hold server requests Clients should not use Privacy - * class since it is private for the smack framework. - */ - private Privacy privacy = new Privacy(); - - public PrivacyClient(PrivacyListManager manager) { - super(); - } - - public void setPrivacyList(String listName, List listItem) { - privacy.setPrivacyList(listName, listItem); - } - - public void updatedPrivacyList(String listName) { - this.wasModified = true; - } - - public boolean wasModified() { - return this.wasModified; - } -} +package org.jivesoftware.smack; + +import java.util.List; + +import org.jivesoftware.smack.packet.Privacy; +import org.jivesoftware.smack.packet.PrivacyItem; + +/** + * This class supports automated tests about privacy communication from the + * server to the client. + * + * @author Francisco Vives + */ + +public class PrivacyClient implements PrivacyListListener { + /** + * holds if the receiver list was modified + */ + private boolean wasModified = false; + + /** + * holds a privacy to hold server requests Clients should not use Privacy + * class since it is private for the smack framework. + */ + private Privacy privacy = new Privacy(); + + public PrivacyClient(PrivacyListManager manager) { + super(); + } + + public void setPrivacyList(String listName, List listItem) { + privacy.setPrivacyList(listName, listItem); + } + + public void updatedPrivacyList(String listName) { + this.wasModified = true; + } + + public boolean wasModified() { + return this.wasModified; + } +} diff --git a/integration-test/org/jivesoftware/smack/ReconnectionTest.java b/integration-test/org/jivesoftware/smack/ReconnectionTest.java index 870977e7e..fd11bf354 100644 --- a/integration-test/org/jivesoftware/smack/ReconnectionTest.java +++ b/integration-test/org/jivesoftware/smack/ReconnectionTest.java @@ -12,246 +12,246 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack; - +package org.jivesoftware.smack; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smackx.ping.PingManager; - -/** - * Tests the connection and reconnection mechanism - * - * @author Francisco Vives - */ - -public class ReconnectionTest extends SmackTestCase { + +/** + * Tests the connection and reconnection mechanism + * + * @author Francisco Vives + */ + +public class ReconnectionTest extends SmackTestCase { private static final long MIN_RECONNECT_WAIT = 17; // Seconds - - public ReconnectionTest(String arg0) { - super(arg0); - } - - /** - * Tests an automatic reconnection. - * Simulates a connection error and then waits until gets reconnected. - */ - - public void testAutomaticReconnection() throws Exception { + + public ReconnectionTest(String arg0) { + super(arg0); + } + + /** + * Tests an automatic reconnection. + * Simulates a connection error and then waits until gets reconnected. + */ + + public void testAutomaticReconnection() throws Exception { XMPPConnection connection = getConnection(0); - CountDownLatch latch = new CountDownLatch(1); - XMPPConnectionTestListener listener = new XMPPConnectionTestListener(latch); - connection.addConnectionListener(listener); - - // Simulates an error in the connection - connection.notifyConnectionError(new Exception("Simulated Error")); - latch.await(MIN_RECONNECT_WAIT, TimeUnit.SECONDS); - - // After 10 seconds, the reconnection manager must reestablishes the connection - assertEquals("The ConnectionListener.connectionStablished() notification was not fired", true, listener.reconnected); - assertTrue("The ReconnectionManager algorithm has reconnected without waiting at least 5 seconds", listener.attemptsNotifications > 0); - - // Executes some server interaction testing the connection - executeSomeServerInteraction(connection); - } - - public void testAutomaticReconnectionWithCompression() throws Exception { - // Create the configuration for this new connection - ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); - config.setCompressionEnabled(true); - config.setSASLAuthenticationEnabled(true); - - XMPPConnection connection = new XMPPConnection(config); - // Connect to the server - connection.connect(); - // Log into the server - connection.login(getUsername(0), getPassword(0), "MyOtherResource"); - - assertTrue("Failed to use compression", connection.isUsingCompression()); - - // Executes some server interaction testing the connection - executeSomeServerInteraction(connection); - CountDownLatch latch = new CountDownLatch(1); - XMPPConnectionTestListener listener = new XMPPConnectionTestListener(latch); - connection.addConnectionListener(listener); - - // Simulates an error in the connection - connection.notifyConnectionError(new Exception("Simulated Error")); + XMPPConnectionTestListener listener = new XMPPConnectionTestListener(latch); + connection.addConnectionListener(listener); + + // Simulates an error in the connection + connection.notifyConnectionError(new Exception("Simulated Error")); latch.await(MIN_RECONNECT_WAIT, TimeUnit.SECONDS); - - // After 10 seconds, the reconnection manager must reestablishes the connection - assertEquals("The ConnectionListener.connectionEstablished() notification was not fired", true, listener.reconnected); + + // After 10 seconds, the reconnection manager must reestablishes the connection + assertEquals("The ConnectionListener.connectionStablished() notification was not fired", true, listener.reconnected); assertTrue("The ReconnectionManager algorithm has reconnected without waiting at least 5 seconds", listener.attemptsNotifications > 0); - - // Executes some server interaction testing the connection - executeSomeServerInteraction(connection); - } - - /** - * Tests a manual reconnection. - * Simulates a connection error, disables the reconnection mechanism and then reconnects. - */ - public void testManualReconnectionWithCancelation() throws Exception { - XMPPConnection connection = getConnection(0); + + // Executes some server interaction testing the connection + executeSomeServerInteraction(connection); + } + + public void testAutomaticReconnectionWithCompression() throws Exception { + // Create the configuration for this new connection + ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); + config.setCompressionEnabled(true); + config.setSASLAuthenticationEnabled(true); + + XMPPConnection connection = new XMPPConnection(config); + // Connect to the server + connection.connect(); + // Log into the server + connection.login(getUsername(0), getPassword(0), "MyOtherResource"); + + assertTrue("Failed to use compression", connection.isUsingCompression()); + + // Executes some server interaction testing the connection + executeSomeServerInteraction(connection); + CountDownLatch latch = new CountDownLatch(1); XMPPConnectionTestListener listener = new XMPPConnectionTestListener(latch); - connection.addConnectionListener(listener); - - // Produces a connection error - connection.notifyConnectionError(new Exception("Simulated Error")); - assertEquals( - "An error occurs but the ConnectionListener.connectionClosedOnError(e) was not notified", - true, listener.connectionClosedOnError); + connection.addConnectionListener(listener); + + // Simulates an error in the connection + connection.notifyConnectionError(new Exception("Simulated Error")); + latch.await(MIN_RECONNECT_WAIT, TimeUnit.SECONDS); + + // After 10 seconds, the reconnection manager must reestablishes the connection + assertEquals("The ConnectionListener.connectionEstablished() notification was not fired", true, listener.reconnected); + assertTrue("The ReconnectionManager algorithm has reconnected without waiting at least 5 seconds", listener.attemptsNotifications > 0); + + // Executes some server interaction testing the connection + executeSomeServerInteraction(connection); + } + + /** + * Tests a manual reconnection. + * Simulates a connection error, disables the reconnection mechanism and then reconnects. + */ + public void testManualReconnectionWithCancelation() throws Exception { + XMPPConnection connection = getConnection(0); + CountDownLatch latch = new CountDownLatch(1); + XMPPConnectionTestListener listener = new XMPPConnectionTestListener(latch); + connection.addConnectionListener(listener); + + // Produces a connection error + connection.notifyConnectionError(new Exception("Simulated Error")); + assertEquals( + "An error occurs but the ConnectionListener.connectionClosedOnError(e) was not notified", + true, listener.connectionClosedOnError); // Thread.sleep(1000); - - // Cancels the automatic reconnection - connection.getConfiguration().setReconnectionAllowed(false); - // Waits for a reconnection that must not happened. - Thread.sleep(MIN_RECONNECT_WAIT * 1000); - // Cancels the automatic reconnection - assertEquals(false, listener.reconnected); - - // Makes a manual reconnection from an error terminated connection without reconnection - connection.connect(); - - // Executes some server interaction testing the connection - executeSomeServerInteraction(connection); - } - - /** - * Tests a manual reconnection after a login. - * Closes the connection and then reconnects. - */ - public void testCloseAndManualReconnection() throws Exception { - XMPPConnection connection = getConnection(0); - String username = connection.getConfiguration().getUsername(); - String password = connection.getConfiguration().getPassword(); - XMPPConnectionTestListener listener = new XMPPConnectionTestListener(); - connection.addConnectionListener(listener); - - // Produces a normal disconnection - connection.disconnect(); - assertEquals("ConnectionListener.connectionClosed() was not notified", - true, listener.connectionClosed); - // Waits 10 seconds waiting for a reconnection that must not happened. - Thread.sleep(MIN_RECONNECT_WAIT * 1000); - assertEquals("The connection was stablished but it was not allowed to", false, - listener.reconnected); - - // Makes a manual reconnection - connection.connect(); - connection.login(username, password); - - // Executes some server interaction testing the connection - executeSomeServerInteraction(connection); - } - - /** - * Tests a reconnection in a anonymously logged connection. - * Closes the connection and then reconnects. - */ - public void testAnonymousReconnection() throws Exception { - XMPPConnection connection = createConnection(); - connection.connect(); - XMPPConnectionTestListener listener = new XMPPConnectionTestListener(); - connection.addConnectionListener(listener); - - // Makes the anounymous login - connection.loginAnonymously(); - - // Produces a normal disconnection - connection.disconnect(); - assertEquals("ConnectionListener.connectionClosed() was not notified", - true, listener.connectionClosed); - // Makes a manual reconnection - connection.connect(); - connection.loginAnonymously(); - assertEquals("Failed the manual connection", true, connection.isAnonymous()); - } - - private XMPPConnection createXMPPConnection() throws Exception { - XMPPConnection connection; - // Create the configuration - ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); - config.setCompressionEnabled(Boolean.getBoolean("test.compressionEnabled")); - config.setSASLAuthenticationEnabled(true); - connection = new XMPPConnection(config); - - return connection; - } - - /** - * Execute some server interaction in order to test that the regenerated connection works fine. - */ - private void executeSomeServerInteraction(XMPPConnection connection) throws XMPPException { - PingManager pingManager = PingManager.getInstanceFor(connection); - pingManager.pingMyServer(); - } - - protected int getMaxConnections() { - return 1; - } - - private class XMPPConnectionTestListener implements ConnectionListener { - - // Variables to support listener notifications verification - private volatile boolean connectionClosed = false; - private volatile boolean connectionClosedOnError = false; - private volatile boolean reconnected = false; - private volatile boolean reconnectionFailed = false; - private volatile int remainingSeconds = 0; - private volatile int attemptsNotifications = 0; + + // Cancels the automatic reconnection + connection.getConfiguration().setReconnectionAllowed(false); + // Waits for a reconnection that must not happened. + Thread.sleep(MIN_RECONNECT_WAIT * 1000); + // Cancels the automatic reconnection + assertEquals(false, listener.reconnected); + + // Makes a manual reconnection from an error terminated connection without reconnection + connection.connect(); + + // Executes some server interaction testing the connection + executeSomeServerInteraction(connection); + } + + /** + * Tests a manual reconnection after a login. + * Closes the connection and then reconnects. + */ + public void testCloseAndManualReconnection() throws Exception { + XMPPConnection connection = getConnection(0); + String username = connection.getConfiguration().getUsername(); + String password = connection.getConfiguration().getPassword(); + XMPPConnectionTestListener listener = new XMPPConnectionTestListener(); + connection.addConnectionListener(listener); + + // Produces a normal disconnection + connection.disconnect(); + assertEquals("ConnectionListener.connectionClosed() was not notified", + true, listener.connectionClosed); + // Waits 10 seconds waiting for a reconnection that must not happened. + Thread.sleep(MIN_RECONNECT_WAIT * 1000); + assertEquals("The connection was stablished but it was not allowed to", false, + listener.reconnected); + + // Makes a manual reconnection + connection.connect(); + connection.login(username, password); + + // Executes some server interaction testing the connection + executeSomeServerInteraction(connection); + } + + /** + * Tests a reconnection in a anonymously logged connection. + * Closes the connection and then reconnects. + */ + public void testAnonymousReconnection() throws Exception { + XMPPConnection connection = createConnection(); + connection.connect(); + XMPPConnectionTestListener listener = new XMPPConnectionTestListener(); + connection.addConnectionListener(listener); + + // Makes the anounymous login + connection.loginAnonymously(); + + // Produces a normal disconnection + connection.disconnect(); + assertEquals("ConnectionListener.connectionClosed() was not notified", + true, listener.connectionClosed); + // Makes a manual reconnection + connection.connect(); + connection.loginAnonymously(); + assertEquals("Failed the manual connection", true, connection.isAnonymous()); + } + + private XMPPConnection createXMPPConnection() throws Exception { + XMPPConnection connection; + // Create the configuration + ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); + config.setCompressionEnabled(Boolean.getBoolean("test.compressionEnabled")); + config.setSASLAuthenticationEnabled(true); + connection = new XMPPConnection(config); + + return connection; + } + + /** + * Execute some server interaction in order to test that the regenerated connection works fine. + */ + private void executeSomeServerInteraction(XMPPConnection connection) throws XMPPException { + PingManager pingManager = PingManager.getInstanceFor(connection); + pingManager.pingMyServer(); + } + + protected int getMaxConnections() { + return 1; + } + + private class XMPPConnectionTestListener implements ConnectionListener { + + // Variables to support listener notifications verification + private volatile boolean connectionClosed = false; + private volatile boolean connectionClosedOnError = false; + private volatile boolean reconnected = false; + private volatile boolean reconnectionFailed = false; + private volatile int remainingSeconds = 0; + private volatile int attemptsNotifications = 0; private volatile boolean reconnectionCanceled = false; - private CountDownLatch countDownLatch; + private CountDownLatch countDownLatch; private XMPPConnectionTestListener(CountDownLatch latch) { countDownLatch = latch; - } + } private XMPPConnectionTestListener() { } - /** - * Methods to test the listener. - */ + /** + * Methods to test the listener. + */ public void connectionClosed() { connectionClosed = true; - if (countDownLatch != null) + if (countDownLatch != null) countDownLatch.countDown(); - } - - public void connectionClosedOnError(Exception e) { + } + + public void connectionClosedOnError(Exception e) { connectionClosedOnError = true; - } - - public void reconnectionCanceled() { - reconnectionCanceled = true; + } + + public void reconnectionCanceled() { + reconnectionCanceled = true; if (countDownLatch != null) countDownLatch.countDown(); - } - - public void reconnectingIn(int seconds) { - attemptsNotifications = attemptsNotifications + 1; + } + + public void reconnectingIn(int seconds) { + attemptsNotifications = attemptsNotifications + 1; remainingSeconds = seconds; - } - - public void reconnectionSuccessful() { - reconnected = true; + } + + public void reconnectionSuccessful() { + reconnected = true; if (countDownLatch != null) countDownLatch.countDown(); - } - - public void reconnectionFailed(Exception error) { - reconnectionFailed = true; + } + + public void reconnectionFailed(Exception error) { + reconnectionFailed = true; if (countDownLatch != null) countDownLatch.countDown(); - } - } - -} \ No newline at end of file + } + } + +} diff --git a/integration-test/org/jivesoftware/smack/RosterSmackTest.java b/integration-test/org/jivesoftware/smack/RosterSmackTest.java index 050fe32ff..f95f4a476 100644 --- a/integration-test/org/jivesoftware/smack/RosterSmackTest.java +++ b/integration-test/org/jivesoftware/smack/RosterSmackTest.java @@ -707,4 +707,4 @@ public class RosterSmackTest extends SmackTestCase { public void entriesAdded(Collection addresses) {} }; -} \ No newline at end of file +} diff --git a/integration-test/org/jivesoftware/smack/packet/PrivacyProviderTest.java b/integration-test/org/jivesoftware/smack/packet/PrivacyProviderTest.java index 31b5612e8..5ac097e6d 100644 --- a/integration-test/org/jivesoftware/smack/packet/PrivacyProviderTest.java +++ b/integration-test/org/jivesoftware/smack/packet/PrivacyProviderTest.java @@ -12,373 +12,373 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.packet; - -import org.jivesoftware.smack.provider.PrivacyProvider; -import org.jivesoftware.smack.test.SmackTestCase; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.StringReader; - -/** - * Test the PrivacyProvider class with valids privacy xmls - * - * @author Francisco Vives - */ -public class PrivacyProviderTest extends SmackTestCase { - - /** - * Constructor for PrivacyTest. - * @param arg0 - */ - public PrivacyProviderTest(String arg0) { - super(arg0); - } - - public static void main(String args[]) { - try { - new PrivacyProviderTest(null).testFull(); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } - - /** - * Check the parser with an xml with all kind of stanzas. - * To create the xml string based from an xml file, replace:\n with: "\n + " - */ - public void testFull() { - // Make the XML to test - String xml = "" - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " "; - - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); - - // check if it exist - assertNotNull(packet); - // assertEquals(xml, packet.getChildElementXML()); - - // check the default and active names - assertEquals("testFilter", packet.getActiveName()); - assertEquals("testSubscription", packet.getDefaultName()); - - // check the list - assertEquals(2, packet.getPrivacyList("testFilter").size()); - assertEquals(5, packet.getPrivacyList("testSubscription").size()); - assertEquals(4, packet.getPrivacyList("testJID").size()); - assertEquals(2, packet.getPrivacyList("testGroup").size()); - assertEquals(0, packet.getPrivacyList("testEmpty").size()); - - // check each privacy item - PrivacyItem item = packet.getItem("testGroup", 4); - assertEquals("Enemies", item.getValue()); - assertEquals(PrivacyItem.Type.group, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(true, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(false, item.isFilterEverything()); - - item = packet.getItem("testFilter", 1); - assertEquals("tybalt@example.com", item.getValue()); - assertEquals(PrivacyItem.Type.jid, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testFilter", 2); - assertEquals(null, item.getValue()); - assertEquals(null, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(true, item.isFilterMessage()); - assertEquals(true, item.isFilterIQ()); - assertEquals(true, item.isFilterPresence_in()); - assertEquals(true, item.isFilterPresence_out()); - assertEquals(false, item.isFilterEverything()); - - // TEST THE testSubscription LIST - item = packet.getItem("testSubscription", 10); - assertEquals("both", item.getValue()); - assertEquals(PrivacyItem.Type.subscription, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testSubscription", 11); - assertEquals("to", item.getValue()); - assertEquals(PrivacyItem.Type.subscription, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testSubscription", 12); - assertEquals("from", item.getValue()); - assertEquals(PrivacyItem.Type.subscription, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testSubscription", 5); - assertEquals("none", item.getValue()); - assertEquals(PrivacyItem.Type.subscription, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(true, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(false, item.isFilterEverything()); - - item = packet.getItem("testSubscription", 15); - assertEquals(null, item.getValue()); - assertEquals(null, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - // TEST THE testJID LIST - - item = packet.getItem("testJID", 6); - assertEquals("juliet@example.com", item.getValue()); - assertEquals(PrivacyItem.Type.jid, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testJID", 7); - assertEquals("benvolio@example.org/palm", item.getValue()); - assertEquals(PrivacyItem.Type.jid, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testJID", 42); - assertEquals(null, item.getValue()); - assertEquals(PrivacyItem.Type.jid, item.getType()); - assertEquals(true, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - item = packet.getItem("testJID", 666); - assertEquals(null, item.getValue()); - assertEquals(null, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - // TEST THE testGroup LIST - - item = packet.getItem("testGroup", 4); - assertEquals("Enemies", item.getValue()); - assertEquals(PrivacyItem.Type.group, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(true, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(false, item.isFilterEverything()); - - item = packet.getItem("testGroup", 666); - assertEquals(null, item.getValue()); - assertEquals(null, item.getType()); - assertEquals(false, item.isAllow()); - assertEquals(false, item.isFilterMessage()); - assertEquals(false, item.isFilterIQ()); - assertEquals(false, item.isFilterPresence_in()); - assertEquals(false, item.isFilterPresence_out()); - assertEquals(true, item.isFilterEverything()); - - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - - /** - * Check the parser with an xml with empty lists. It includes the active, - * default and special list. - * To create the xml string based from an xml file, replace:\n with: "\n + " - */ - public void testEmptyLists() { - // Make the XML to test - String xml = "" - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " " - + " "; - - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); - - assertNotNull(packet); - assertNotNull(packet.getChildElementXML()); - - assertEquals("public", packet.getDefaultName()); - assertEquals(null, packet.getActiveName()); - - assertEquals(0, packet.getPrivacyList("public").size()); - assertEquals(0, packet.getPrivacyList("private").size()); - assertEquals(0, packet.getPrivacyList("special").size()); - - assertEquals(true, packet.isDeclineActiveList()); - assertEquals(false, packet.isDeclineDefaultList()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check the parser with an xml with empty lists. It includes the active, - * default and special list. - * To create the xml string based from an xml file, replace:\n with: "\n + " - */ - public void testDeclineLists() { - // Make the XML to test - String xml = "" - + " " - + " " - + " " - + " " - + " " - + " "; - - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); - - assertNotNull(packet); - - assertEquals(null, packet.getDefaultName()); - assertEquals(null, packet.getActiveName()); - - assertEquals(true, packet.isDeclineActiveList()); - assertEquals(true, packet.isDeclineDefaultList()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - private XmlPullParser getParserFromXML(String xml) throws XmlPullParserException { - MXParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new StringReader(xml)); - return parser; - } - - protected int getMaxConnections() { - return 0; - } -} +package org.jivesoftware.smack.packet; + +import org.jivesoftware.smack.provider.PrivacyProvider; +import org.jivesoftware.smack.test.SmackTestCase; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.StringReader; + +/** + * Test the PrivacyProvider class with valids privacy xmls + * + * @author Francisco Vives + */ +public class PrivacyProviderTest extends SmackTestCase { + + /** + * Constructor for PrivacyTest. + * @param arg0 + */ + public PrivacyProviderTest(String arg0) { + super(arg0); + } + + public static void main(String args[]) { + try { + new PrivacyProviderTest(null).testFull(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Check the parser with an xml with all kind of stanzas. + * To create the xml string based from an xml file, replace:\n with: "\n + " + */ + public void testFull() { + // Make the XML to test + String xml = "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " "; + + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); + + // check if it exist + assertNotNull(packet); + // assertEquals(xml, packet.getChildElementXML()); + + // check the default and active names + assertEquals("testFilter", packet.getActiveName()); + assertEquals("testSubscription", packet.getDefaultName()); + + // check the list + assertEquals(2, packet.getPrivacyList("testFilter").size()); + assertEquals(5, packet.getPrivacyList("testSubscription").size()); + assertEquals(4, packet.getPrivacyList("testJID").size()); + assertEquals(2, packet.getPrivacyList("testGroup").size()); + assertEquals(0, packet.getPrivacyList("testEmpty").size()); + + // check each privacy item + PrivacyItem item = packet.getItem("testGroup", 4); + assertEquals("Enemies", item.getValue()); + assertEquals(PrivacyItem.Type.group, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(true, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(false, item.isFilterEverything()); + + item = packet.getItem("testFilter", 1); + assertEquals("tybalt@example.com", item.getValue()); + assertEquals(PrivacyItem.Type.jid, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testFilter", 2); + assertEquals(null, item.getValue()); + assertEquals(null, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(true, item.isFilterMessage()); + assertEquals(true, item.isFilterIQ()); + assertEquals(true, item.isFilterPresence_in()); + assertEquals(true, item.isFilterPresence_out()); + assertEquals(false, item.isFilterEverything()); + + // TEST THE testSubscription LIST + item = packet.getItem("testSubscription", 10); + assertEquals("both", item.getValue()); + assertEquals(PrivacyItem.Type.subscription, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testSubscription", 11); + assertEquals("to", item.getValue()); + assertEquals(PrivacyItem.Type.subscription, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testSubscription", 12); + assertEquals("from", item.getValue()); + assertEquals(PrivacyItem.Type.subscription, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testSubscription", 5); + assertEquals("none", item.getValue()); + assertEquals(PrivacyItem.Type.subscription, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(true, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(false, item.isFilterEverything()); + + item = packet.getItem("testSubscription", 15); + assertEquals(null, item.getValue()); + assertEquals(null, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + // TEST THE testJID LIST + + item = packet.getItem("testJID", 6); + assertEquals("juliet@example.com", item.getValue()); + assertEquals(PrivacyItem.Type.jid, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testJID", 7); + assertEquals("benvolio@example.org/palm", item.getValue()); + assertEquals(PrivacyItem.Type.jid, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testJID", 42); + assertEquals(null, item.getValue()); + assertEquals(PrivacyItem.Type.jid, item.getType()); + assertEquals(true, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + item = packet.getItem("testJID", 666); + assertEquals(null, item.getValue()); + assertEquals(null, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + // TEST THE testGroup LIST + + item = packet.getItem("testGroup", 4); + assertEquals("Enemies", item.getValue()); + assertEquals(PrivacyItem.Type.group, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(true, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(false, item.isFilterEverything()); + + item = packet.getItem("testGroup", 666); + assertEquals(null, item.getValue()); + assertEquals(null, item.getType()); + assertEquals(false, item.isAllow()); + assertEquals(false, item.isFilterMessage()); + assertEquals(false, item.isFilterIQ()); + assertEquals(false, item.isFilterPresence_in()); + assertEquals(false, item.isFilterPresence_out()); + assertEquals(true, item.isFilterEverything()); + + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + + /** + * Check the parser with an xml with empty lists. It includes the active, + * default and special list. + * To create the xml string based from an xml file, replace:\n with: "\n + " + */ + public void testEmptyLists() { + // Make the XML to test + String xml = "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " "; + + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); + + assertNotNull(packet); + assertNotNull(packet.getChildElementXML()); + + assertEquals("public", packet.getDefaultName()); + assertEquals(null, packet.getActiveName()); + + assertEquals(0, packet.getPrivacyList("public").size()); + assertEquals(0, packet.getPrivacyList("private").size()); + assertEquals(0, packet.getPrivacyList("special").size()); + + assertEquals(true, packet.isDeclineActiveList()); + assertEquals(false, packet.isDeclineDefaultList()); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check the parser with an xml with empty lists. It includes the active, + * default and special list. + * To create the xml string based from an xml file, replace:\n with: "\n + " + */ + public void testDeclineLists() { + // Make the XML to test + String xml = "" + + " " + + " " + + " " + + " " + + " " + + " "; + + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + Privacy packet = (Privacy) (new PrivacyProvider()).parseIQ(parser); + + assertNotNull(packet); + + assertEquals(null, packet.getDefaultName()); + assertEquals(null, packet.getActiveName()); + + assertEquals(true, packet.isDeclineActiveList()); + assertEquals(true, packet.isDeclineDefaultList()); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + private XmlPullParser getParserFromXML(String xml) throws XmlPullParserException { + MXParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new StringReader(xml)); + return parser; + } + + protected int getMaxConnections() { + return 0; + } +} diff --git a/integration-test/org/jivesoftware/smack/packet/PrivacyTest.java b/integration-test/org/jivesoftware/smack/packet/PrivacyTest.java index 9cf1a2988..cd185f5ee 100644 --- a/integration-test/org/jivesoftware/smack/packet/PrivacyTest.java +++ b/integration-test/org/jivesoftware/smack/packet/PrivacyTest.java @@ -12,510 +12,510 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.packet; - -import org.jivesoftware.smack.PrivacyList; -import org.jivesoftware.smack.PrivacyListListener; -import org.jivesoftware.smack.PrivacyListManager; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.PrivacyItem.PrivacyRule; -import org.jivesoftware.smack.test.SmackTestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class PrivacyTest extends SmackTestCase { - - public PrivacyTest(String arg0) { - super(arg0); - } - - - /** - * Check when a client set a new active list. - */ - public void testCreateActiveList() { - try { - String listName = "testCreateActiveList"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - // Add the list that will be set as the active - ArrayList items = new ArrayList(); - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); - item.setValue(getConnection(0).getUser()); - items.add(item); - privacyManager.createPrivacyList(listName, items); - - Thread.sleep(500); - - // Set the active list - privacyManager.setActiveListName(listName); - - Thread.sleep(500); - - // Assert the list composition. - assertEquals(listName, privacyManager.getActiveList().toString()); - List privacyItems = privacyManager.getPrivacyList(listName).getItems(); - assertEquals(1, privacyItems.size()); - - // Assert the privacy item composition - PrivacyItem receivedItem = privacyItems.get(0); - assertEquals(1, receivedItem.getOrder()); - assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); - assertEquals(true, receivedItem.isAllow()); - - privacyManager.deletePrivacyList(listName); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client set more than one list. - */ - public void testCreateTwoLists() { - try { - String listName1 = "1testCreateTwoLists"; - String listName2 = "2testCreateTwoLists"; - String groupName = "testCreateTwoListsGroup"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - // Add a list - ArrayList items = new ArrayList(); - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); - item.setValue(getConnection(0).getUser()); - items.add(item); - privacyManager.createPrivacyList(listName1, items); - - Thread.sleep(500); - - // Add the another list - ArrayList itemsList2 = new ArrayList(); - item = new PrivacyItem(PrivacyItem.Type.group.name(), false, 2); - item.setValue(groupName); - item.setFilterMessage(true); - itemsList2.add(item); - privacyManager.createPrivacyList(listName2, itemsList2); - - Thread.sleep(500); - - // Assert the list composition. - PrivacyList[] privacyLists = privacyManager.getPrivacyLists(); - PrivacyList receivedList1 = null; - PrivacyList receivedList2 = null; - for (PrivacyList privacyList : privacyLists) { - if (listName1.equals(privacyList.toString())) { - receivedList1 = privacyList; - } - if (listName2.equals(privacyList.toString())) { - receivedList2 = privacyList; - } - } - - - PrivacyItem receivedItem; - // Assert on the list 1 - assertNotNull(receivedList1); - assertEquals(1, receivedList1.getItems().size()); - receivedItem = receivedList1.getItems().get(0); - assertEquals(1, receivedItem.getOrder()); - assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); - assertEquals(true, receivedItem.isAllow()); - assertEquals(getConnection(0).getUser(), receivedItem.getValue()); - - // Assert on the list 2 - assertNotNull(receivedList2); - assertEquals(1, receivedList2.getItems().size()); - receivedItem = receivedList2.getItems().get(0); - assertEquals(2, receivedItem.getOrder()); - assertEquals(PrivacyItem.Type.group, receivedItem.getType()); - assertEquals(groupName, receivedItem.getValue()); - assertEquals(false, receivedItem.isAllow()); - assertEquals(groupName, receivedItem.getValue()); - assertEquals(false, receivedItem.isFilterEverything()); - assertEquals(true, receivedItem.isFilterMessage()); - assertEquals(false, receivedItem.isFilterPresence_in()); - assertEquals(false, receivedItem.isFilterPresence_out()); - - privacyManager.deletePrivacyList(listName1); - privacyManager.deletePrivacyList(listName2); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client set a new list and then update its content. - */ - public void testCreateAndUpdateList() { - try { - String listName = "testCreateAndUpdateList"; - String user = "tybalt@example.com"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - // Add the list that will be set as the active - ArrayList items = new ArrayList(); - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); - item.setValue(getConnection(0).getUser()); - items.add(item); - privacyManager.createPrivacyList(listName, items); - - Thread.sleep(500); - - // Remove the existing item and add a new one. - items.remove(item); - item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, 2); - item.setValue(user); - item.setFilterPresence_out(true); - item.setFilterPresence_in(true); - item.setFilterMessage(true); - items.add(item); - - // Update the list on server - privacyManager.updatePrivacyList(listName, items); - - Thread.sleep(500); - - // Assert the list composition. - PrivacyList list = privacyManager.getPrivacyList(listName); - assertEquals(1, list.getItems().size()); - - // Assert the privacy item composition - PrivacyItem receivedItem = list.getItems().get(0); - assertEquals(2, receivedItem.getOrder()); - assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); - assertEquals(false, receivedItem.isAllow()); - assertEquals(user, receivedItem.getValue()); - assertEquals(false, receivedItem.isFilterEverything()); - assertEquals(true, receivedItem.isFilterMessage()); - assertEquals(true, receivedItem.isFilterPresence_in()); - assertEquals(true, receivedItem.isFilterPresence_out()); - assertEquals(true, client.wasModified()); - - privacyManager.deletePrivacyList(listName); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client denies the use of a default list. - */ - public void testDenyDefaultList() { - try { - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - privacyManager.declineDefaultList(); - - Thread.sleep(500); - - try { - // The list should not exist and an error will be raised - privacyManager.getDefaultList(); - } catch (XMPPException xmppException) { - assertEquals(404, xmppException.getXMPPError().getCode()); - } - - assertEquals(null, null); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client denies the use of the active list. - */ - public void testDenyActiveList() { - try { - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - privacyManager.declineActiveList(); - - Thread.sleep(500); - - try { - // The list should not exist and an error will be raised - privacyManager.getActiveList(); - } catch (XMPPException xmppException) { - assertEquals(404, xmppException.getXMPPError().getCode()); - } - - assertEquals(null, null); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client set a new default list. - */ - public void testCreateDefaultList() { - try { - String listName = "testCreateDefaultList"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - // Add the list that will be set as the Default - ArrayList items = new ArrayList(); - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); - item.setValue(getConnection(0).getUser()); - items.add(item); - privacyManager.createPrivacyList(listName, items); - - Thread.sleep(500); - - // Set the Default list - privacyManager.setDefaultListName(listName); - - Thread.sleep(500); - - // Assert the list composition. - assertEquals(listName, privacyManager.getDefaultList().toString()); - List privacyItems = privacyManager.getPrivacyList(listName).getItems(); - assertEquals(1, privacyItems.size()); - - // Assert the privacy item composition - PrivacyItem receivedItem = privacyItems.get(0); - assertEquals(1, receivedItem.getOrder()); - assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); - assertEquals(true, receivedItem.isAllow()); - - privacyManager.deletePrivacyList(listName); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check when a client add a new list and then remove it. - */ - public void testRemoveList() { - try { - String listName = "testRemoveList"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - // Add the list that will be set as the Default - ArrayList items = new ArrayList(); - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); - item.setValue(getConnection(0).getUser()); - items.add(item); - privacyManager.createPrivacyList(listName, items); - - Thread.sleep(500); - - // Set the Default list - privacyManager.setDefaultListName(listName); - - Thread.sleep(500); - - privacyManager.deletePrivacyList(listName); - - Thread.sleep(500); - - try { - // The list should not exist and an error will be raised - privacyManager.getPrivacyList(listName); - } catch (XMPPException xmppException) { - assertEquals(404, xmppException.getXMPPError().getCode()); - } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check different types of privacy items. - */ - public void testPrivacyItems() { - try { - String listName = "testPrivacyItems"; - String user = "tybalt@example.com"; - String groupName = "enemies"; - - PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); - PrivacyClient client = new PrivacyClient(privacyManager); - privacyManager.addListener(client); - - PrivacyItem[] originalPrivacyItems = new PrivacyItem[12]; - int i=0; - - // Items to test JID - PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, i); - item.setValue(i + "_" + user); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, i); - item.setValue(i + "_" + user); - originalPrivacyItems[i] = item; - i = i + 1; - - // Items to test suscription - item = new PrivacyItem(PrivacyItem.Type.subscription.name(), true, i); - item.setValue(PrivacyRule.SUBSCRIPTION_BOTH); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); - item.setValue(PrivacyRule.SUBSCRIPTION_FROM); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.subscription.name(), true, i); - item.setValue(PrivacyRule.SUBSCRIPTION_TO); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); - item.setValue(PrivacyRule.SUBSCRIPTION_NONE); - originalPrivacyItems[i] = item; - i = i + 1; - - // Items to test Group - item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); - item.setValue(groupName); - originalPrivacyItems[i] = item; - i = i + 1; - - // Items to test messages - item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); - item.setValue(groupName); - item.setFilterMessage(true); - originalPrivacyItems[i] = item; - i = i + 1; - - // Items to test presence notifications - item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); - item.setValue(groupName); - item.setFilterMessage(true); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(null, false, i); - item.setFilterPresence_in(true); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); - item.setValue(PrivacyRule.SUBSCRIPTION_TO); - item.setFilterPresence_out(true); - originalPrivacyItems[i] = item; - i = i + 1; - - item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, i); - item.setValue(i + "_" + user); - item.setFilterPresence_out(true); - item.setFilterPresence_in(true); - item.setFilterMessage(true); - originalPrivacyItems[i] = item; - - // Set the new privacy list - privacyManager.createPrivacyList(listName, Arrays.asList(originalPrivacyItems)); - - Thread.sleep(500); - - // Assert the server list composition. - List privacyItems = privacyManager.getPrivacyList(listName).getItems(); - assertEquals(originalPrivacyItems.length, privacyItems.size()); - - // Assert the local and server privacy item composition - PrivacyItem originalItem; - PrivacyItem receivedItem; - int index; - for (int j = 0; j < originalPrivacyItems.length; j++) { - // Look for the same server and original items - receivedItem = privacyItems.get(j); - index = 0; - while ((index < originalPrivacyItems.length) - && (originalPrivacyItems[index].getOrder() != receivedItem.getOrder())) { - index++; - } - originalItem = originalPrivacyItems[index]; - - // Assert the items - assertEquals(originalItem.getOrder(), receivedItem.getOrder()); - assertEquals(originalItem.getType(), receivedItem.getType()); - assertEquals(originalItem.isAllow(), receivedItem.isAllow()); - assertEquals(originalItem.getValue(), receivedItem.getValue()); - assertEquals(originalItem.isFilterEverything(), receivedItem.isFilterEverything()); - assertEquals(originalItem.isFilterIQ(), receivedItem.isFilterIQ()); - assertEquals(originalItem.isFilterMessage(), receivedItem.isFilterMessage()); - assertEquals(originalItem.isFilterPresence_in(), receivedItem.isFilterPresence_in()); - assertEquals(originalItem.isFilterPresence_out(), receivedItem.isFilterPresence_out()); - } - - privacyManager.deletePrivacyList(listName); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - protected int getMaxConnections() { - return 1; - } - - /** - * This class supports automated tests about privacy communication from the - * server to the client. - * - * @author Francisco Vives - */ - - public class PrivacyClient implements PrivacyListListener { - /** - * holds if the receiver list was modified - */ - private boolean wasModified = false; - - /** - * holds a privacy to hold server requests Clients should not use Privacy - * class since it is private for the smack framework. - */ - private Privacy privacy = new Privacy(); - - public PrivacyClient(PrivacyListManager manager) { - super(); - } - - public void setPrivacyList(String listName, List listItem) { - privacy.setPrivacyList(listName, listItem); - } - - public void updatedPrivacyList(String listName) { - this.wasModified = true; - } - - public boolean wasModified() { - return this.wasModified; - } - } -} - - +package org.jivesoftware.smack.packet; + +import org.jivesoftware.smack.PrivacyList; +import org.jivesoftware.smack.PrivacyListListener; +import org.jivesoftware.smack.PrivacyListManager; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.PrivacyItem.PrivacyRule; +import org.jivesoftware.smack.test.SmackTestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PrivacyTest extends SmackTestCase { + + public PrivacyTest(String arg0) { + super(arg0); + } + + + /** + * Check when a client set a new active list. + */ + public void testCreateActiveList() { + try { + String listName = "testCreateActiveList"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + // Add the list that will be set as the active + ArrayList items = new ArrayList(); + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); + item.setValue(getConnection(0).getUser()); + items.add(item); + privacyManager.createPrivacyList(listName, items); + + Thread.sleep(500); + + // Set the active list + privacyManager.setActiveListName(listName); + + Thread.sleep(500); + + // Assert the list composition. + assertEquals(listName, privacyManager.getActiveList().toString()); + List privacyItems = privacyManager.getPrivacyList(listName).getItems(); + assertEquals(1, privacyItems.size()); + + // Assert the privacy item composition + PrivacyItem receivedItem = privacyItems.get(0); + assertEquals(1, receivedItem.getOrder()); + assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); + assertEquals(true, receivedItem.isAllow()); + + privacyManager.deletePrivacyList(listName); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client set more than one list. + */ + public void testCreateTwoLists() { + try { + String listName1 = "1testCreateTwoLists"; + String listName2 = "2testCreateTwoLists"; + String groupName = "testCreateTwoListsGroup"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + // Add a list + ArrayList items = new ArrayList(); + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); + item.setValue(getConnection(0).getUser()); + items.add(item); + privacyManager.createPrivacyList(listName1, items); + + Thread.sleep(500); + + // Add the another list + ArrayList itemsList2 = new ArrayList(); + item = new PrivacyItem(PrivacyItem.Type.group.name(), false, 2); + item.setValue(groupName); + item.setFilterMessage(true); + itemsList2.add(item); + privacyManager.createPrivacyList(listName2, itemsList2); + + Thread.sleep(500); + + // Assert the list composition. + PrivacyList[] privacyLists = privacyManager.getPrivacyLists(); + PrivacyList receivedList1 = null; + PrivacyList receivedList2 = null; + for (PrivacyList privacyList : privacyLists) { + if (listName1.equals(privacyList.toString())) { + receivedList1 = privacyList; + } + if (listName2.equals(privacyList.toString())) { + receivedList2 = privacyList; + } + } + + + PrivacyItem receivedItem; + // Assert on the list 1 + assertNotNull(receivedList1); + assertEquals(1, receivedList1.getItems().size()); + receivedItem = receivedList1.getItems().get(0); + assertEquals(1, receivedItem.getOrder()); + assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); + assertEquals(true, receivedItem.isAllow()); + assertEquals(getConnection(0).getUser(), receivedItem.getValue()); + + // Assert on the list 2 + assertNotNull(receivedList2); + assertEquals(1, receivedList2.getItems().size()); + receivedItem = receivedList2.getItems().get(0); + assertEquals(2, receivedItem.getOrder()); + assertEquals(PrivacyItem.Type.group, receivedItem.getType()); + assertEquals(groupName, receivedItem.getValue()); + assertEquals(false, receivedItem.isAllow()); + assertEquals(groupName, receivedItem.getValue()); + assertEquals(false, receivedItem.isFilterEverything()); + assertEquals(true, receivedItem.isFilterMessage()); + assertEquals(false, receivedItem.isFilterPresence_in()); + assertEquals(false, receivedItem.isFilterPresence_out()); + + privacyManager.deletePrivacyList(listName1); + privacyManager.deletePrivacyList(listName2); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client set a new list and then update its content. + */ + public void testCreateAndUpdateList() { + try { + String listName = "testCreateAndUpdateList"; + String user = "tybalt@example.com"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + // Add the list that will be set as the active + ArrayList items = new ArrayList(); + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); + item.setValue(getConnection(0).getUser()); + items.add(item); + privacyManager.createPrivacyList(listName, items); + + Thread.sleep(500); + + // Remove the existing item and add a new one. + items.remove(item); + item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, 2); + item.setValue(user); + item.setFilterPresence_out(true); + item.setFilterPresence_in(true); + item.setFilterMessage(true); + items.add(item); + + // Update the list on server + privacyManager.updatePrivacyList(listName, items); + + Thread.sleep(500); + + // Assert the list composition. + PrivacyList list = privacyManager.getPrivacyList(listName); + assertEquals(1, list.getItems().size()); + + // Assert the privacy item composition + PrivacyItem receivedItem = list.getItems().get(0); + assertEquals(2, receivedItem.getOrder()); + assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); + assertEquals(false, receivedItem.isAllow()); + assertEquals(user, receivedItem.getValue()); + assertEquals(false, receivedItem.isFilterEverything()); + assertEquals(true, receivedItem.isFilterMessage()); + assertEquals(true, receivedItem.isFilterPresence_in()); + assertEquals(true, receivedItem.isFilterPresence_out()); + assertEquals(true, client.wasModified()); + + privacyManager.deletePrivacyList(listName); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client denies the use of a default list. + */ + public void testDenyDefaultList() { + try { + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + privacyManager.declineDefaultList(); + + Thread.sleep(500); + + try { + // The list should not exist and an error will be raised + privacyManager.getDefaultList(); + } catch (XMPPException xmppException) { + assertEquals(404, xmppException.getXMPPError().getCode()); + } + + assertEquals(null, null); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client denies the use of the active list. + */ + public void testDenyActiveList() { + try { + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + privacyManager.declineActiveList(); + + Thread.sleep(500); + + try { + // The list should not exist and an error will be raised + privacyManager.getActiveList(); + } catch (XMPPException xmppException) { + assertEquals(404, xmppException.getXMPPError().getCode()); + } + + assertEquals(null, null); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client set a new default list. + */ + public void testCreateDefaultList() { + try { + String listName = "testCreateDefaultList"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + // Add the list that will be set as the Default + ArrayList items = new ArrayList(); + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); + item.setValue(getConnection(0).getUser()); + items.add(item); + privacyManager.createPrivacyList(listName, items); + + Thread.sleep(500); + + // Set the Default list + privacyManager.setDefaultListName(listName); + + Thread.sleep(500); + + // Assert the list composition. + assertEquals(listName, privacyManager.getDefaultList().toString()); + List privacyItems = privacyManager.getPrivacyList(listName).getItems(); + assertEquals(1, privacyItems.size()); + + // Assert the privacy item composition + PrivacyItem receivedItem = privacyItems.get(0); + assertEquals(1, receivedItem.getOrder()); + assertEquals(PrivacyItem.Type.jid, receivedItem.getType()); + assertEquals(true, receivedItem.isAllow()); + + privacyManager.deletePrivacyList(listName); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check when a client add a new list and then remove it. + */ + public void testRemoveList() { + try { + String listName = "testRemoveList"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + // Add the list that will be set as the Default + ArrayList items = new ArrayList(); + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, 1); + item.setValue(getConnection(0).getUser()); + items.add(item); + privacyManager.createPrivacyList(listName, items); + + Thread.sleep(500); + + // Set the Default list + privacyManager.setDefaultListName(listName); + + Thread.sleep(500); + + privacyManager.deletePrivacyList(listName); + + Thread.sleep(500); + + try { + // The list should not exist and an error will be raised + privacyManager.getPrivacyList(listName); + } catch (XMPPException xmppException) { + assertEquals(404, xmppException.getXMPPError().getCode()); + } + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check different types of privacy items. + */ + public void testPrivacyItems() { + try { + String listName = "testPrivacyItems"; + String user = "tybalt@example.com"; + String groupName = "enemies"; + + PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(getConnection(0)); + PrivacyClient client = new PrivacyClient(privacyManager); + privacyManager.addListener(client); + + PrivacyItem[] originalPrivacyItems = new PrivacyItem[12]; + int i=0; + + // Items to test JID + PrivacyItem item = new PrivacyItem(PrivacyItem.Type.jid.name(), true, i); + item.setValue(i + "_" + user); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, i); + item.setValue(i + "_" + user); + originalPrivacyItems[i] = item; + i = i + 1; + + // Items to test suscription + item = new PrivacyItem(PrivacyItem.Type.subscription.name(), true, i); + item.setValue(PrivacyRule.SUBSCRIPTION_BOTH); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); + item.setValue(PrivacyRule.SUBSCRIPTION_FROM); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.subscription.name(), true, i); + item.setValue(PrivacyRule.SUBSCRIPTION_TO); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); + item.setValue(PrivacyRule.SUBSCRIPTION_NONE); + originalPrivacyItems[i] = item; + i = i + 1; + + // Items to test Group + item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); + item.setValue(groupName); + originalPrivacyItems[i] = item; + i = i + 1; + + // Items to test messages + item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); + item.setValue(groupName); + item.setFilterMessage(true); + originalPrivacyItems[i] = item; + i = i + 1; + + // Items to test presence notifications + item = new PrivacyItem(PrivacyItem.Type.group.name(), false, i); + item.setValue(groupName); + item.setFilterMessage(true); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(null, false, i); + item.setFilterPresence_in(true); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.subscription.name(), false, i); + item.setValue(PrivacyRule.SUBSCRIPTION_TO); + item.setFilterPresence_out(true); + originalPrivacyItems[i] = item; + i = i + 1; + + item = new PrivacyItem(PrivacyItem.Type.jid.name(), false, i); + item.setValue(i + "_" + user); + item.setFilterPresence_out(true); + item.setFilterPresence_in(true); + item.setFilterMessage(true); + originalPrivacyItems[i] = item; + + // Set the new privacy list + privacyManager.createPrivacyList(listName, Arrays.asList(originalPrivacyItems)); + + Thread.sleep(500); + + // Assert the server list composition. + List privacyItems = privacyManager.getPrivacyList(listName).getItems(); + assertEquals(originalPrivacyItems.length, privacyItems.size()); + + // Assert the local and server privacy item composition + PrivacyItem originalItem; + PrivacyItem receivedItem; + int index; + for (int j = 0; j < originalPrivacyItems.length; j++) { + // Look for the same server and original items + receivedItem = privacyItems.get(j); + index = 0; + while ((index < originalPrivacyItems.length) + && (originalPrivacyItems[index].getOrder() != receivedItem.getOrder())) { + index++; + } + originalItem = originalPrivacyItems[index]; + + // Assert the items + assertEquals(originalItem.getOrder(), receivedItem.getOrder()); + assertEquals(originalItem.getType(), receivedItem.getType()); + assertEquals(originalItem.isAllow(), receivedItem.isAllow()); + assertEquals(originalItem.getValue(), receivedItem.getValue()); + assertEquals(originalItem.isFilterEverything(), receivedItem.isFilterEverything()); + assertEquals(originalItem.isFilterIQ(), receivedItem.isFilterIQ()); + assertEquals(originalItem.isFilterMessage(), receivedItem.isFilterMessage()); + assertEquals(originalItem.isFilterPresence_in(), receivedItem.isFilterPresence_in()); + assertEquals(originalItem.isFilterPresence_out(), receivedItem.isFilterPresence_out()); + } + + privacyManager.deletePrivacyList(listName); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + protected int getMaxConnections() { + return 1; + } + + /** + * This class supports automated tests about privacy communication from the + * server to the client. + * + * @author Francisco Vives + */ + + public class PrivacyClient implements PrivacyListListener { + /** + * holds if the receiver list was modified + */ + private boolean wasModified = false; + + /** + * holds a privacy to hold server requests Clients should not use Privacy + * class since it is private for the smack framework. + */ + private Privacy privacy = new Privacy(); + + public PrivacyClient(PrivacyListManager manager) { + super(); + } + + public void setPrivacyList(String listName, List listItem) { + privacy.setPrivacyList(listName, listItem); + } + + public void updatedPrivacyList(String listName) { + this.wasModified = true; + } + + public boolean wasModified() { + return this.wasModified; + } + } +} + + diff --git a/integration-test/org/jivesoftware/smack/util/XMPPErrorTest.java b/integration-test/org/jivesoftware/smack/util/XMPPErrorTest.java index acec8be82..000741d55 100644 --- a/integration-test/org/jivesoftware/smack/util/XMPPErrorTest.java +++ b/integration-test/org/jivesoftware/smack/util/XMPPErrorTest.java @@ -14,190 +14,190 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.util; - -import java.io.StringReader; - -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.test.SmackTestCase; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -public class XMPPErrorTest extends SmackTestCase { - - public XMPPErrorTest(String arg0) { - super(arg0); - } - - /** - * Check the creation of a new xmppError locally. - */ - public void testLocalErrorCreation() { - XMPPError error = new XMPPError(XMPPError.Condition.item_not_found); - error.toXML(); - - assertEquals(error.getCondition(), "item-not-found"); - assertEquals(error.getCode(), 404); - assertEquals(error.getType(), XMPPError.Type.CANCEL); - assertNull(error.getMessage()); - } - - /** - * Check the creation of a new xmppError locally. - */ - public void testLocalErrorWithCommentCreation() { - String message = "Error Message"; - XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, message); - error.toXML(); - - assertEquals(error.getCondition(), "item-not-found"); - assertEquals(error.getCode(), 404); - assertEquals(error.getType(), XMPPError.Type.CANCEL); - assertEquals(error.getMessage(), message); - } - - /** - * Check the creation of a new xmppError locally where there is not a default defined. - */ - public void testUserDefinedErrorWithCommentCreation() { - String message = "Error Message"; - XMPPError error = new XMPPError(new XMPPError.Condition("my_own_error"), message); - error.toXML(); - - assertEquals(error.getCondition(), "my_own_error"); - assertEquals(error.getCode(), 0); - assertNull(error.getType()); - assertEquals(error.getMessage(), message); - } - - /** - * Check the parser with an xml with the 404 error. - */ - public void test404() { - // Make the XML to test - String xml = "" + - "" + - ""; - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - XMPPError packet = parseError(parser); - - assertNotNull(packet); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check the parser with an xml with the 404 error. - */ - public void testCancel() { - // Make the XML to test - String xml = "" + - "" + - ""; - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - XMPPError error = parseError(parser); - - assertNotNull(error); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - public void testMessageAndApplicationDefinedError() { - String xml = "" + - "" + - "" + - "Some special application diagnostic information..." + - "" + - "" + - ""; - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - XMPPError error = parseError(parser); - - String sendingXML = error.toXML(); - - assertNotNull(error); - assertNotNull(sendingXML); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - /** - * Check the parser with an xml with the 404 error. - */ - public void testCancelWithMessage() { - // Make the XML to test - String xml = "" + - "" + - "" + - "Some special application diagnostic information!" + - "" + - ""; - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - XMPPError error = parseError(parser); - - assertNotNull(error); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - /** - * Check the parser with an xml with the 404 error. - */ - public void testCancelWithMessageAndApplicationError() { - // Make the XML to test - String xml = "" + - "" + - "" + - "Some special application diagnostic information!" + - "" + - "" + - ""; - try { - // Create the xml parser - XmlPullParser parser = getParserFromXML(xml); - // Create a packet from the xml - XMPPError error = parseError(parser); - - assertNotNull(error); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - private XMPPError parseError(XmlPullParser parser) throws Exception { - parser.next(); - return PacketParserUtils.parseError(parser); - } - - private XmlPullParser getParserFromXML(String xml) throws XmlPullParserException { - MXParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new StringReader(xml)); - return parser; - } - - protected int getMaxConnections() { - return 0; - } -} +package org.jivesoftware.smack.util; + +import java.io.StringReader; + +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.test.SmackTestCase; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +public class XMPPErrorTest extends SmackTestCase { + + public XMPPErrorTest(String arg0) { + super(arg0); + } + + /** + * Check the creation of a new xmppError locally. + */ + public void testLocalErrorCreation() { + XMPPError error = new XMPPError(XMPPError.Condition.item_not_found); + error.toXML(); + + assertEquals(error.getCondition(), "item-not-found"); + assertEquals(error.getCode(), 404); + assertEquals(error.getType(), XMPPError.Type.CANCEL); + assertNull(error.getMessage()); + } + + /** + * Check the creation of a new xmppError locally. + */ + public void testLocalErrorWithCommentCreation() { + String message = "Error Message"; + XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, message); + error.toXML(); + + assertEquals(error.getCondition(), "item-not-found"); + assertEquals(error.getCode(), 404); + assertEquals(error.getType(), XMPPError.Type.CANCEL); + assertEquals(error.getMessage(), message); + } + + /** + * Check the creation of a new xmppError locally where there is not a default defined. + */ + public void testUserDefinedErrorWithCommentCreation() { + String message = "Error Message"; + XMPPError error = new XMPPError(new XMPPError.Condition("my_own_error"), message); + error.toXML(); + + assertEquals(error.getCondition(), "my_own_error"); + assertEquals(error.getCode(), 0); + assertNull(error.getType()); + assertEquals(error.getMessage(), message); + } + + /** + * Check the parser with an xml with the 404 error. + */ + public void test404() { + // Make the XML to test + String xml = "" + + "" + + ""; + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + XMPPError packet = parseError(parser); + + assertNotNull(packet); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check the parser with an xml with the 404 error. + */ + public void testCancel() { + // Make the XML to test + String xml = "" + + "" + + ""; + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + XMPPError error = parseError(parser); + + assertNotNull(error); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + public void testMessageAndApplicationDefinedError() { + String xml = "" + + "" + + "" + + "Some special application diagnostic information..." + + "" + + "" + + ""; + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + XMPPError error = parseError(parser); + + String sendingXML = error.toXML(); + + assertNotNull(error); + assertNotNull(sendingXML); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + /** + * Check the parser with an xml with the 404 error. + */ + public void testCancelWithMessage() { + // Make the XML to test + String xml = "" + + "" + + "" + + "Some special application diagnostic information!" + + "" + + ""; + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + XMPPError error = parseError(parser); + + assertNotNull(error); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /** + * Check the parser with an xml with the 404 error. + */ + public void testCancelWithMessageAndApplicationError() { + // Make the XML to test + String xml = "" + + "" + + "" + + "Some special application diagnostic information!" + + "" + + "" + + ""; + try { + // Create the xml parser + XmlPullParser parser = getParserFromXML(xml); + // Create a packet from the xml + XMPPError error = parseError(parser); + + assertNotNull(error); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + private XMPPError parseError(XmlPullParser parser) throws Exception { + parser.next(); + return PacketParserUtils.parseError(parser); + } + + private XmlPullParser getParserFromXML(String xml) throws XmlPullParserException { + MXParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new StringReader(xml)); + return parser; + } + + protected int getMaxConnections() { + return 0; + } +} diff --git a/integration-test/org/jivesoftware/smackx/CompressionTest.java b/integration-test/org/jivesoftware/smackx/CompressionTest.java index dc0069544..524742a91 100644 --- a/integration-test/org/jivesoftware/smackx/CompressionTest.java +++ b/integration-test/org/jivesoftware/smackx/CompressionTest.java @@ -113,4 +113,4 @@ public class CompressionTest extends SmackTestCase { // Close the setupConnection setupConnection.disconnect(); } -} \ No newline at end of file +} diff --git a/integration-test/org/jivesoftware/smackx/GroupChatInvitationTest.java b/integration-test/org/jivesoftware/smackx/GroupChatInvitationTest.java index 21e8683bb..991650801 100644 --- a/integration-test/org/jivesoftware/smackx/GroupChatInvitationTest.java +++ b/integration-test/org/jivesoftware/smackx/GroupChatInvitationTest.java @@ -81,4 +81,4 @@ public class GroupChatInvitationTest extends SmackTestCase { protected int getMaxConnections() { return 2; } -} \ No newline at end of file +} diff --git a/integration-test/org/jivesoftware/smackx/LastActivityManagerTest.java b/integration-test/org/jivesoftware/smackx/LastActivityManagerTest.java index 0e4dfa807..a6ae6d7ce 100644 --- a/integration-test/org/jivesoftware/smackx/LastActivityManagerTest.java +++ b/integration-test/org/jivesoftware/smackx/LastActivityManagerTest.java @@ -1,157 +1,157 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.packet.LastActivity; - -public class LastActivityManagerTest extends SmackTestCase { - - /** - * This is a test to check if a LastActivity request for idle time is - * answered and correct. - */ - public void testOnline() { - XMPPConnection conn0 = getConnection(0); - XMPPConnection conn1 = getConnection(1); - - // Send a message as the last activity action from connection 1 to - // connection 0 - conn1.sendPacket(new Message(getBareJID(0))); - - // Wait 1 seconds to have some idle time - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - fail("Thread sleep interrupted"); - } - - LastActivity lastActivity = null; - try { - lastActivity = LastActivityManager.getLastActivity(conn0, getFullJID(1)); - } catch (XMPPException e) { - e.printStackTrace(); - fail("An error occurred requesting the Last Activity"); - } - - // Asserts that the last activity packet was received - assertNotNull("No last activity packet", lastActivity); - // Asserts that there is at least a 1 second of idle time - assertTrue( - "The last activity idle time is less than expected: " + lastActivity.getIdleTime(), - lastActivity.getIdleTime() >= 1); - } - - /** - * This is a test to check if a denied LastActivity response is handled correctly. - */ - public void testOnlinePermisionDenied() { - XMPPConnection conn0 = getConnection(0); - XMPPConnection conn2 = getConnection(2); - - // Send a message as the last activity action from connection 2 to - // connection 0 - conn2.sendPacket(new Message(getBareJID(0))); - - // Wait 1 seconds to have some idle time - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - fail("Thread sleep interrupted"); - } - - try { - LastActivityManager.getLastActivity(conn0, getFullJID(2)); - fail("No error was received from the server. User was able to get info of other user not in his roster."); - } catch (XMPPException e) { - assertNotNull("No error was returned from the server", e.getXMPPError()); - assertEquals("Forbidden error was not returned from the server", 403, - e.getXMPPError().getCode()); - } - } - - /** - * This is a test to check if a LastActivity request for last logged out - * lapsed time is answered and correct - */ - public void testLastLoggedOut() { - XMPPConnection conn0 = getConnection(0); - - LastActivity lastActivity = null; - try { - lastActivity = LastActivityManager.getLastActivity(conn0, getBareJID(1)); - } catch (XMPPException e) { - e.printStackTrace(); - fail("An error occurred requesting the Last Activity"); - } - - assertNotNull("No last activity packet", lastActivity); - assertTrue("The last activity idle time should be 0 since the user is logged in: " + - lastActivity.getIdleTime(), lastActivity.getIdleTime() == 0); - } - - /** - * This is a test to check if a LastActivity request for server uptime - * is answered and correct - */ - public void testServerUptime() { - XMPPConnection conn0 = getConnection(0); - - LastActivity lastActivity = null; - try { - lastActivity = LastActivityManager.getLastActivity(conn0, getHost()); - } catch (XMPPException e) { - if (e.getXMPPError().getCode() == 403) { - //The test can not be done since the host do not allow this kind of request - return; - } - e.printStackTrace(); - fail("An error occurred requesting the Last Activity"); - } - - assertNotNull("No last activity packet", lastActivity); - assertTrue("The last activity idle time should be greater than 0 : " + - lastActivity.getIdleTime(), lastActivity.getIdleTime() > 0); - } - - public LastActivityManagerTest(String name) { - super(name); - } - - @Override - protected int getMaxConnections() { - return 3; - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - try { - getConnection(0).getRoster().createEntry(getBareJID(1), "User1", null); - Thread.sleep(300); - } catch (Exception e) { - fail(e.getMessage()); - } - } - -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.packet.LastActivity; + +public class LastActivityManagerTest extends SmackTestCase { + + /** + * This is a test to check if a LastActivity request for idle time is + * answered and correct. + */ + public void testOnline() { + XMPPConnection conn0 = getConnection(0); + XMPPConnection conn1 = getConnection(1); + + // Send a message as the last activity action from connection 1 to + // connection 0 + conn1.sendPacket(new Message(getBareJID(0))); + + // Wait 1 seconds to have some idle time + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + fail("Thread sleep interrupted"); + } + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getFullJID(1)); + } catch (XMPPException e) { + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + // Asserts that the last activity packet was received + assertNotNull("No last activity packet", lastActivity); + // Asserts that there is at least a 1 second of idle time + assertTrue( + "The last activity idle time is less than expected: " + lastActivity.getIdleTime(), + lastActivity.getIdleTime() >= 1); + } + + /** + * This is a test to check if a denied LastActivity response is handled correctly. + */ + public void testOnlinePermisionDenied() { + XMPPConnection conn0 = getConnection(0); + XMPPConnection conn2 = getConnection(2); + + // Send a message as the last activity action from connection 2 to + // connection 0 + conn2.sendPacket(new Message(getBareJID(0))); + + // Wait 1 seconds to have some idle time + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + fail("Thread sleep interrupted"); + } + + try { + LastActivityManager.getLastActivity(conn0, getFullJID(2)); + fail("No error was received from the server. User was able to get info of other user not in his roster."); + } catch (XMPPException e) { + assertNotNull("No error was returned from the server", e.getXMPPError()); + assertEquals("Forbidden error was not returned from the server", 403, + e.getXMPPError().getCode()); + } + } + + /** + * This is a test to check if a LastActivity request for last logged out + * lapsed time is answered and correct + */ + public void testLastLoggedOut() { + XMPPConnection conn0 = getConnection(0); + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getBareJID(1)); + } catch (XMPPException e) { + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + assertNotNull("No last activity packet", lastActivity); + assertTrue("The last activity idle time should be 0 since the user is logged in: " + + lastActivity.getIdleTime(), lastActivity.getIdleTime() == 0); + } + + /** + * This is a test to check if a LastActivity request for server uptime + * is answered and correct + */ + public void testServerUptime() { + XMPPConnection conn0 = getConnection(0); + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getHost()); + } catch (XMPPException e) { + if (e.getXMPPError().getCode() == 403) { + //The test can not be done since the host do not allow this kind of request + return; + } + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + assertNotNull("No last activity packet", lastActivity); + assertTrue("The last activity idle time should be greater than 0 : " + + lastActivity.getIdleTime(), lastActivity.getIdleTime() > 0); + } + + public LastActivityManagerTest(String name) { + super(name); + } + + @Override + protected int getMaxConnections() { + return 3; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + getConnection(0).getRoster().createEntry(getBareJID(1), "User1", null); + Thread.sleep(300); + } catch (Exception e) { + fail(e.getMessage()); + } + } + +} diff --git a/integration-test/org/jivesoftware/smackx/SharedGroupsTest.java b/integration-test/org/jivesoftware/smackx/SharedGroupsTest.java index c4ed2b593..44d49fcc9 100644 --- a/integration-test/org/jivesoftware/smackx/SharedGroupsTest.java +++ b/integration-test/org/jivesoftware/smackx/SharedGroupsTest.java @@ -14,34 +14,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx; - -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smack.XMPPException; - -import java.util.List; - -/** - * Test cases for getting the shared groups of a user.

    - * - * Important note: This functionality is not part of the XMPP spec and it will only work - * with Wildfire. - * - * @author Gaston Dombiak - */ -public class SharedGroupsTest extends SmackTestCase { - - public SharedGroupsTest(String arg0) { - super(arg0); - } - - public void testGetUserSharedGroups() throws XMPPException { - List groups = SharedGroupManager.getSharedGroups(getConnection(0)); - - assertNotNull("User groups was null", groups); - } - - protected int getMaxConnections() { - return 1; - } -} +package org.jivesoftware.smackx; + +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smack.XMPPException; + +import java.util.List; + +/** + * Test cases for getting the shared groups of a user.

    + * + * Important note: This functionality is not part of the XMPP spec and it will only work + * with Wildfire. + * + * @author Gaston Dombiak + */ +public class SharedGroupsTest extends SmackTestCase { + + public SharedGroupsTest(String arg0) { + super(arg0); + } + + public void testGetUserSharedGroups() throws XMPPException { + List groups = SharedGroupManager.getSharedGroups(getConnection(0)); + + assertNotNull("User groups was null", groups); + } + + protected int getMaxConnections() { + return 1; + } +} diff --git a/integration-test/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamTest.java b/integration-test/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamTest.java index c64f73963..5bc6c058b 100644 --- a/integration-test/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamTest.java +++ b/integration-test/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamTest.java @@ -1,259 +1,259 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.InputStream; -import java.io.OutputStream; -import java.util.Random; -import java.util.concurrent.SynchronousQueue; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.PacketCollector; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.filter.PacketIDFilter; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener; -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.InBandBytestreamManager.StanzaType; -import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; - -/** - * Test for In-Band Bytestreams with real XMPP servers. - * - * @author Henning Staib - */ -public class InBandBytestreamTest extends SmackTestCase { - - /* the amount of data transmitted in each test */ - int dataSize = 1024000; - - public InBandBytestreamTest(String arg0) { - super(arg0); - } - - /** - * Target should respond with not-acceptable error if no listeners for incoming In-Band - * Bytestream requests are registered. - * - * @throws XMPPException should not happen - */ - public void testRespondWithErrorOnInBandBytestreamRequest() throws XMPPException { - Connection targetConnection = getConnection(0); - - Connection initiatorConnection = getConnection(1); - - Open open = new Open("sessionID", 1024); - open.setFrom(initiatorConnection.getUser()); - open.setTo(targetConnection.getUser()); - - PacketCollector collector = initiatorConnection.createPacketCollector(new PacketIDFilter( - open.getPacketID())); - initiatorConnection.sendPacket(open); - Packet result = collector.nextResult(); - - assertNotNull(result.getError()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), result.getError().getCondition()); - - } - - /** - * An In-Band Bytestream should be successfully established using IQ stanzas. - * - * @throws Exception should not happen - */ - public void testInBandBytestreamWithIQStanzas() throws Exception { - - Connection initiatorConnection = getConnection(0); - Connection targetConnection = getConnection(1); - - // test data - Random rand = new Random(); - final byte[] data = new byte[dataSize]; - rand.nextBytes(data); - final SynchronousQueue queue = new SynchronousQueue(); - - InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); - - InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { - - public void incomingBytestreamRequest(InBandBytestreamRequest request) { - InputStream inputStream; - try { - inputStream = request.accept().getInputStream(); - byte[] receivedData = new byte[dataSize]; - int totalRead = 0; - while (totalRead < dataSize) { - int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); - totalRead += read; - } - queue.put(receivedData); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); - - OutputStream outputStream = initiatorByteStreamManager.establishSession( - targetConnection.getUser()).getOutputStream(); - - // verify stream - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - } - - /** - * An In-Band Bytestream should be successfully established using message stanzas. - * - * @throws Exception should not happen - */ - public void testInBandBytestreamWithMessageStanzas() throws Exception { - - Connection initiatorConnection = getConnection(0); - Connection targetConnection = getConnection(1); - - // test data - Random rand = new Random(); - final byte[] data = new byte[dataSize]; - rand.nextBytes(data); - final SynchronousQueue queue = new SynchronousQueue(); - - InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); - - InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { - - public void incomingBytestreamRequest(InBandBytestreamRequest request) { - InputStream inputStream; - try { - inputStream = request.accept().getInputStream(); - byte[] receivedData = new byte[dataSize]; - int totalRead = 0; - while (totalRead < dataSize) { - int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); - totalRead += read; - } - queue.put(receivedData); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); - initiatorByteStreamManager.setStanza(StanzaType.MESSAGE); - - OutputStream outputStream = initiatorByteStreamManager.establishSession( - targetConnection.getUser()).getOutputStream(); - - // verify stream - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - } - - /** - * An In-Band Bytestream should be successfully established using IQ stanzas. The established - * session should transfer data bidirectional. - * - * @throws Exception should not happen - */ - public void testBiDirectionalInBandBytestream() throws Exception { - - Connection initiatorConnection = getConnection(0); - - Connection targetConnection = getConnection(1); - - // test data - Random rand = new Random(); - final byte[] data = new byte[dataSize]; - rand.nextBytes(data); - - final SynchronousQueue queue = new SynchronousQueue(); - - InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); - - InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { - - public void incomingBytestreamRequest(InBandBytestreamRequest request) { - try { - InBandBytestreamSession session = request.accept(); - OutputStream outputStream = session.getOutputStream(); - outputStream.write(data); - outputStream.flush(); - InputStream inputStream = session.getInputStream(); - byte[] receivedData = new byte[dataSize]; - int totalRead = 0; - while (totalRead < dataSize) { - int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); - totalRead += read; - } - queue.put(receivedData); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); - - InBandBytestreamSession session = initiatorByteStreamManager.establishSession(targetConnection.getUser()); - - // verify stream - byte[] receivedData = new byte[dataSize]; - InputStream inputStream = session.getInputStream(); - int totalRead = 0; - while (totalRead < dataSize) { - int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); - totalRead += read; - } - - assertEquals("sent data not equal to received data", data, receivedData); - - OutputStream outputStream = session.getOutputStream(); - - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - } - - @Override - protected int getMaxConnections() { - return 2; - } - -} +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.InputStream; +import java.io.OutputStream; +import java.util.Random; +import java.util.concurrent.SynchronousQueue; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener; +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.InBandBytestreamManager.StanzaType; +import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; + +/** + * Test for In-Band Bytestreams with real XMPP servers. + * + * @author Henning Staib + */ +public class InBandBytestreamTest extends SmackTestCase { + + /* the amount of data transmitted in each test */ + int dataSize = 1024000; + + public InBandBytestreamTest(String arg0) { + super(arg0); + } + + /** + * Target should respond with not-acceptable error if no listeners for incoming In-Band + * Bytestream requests are registered. + * + * @throws XMPPException should not happen + */ + public void testRespondWithErrorOnInBandBytestreamRequest() throws XMPPException { + Connection targetConnection = getConnection(0); + + Connection initiatorConnection = getConnection(1); + + Open open = new Open("sessionID", 1024); + open.setFrom(initiatorConnection.getUser()); + open.setTo(targetConnection.getUser()); + + PacketCollector collector = initiatorConnection.createPacketCollector(new PacketIDFilter( + open.getPacketID())); + initiatorConnection.sendPacket(open); + Packet result = collector.nextResult(); + + assertNotNull(result.getError()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), result.getError().getCondition()); + + } + + /** + * An In-Band Bytestream should be successfully established using IQ stanzas. + * + * @throws Exception should not happen + */ + public void testInBandBytestreamWithIQStanzas() throws Exception { + + Connection initiatorConnection = getConnection(0); + Connection targetConnection = getConnection(1); + + // test data + Random rand = new Random(); + final byte[] data = new byte[dataSize]; + rand.nextBytes(data); + final SynchronousQueue queue = new SynchronousQueue(); + + InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); + + InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { + + public void incomingBytestreamRequest(InBandBytestreamRequest request) { + InputStream inputStream; + try { + inputStream = request.accept().getInputStream(); + byte[] receivedData = new byte[dataSize]; + int totalRead = 0; + while (totalRead < dataSize) { + int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); + totalRead += read; + } + queue.put(receivedData); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); + + OutputStream outputStream = initiatorByteStreamManager.establishSession( + targetConnection.getUser()).getOutputStream(); + + // verify stream + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + } + + /** + * An In-Band Bytestream should be successfully established using message stanzas. + * + * @throws Exception should not happen + */ + public void testInBandBytestreamWithMessageStanzas() throws Exception { + + Connection initiatorConnection = getConnection(0); + Connection targetConnection = getConnection(1); + + // test data + Random rand = new Random(); + final byte[] data = new byte[dataSize]; + rand.nextBytes(data); + final SynchronousQueue queue = new SynchronousQueue(); + + InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); + + InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { + + public void incomingBytestreamRequest(InBandBytestreamRequest request) { + InputStream inputStream; + try { + inputStream = request.accept().getInputStream(); + byte[] receivedData = new byte[dataSize]; + int totalRead = 0; + while (totalRead < dataSize) { + int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); + totalRead += read; + } + queue.put(receivedData); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); + initiatorByteStreamManager.setStanza(StanzaType.MESSAGE); + + OutputStream outputStream = initiatorByteStreamManager.establishSession( + targetConnection.getUser()).getOutputStream(); + + // verify stream + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + } + + /** + * An In-Band Bytestream should be successfully established using IQ stanzas. The established + * session should transfer data bidirectional. + * + * @throws Exception should not happen + */ + public void testBiDirectionalInBandBytestream() throws Exception { + + Connection initiatorConnection = getConnection(0); + + Connection targetConnection = getConnection(1); + + // test data + Random rand = new Random(); + final byte[] data = new byte[dataSize]; + rand.nextBytes(data); + + final SynchronousQueue queue = new SynchronousQueue(); + + InBandBytestreamManager targetByteStreamManager = InBandBytestreamManager.getByteStreamManager(targetConnection); + + InBandBytestreamListener incomingByteStreamListener = new InBandBytestreamListener() { + + public void incomingBytestreamRequest(InBandBytestreamRequest request) { + try { + InBandBytestreamSession session = request.accept(); + OutputStream outputStream = session.getOutputStream(); + outputStream.write(data); + outputStream.flush(); + InputStream inputStream = session.getInputStream(); + byte[] receivedData = new byte[dataSize]; + int totalRead = 0; + while (totalRead < dataSize) { + int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); + totalRead += read; + } + queue.put(receivedData); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + InBandBytestreamManager initiatorByteStreamManager = InBandBytestreamManager.getByteStreamManager(initiatorConnection); + + InBandBytestreamSession session = initiatorByteStreamManager.establishSession(targetConnection.getUser()); + + // verify stream + byte[] receivedData = new byte[dataSize]; + InputStream inputStream = session.getInputStream(); + int totalRead = 0; + while (totalRead < dataSize) { + int read = inputStream.read(receivedData, totalRead, dataSize - totalRead); + totalRead += read; + } + + assertEquals("sent data not equal to received data", data, receivedData); + + OutputStream outputStream = session.getOutputStream(); + + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + } + + @Override + protected int getMaxConnections() { + return 2; + } + +} diff --git a/integration-test/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamTest.java b/integration-test/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamTest.java index f1ff2173b..c1c968e9e 100644 --- a/integration-test/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamTest.java +++ b/integration-test/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamTest.java @@ -1,337 +1,337 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.InputStream; -import java.io.OutputStream; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -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.Packet; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.ServiceDiscoveryManager; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener; -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.Socks5PacketUtils; -import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; -import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; - -/** - * Test for Socks5 bytestreams with real XMPP servers. - * - * @author Henning Staib - */ -public class Socks5ByteStreamTest extends SmackTestCase { - - /** - * Constructor - * - * @param arg0 - */ - public Socks5ByteStreamTest(String arg0) { - super(arg0); - } - - /** - * Socks5 feature should be added to the service discovery on Smack startup. - * - * @throws XMPPException should not happen - */ - public void testInitializationSocks5FeaturesAndListenerOnStartup() throws XMPPException { - Connection connection = getConnection(0); - - assertTrue(ServiceDiscoveryManager.getInstanceFor(connection).includesFeature( - Socks5BytestreamManager.NAMESPACE)); - - } - - /** - * Target should respond with not-acceptable error if no listeners for incoming Socks5 - * bytestream requests are registered. - * - * @throws XMPPException should not happen - */ - public void testRespondWithErrorOnSocks5BytestreamRequest() throws XMPPException { - Connection targetConnection = getConnection(0); - - Connection initiatorConnection = getConnection(1); - - Bytestream bytestreamInitiation = Socks5PacketUtils.createBytestreamInitiation( - initiatorConnection.getUser(), targetConnection.getUser(), "session_id"); - bytestreamInitiation.addStreamHost("proxy.localhost", "127.0.0.1", 7777); - - PacketCollector collector = initiatorConnection.createPacketCollector(new PacketIDFilter( - bytestreamInitiation.getPacketID())); - initiatorConnection.sendPacket(bytestreamInitiation); - Packet result = collector.nextResult(); - - assertNotNull(result.getError()); - assertEquals(XMPPError.Condition.no_acceptable.toString(), result.getError().getCondition()); - - } - - /** - * Socks5 bytestream should be successfully established using the local Socks5 proxy. - * - * @throws Exception should not happen - */ - public void testSocks5BytestreamWithLocalSocks5Proxy() throws Exception { - - // setup port for local socks5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - SmackConfiguration.setLocalSocks5ProxyPort(7778); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); - socks5Proxy.start(); - - assertTrue(socks5Proxy.isRunning()); - - Connection initiatorConnection = getConnection(0); - Connection targetConnection = getConnection(1); - - // test data - final byte[] data = new byte[] { 1, 2, 3 }; - final SynchronousQueue queue = new SynchronousQueue(); - - Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); - - Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { - - public void incomingBytestreamRequest(Socks5BytestreamRequest request) { - InputStream inputStream; - try { - Socks5BytestreamSession session = request.accept(); - inputStream = session.getInputStream(); - byte[] receivedData = new byte[3]; - inputStream.read(receivedData); - queue.put(receivedData); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); - - Socks5BytestreamSession session = initiatorByteStreamManager.establishSession( - targetConnection.getUser()); - OutputStream outputStream = session.getOutputStream(); - - assertTrue(session.isDirect()); - - // verify stream - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - // reset default configuration - SmackConfiguration.setLocalSocks5ProxyPort(7777); - - } - - /** - * Socks5 bytestream should be successfully established using a Socks5 proxy provided by the - * XMPP server. - *

    - * This test will fail if the XMPP server doesn't provide any Socks5 proxies or the Socks5 proxy - * only allows Socks5 bytestreams in the context of a file transfer (like Openfire in default - * configuration, see xmpp.proxy.transfer.required flag). - * - * @throws Exception if no Socks5 proxies found or proxy is unwilling to activate Socks5 - * bytestream - */ - public void testSocks5BytestreamWithRemoteSocks5Proxy() throws Exception { - - // disable local socks5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - Socks5Proxy.getSocks5Proxy().stop(); - - assertFalse(Socks5Proxy.getSocks5Proxy().isRunning()); - - Connection initiatorConnection = getConnection(0); - Connection targetConnection = getConnection(1); - - // test data - final byte[] data = new byte[] { 1, 2, 3 }; - final SynchronousQueue queue = new SynchronousQueue(); - - Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); - - Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { - - public void incomingBytestreamRequest(Socks5BytestreamRequest request) { - InputStream inputStream; - try { - Socks5BytestreamSession session = request.accept(); - inputStream = session.getInputStream(); - byte[] receivedData = new byte[3]; - inputStream.read(receivedData); - queue.put(receivedData); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); - - Socks5BytestreamSession session = initiatorByteStreamManager.establishSession( - targetConnection.getUser()); - OutputStream outputStream = session.getOutputStream(); - - assertTrue(session.isMediated()); - - // verify stream - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - // reset default configuration - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - Socks5Proxy.getSocks5Proxy().start(); - - } - - /** - * Socks5 bytestream should be successfully established using a Socks5 proxy provided by the - * XMPP server. The established connection should transfer data bidirectional if the Socks5 - * proxy supports it. - *

    - * Support for bidirectional Socks5 bytestream: - *

      - *
    • Openfire (3.6.4 and below) - no
    • - *
    • ejabberd (2.0.5 and higher) - yes
    • - *
    - *

    - * This test will fail if the XMPP server doesn't provide any Socks5 proxies or the Socks5 proxy - * only allows Socks5 bytestreams in the context of a file transfer (like Openfire in default - * configuration, see xmpp.proxy.transfer.required flag). - * - * @throws Exception if no Socks5 proxies found or proxy is unwilling to activate Socks5 - * bytestream - */ - public void testBiDirectionalSocks5BytestreamWithRemoteSocks5Proxy() throws Exception { - - Connection initiatorConnection = getConnection(0); - - // disable local socks5 proxy - SmackConfiguration.setLocalSocks5ProxyEnabled(false); - Socks5Proxy.getSocks5Proxy().stop(); - - assertFalse(Socks5Proxy.getSocks5Proxy().isRunning()); - - Connection targetConnection = getConnection(1); - - // test data - final byte[] data = new byte[] { 1, 2, 3 }; - final SynchronousQueue queue = new SynchronousQueue(); - - Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); - - Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { - - public void incomingBytestreamRequest(Socks5BytestreamRequest request) { - try { - Socks5BytestreamSession session = request.accept(); - OutputStream outputStream = session.getOutputStream(); - outputStream.write(data); - outputStream.flush(); - InputStream inputStream = session.getInputStream(); - byte[] receivedData = new byte[3]; - inputStream.read(receivedData); - queue.put(receivedData); - session.close(); - } - catch (Exception e) { - fail(e.getMessage()); - } - } - - }; - targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); - - Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); - - Socks5BytestreamSession session = initiatorByteStreamManager.establishSession(targetConnection.getUser()); - - assertTrue(session.isMediated()); - - // verify stream - final byte[] receivedData = new byte[3]; - final InputStream inputStream = session.getInputStream(); - - FutureTask futureTask = new FutureTask(new Callable() { - - public Integer call() throws Exception { - return inputStream.read(receivedData); - } - }); - Thread executor = new Thread(futureTask); - executor.start(); - - try { - futureTask.get(2000, TimeUnit.MILLISECONDS); - } - catch (TimeoutException e) { - // reset default configuration - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - Socks5Proxy.getSocks5Proxy().start(); - - fail("Couldn't send data from target to inititator"); - } - - assertEquals("sent data not equal to received data", data, receivedData); - - OutputStream outputStream = session.getOutputStream(); - - outputStream.write(data); - outputStream.flush(); - outputStream.close(); - - assertEquals("received data not equal to sent data", data, queue.take()); - - session.close(); - - // reset default configuration - SmackConfiguration.setLocalSocks5ProxyEnabled(true); - Socks5Proxy.getSocks5Proxy().start(); - - } - - @Override - protected int getMaxConnections() { - return 2; - } - -} +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +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.Packet; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamListener; +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.Socks5PacketUtils; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; + +/** + * Test for Socks5 bytestreams with real XMPP servers. + * + * @author Henning Staib + */ +public class Socks5ByteStreamTest extends SmackTestCase { + + /** + * Constructor + * + * @param arg0 + */ + public Socks5ByteStreamTest(String arg0) { + super(arg0); + } + + /** + * Socks5 feature should be added to the service discovery on Smack startup. + * + * @throws XMPPException should not happen + */ + public void testInitializationSocks5FeaturesAndListenerOnStartup() throws XMPPException { + Connection connection = getConnection(0); + + assertTrue(ServiceDiscoveryManager.getInstanceFor(connection).includesFeature( + Socks5BytestreamManager.NAMESPACE)); + + } + + /** + * Target should respond with not-acceptable error if no listeners for incoming Socks5 + * bytestream requests are registered. + * + * @throws XMPPException should not happen + */ + public void testRespondWithErrorOnSocks5BytestreamRequest() throws XMPPException { + Connection targetConnection = getConnection(0); + + Connection initiatorConnection = getConnection(1); + + Bytestream bytestreamInitiation = Socks5PacketUtils.createBytestreamInitiation( + initiatorConnection.getUser(), targetConnection.getUser(), "session_id"); + bytestreamInitiation.addStreamHost("proxy.localhost", "127.0.0.1", 7777); + + PacketCollector collector = initiatorConnection.createPacketCollector(new PacketIDFilter( + bytestreamInitiation.getPacketID())); + initiatorConnection.sendPacket(bytestreamInitiation); + Packet result = collector.nextResult(); + + assertNotNull(result.getError()); + assertEquals(XMPPError.Condition.no_acceptable.toString(), result.getError().getCondition()); + + } + + /** + * Socks5 bytestream should be successfully established using the local Socks5 proxy. + * + * @throws Exception should not happen + */ + public void testSocks5BytestreamWithLocalSocks5Proxy() throws Exception { + + // setup port for local socks5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + SmackConfiguration.setLocalSocks5ProxyPort(7778); + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + socks5Proxy.start(); + + assertTrue(socks5Proxy.isRunning()); + + Connection initiatorConnection = getConnection(0); + Connection targetConnection = getConnection(1); + + // test data + final byte[] data = new byte[] { 1, 2, 3 }; + final SynchronousQueue queue = new SynchronousQueue(); + + Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); + + Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { + + public void incomingBytestreamRequest(Socks5BytestreamRequest request) { + InputStream inputStream; + try { + Socks5BytestreamSession session = request.accept(); + inputStream = session.getInputStream(); + byte[] receivedData = new byte[3]; + inputStream.read(receivedData); + queue.put(receivedData); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); + + Socks5BytestreamSession session = initiatorByteStreamManager.establishSession( + targetConnection.getUser()); + OutputStream outputStream = session.getOutputStream(); + + assertTrue(session.isDirect()); + + // verify stream + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + // reset default configuration + SmackConfiguration.setLocalSocks5ProxyPort(7777); + + } + + /** + * Socks5 bytestream should be successfully established using a Socks5 proxy provided by the + * XMPP server. + *

    + * This test will fail if the XMPP server doesn't provide any Socks5 proxies or the Socks5 proxy + * only allows Socks5 bytestreams in the context of a file transfer (like Openfire in default + * configuration, see xmpp.proxy.transfer.required flag). + * + * @throws Exception if no Socks5 proxies found or proxy is unwilling to activate Socks5 + * bytestream + */ + public void testSocks5BytestreamWithRemoteSocks5Proxy() throws Exception { + + // disable local socks5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + Socks5Proxy.getSocks5Proxy().stop(); + + assertFalse(Socks5Proxy.getSocks5Proxy().isRunning()); + + Connection initiatorConnection = getConnection(0); + Connection targetConnection = getConnection(1); + + // test data + final byte[] data = new byte[] { 1, 2, 3 }; + final SynchronousQueue queue = new SynchronousQueue(); + + Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); + + Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { + + public void incomingBytestreamRequest(Socks5BytestreamRequest request) { + InputStream inputStream; + try { + Socks5BytestreamSession session = request.accept(); + inputStream = session.getInputStream(); + byte[] receivedData = new byte[3]; + inputStream.read(receivedData); + queue.put(receivedData); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); + + Socks5BytestreamSession session = initiatorByteStreamManager.establishSession( + targetConnection.getUser()); + OutputStream outputStream = session.getOutputStream(); + + assertTrue(session.isMediated()); + + // verify stream + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + // reset default configuration + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + Socks5Proxy.getSocks5Proxy().start(); + + } + + /** + * Socks5 bytestream should be successfully established using a Socks5 proxy provided by the + * XMPP server. The established connection should transfer data bidirectional if the Socks5 + * proxy supports it. + *

    + * Support for bidirectional Socks5 bytestream: + *

      + *
    • Openfire (3.6.4 and below) - no
    • + *
    • ejabberd (2.0.5 and higher) - yes
    • + *
    + *

    + * This test will fail if the XMPP server doesn't provide any Socks5 proxies or the Socks5 proxy + * only allows Socks5 bytestreams in the context of a file transfer (like Openfire in default + * configuration, see xmpp.proxy.transfer.required flag). + * + * @throws Exception if no Socks5 proxies found or proxy is unwilling to activate Socks5 + * bytestream + */ + public void testBiDirectionalSocks5BytestreamWithRemoteSocks5Proxy() throws Exception { + + Connection initiatorConnection = getConnection(0); + + // disable local socks5 proxy + SmackConfiguration.setLocalSocks5ProxyEnabled(false); + Socks5Proxy.getSocks5Proxy().stop(); + + assertFalse(Socks5Proxy.getSocks5Proxy().isRunning()); + + Connection targetConnection = getConnection(1); + + // test data + final byte[] data = new byte[] { 1, 2, 3 }; + final SynchronousQueue queue = new SynchronousQueue(); + + Socks5BytestreamManager targetByteStreamManager = Socks5BytestreamManager.getBytestreamManager(targetConnection); + + Socks5BytestreamListener incomingByteStreamListener = new Socks5BytestreamListener() { + + public void incomingBytestreamRequest(Socks5BytestreamRequest request) { + try { + Socks5BytestreamSession session = request.accept(); + OutputStream outputStream = session.getOutputStream(); + outputStream.write(data); + outputStream.flush(); + InputStream inputStream = session.getInputStream(); + byte[] receivedData = new byte[3]; + inputStream.read(receivedData); + queue.put(receivedData); + session.close(); + } + catch (Exception e) { + fail(e.getMessage()); + } + } + + }; + targetByteStreamManager.addIncomingBytestreamListener(incomingByteStreamListener); + + Socks5BytestreamManager initiatorByteStreamManager = Socks5BytestreamManager.getBytestreamManager(initiatorConnection); + + Socks5BytestreamSession session = initiatorByteStreamManager.establishSession(targetConnection.getUser()); + + assertTrue(session.isMediated()); + + // verify stream + final byte[] receivedData = new byte[3]; + final InputStream inputStream = session.getInputStream(); + + FutureTask futureTask = new FutureTask(new Callable() { + + public Integer call() throws Exception { + return inputStream.read(receivedData); + } + }); + Thread executor = new Thread(futureTask); + executor.start(); + + try { + futureTask.get(2000, TimeUnit.MILLISECONDS); + } + catch (TimeoutException e) { + // reset default configuration + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + Socks5Proxy.getSocks5Proxy().start(); + + fail("Couldn't send data from target to inititator"); + } + + assertEquals("sent data not equal to received data", data, receivedData); + + OutputStream outputStream = session.getOutputStream(); + + outputStream.write(data); + outputStream.flush(); + outputStream.close(); + + assertEquals("received data not equal to sent data", data, queue.take()); + + session.close(); + + // reset default configuration + SmackConfiguration.setLocalSocks5ProxyEnabled(true); + Socks5Proxy.getSocks5Proxy().start(); + + } + + @Override + protected int getMaxConnections() { + return 2; + } + +} diff --git a/integration-test/org/jivesoftware/smackx/commands/AdHocCommandDiscoTest.java b/integration-test/org/jivesoftware/smackx/commands/AdHocCommandDiscoTest.java index 55b8eec6b..3450a74ba 100644 --- a/integration-test/org/jivesoftware/smackx/commands/AdHocCommandDiscoTest.java +++ b/integration-test/org/jivesoftware/smackx/commands/AdHocCommandDiscoTest.java @@ -108,4 +108,4 @@ public class AdHocCommandDiscoTest extends SmackTestCase { protected int getMaxConnections() { return 2; } -} \ No newline at end of file +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/CarExtension.java b/integration-test/org/jivesoftware/smackx/pubsub/CarExtension.java index d7d871958..a0c0c66c4 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/CarExtension.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/CarExtension.java @@ -14,50 +14,50 @@ * 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; +package org.jivesoftware.smackx.pubsub; + +import org.jivesoftware.smack.packet.PacketExtension; /** * * @author Robin Collier * */ -class CarExtension implements PacketExtension -{ - private String color; - private int numTires; - - public CarExtension(String col, int num) - { - color = col; - numTires = num; - } - - public String getColor() - { - return color; - } - - public int getNumTires() - { - return numTires; - } - - public String getElementName() - { - return "car"; - } - - public String getNamespace() - { - return "pubsub:test:vehicle"; - } - - public String toXML() - { - return "<" + getElementName() + " xmlns='" + getNamespace() + "'>"; - } - -} \ No newline at end of file +class CarExtension implements PacketExtension +{ + private String color; + private int numTires; + + public CarExtension(String col, int num) + { + color = col; + numTires = num; + } + + public String getColor() + { + return color; + } + + public int getNumTires() + { + return numTires; + } + + public String getElementName() + { + return "car"; + } + + public String getNamespace() + { + return "pubsub:test:vehicle"; + } + + public String toXML() + { + return "<" + getElementName() + " xmlns='" + getNamespace() + "'>"; + } + +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/CarExtensionProvider.java b/integration-test/org/jivesoftware/smackx/pubsub/CarExtensionProvider.java index 7d6f643b4..e3ae8c514 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/CarExtensionProvider.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/CarExtensionProvider.java @@ -14,40 +14,40 @@ * 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.smack.provider.PacketExtensionProvider; -import org.xmlpull.v1.XmlPullParser; +package org.jivesoftware.smackx.pubsub; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.xmlpull.v1.XmlPullParser; /** * * @author Robin Collier * - */ -public class CarExtensionProvider implements PacketExtensionProvider -{ - - public PacketExtension parseExtension(XmlPullParser parser) throws Exception - { - String color = null; - int numTires = 0; - - for (int i=0; i<2; i++) - { - while (parser.next() != XmlPullParser.START_TAG); - - if (parser.getName().equals("paint")) - { - color = parser.getAttributeValue(0); - } - else - { - numTires = Integer.parseInt(parser.getAttributeValue(0)); - } - } - while (parser.next() != XmlPullParser.END_TAG); - return new CarExtension(color, numTires); - } - -} \ No newline at end of file + */ +public class CarExtensionProvider implements PacketExtensionProvider +{ + + public PacketExtension parseExtension(XmlPullParser parser) throws Exception + { + String color = null; + int numTires = 0; + + for (int i=0; i<2; i++) + { + while (parser.next() != XmlPullParser.START_TAG); + + if (parser.getName().equals("paint")) + { + color = parser.getAttributeValue(0); + } + else + { + numTires = Integer.parseInt(parser.getAttributeValue(0)); + } + } + while (parser.next() != XmlPullParser.END_TAG); + return new CarExtension(color, numTires); + } + +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/EntityUseCases.java b/integration-test/org/jivesoftware/smackx/pubsub/EntityUseCases.java index 547fc1f3f..43b495eea 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/EntityUseCases.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/EntityUseCases.java @@ -14,76 +14,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import java.util.Iterator; -import java.util.List; - -import org.jivesoftware.smackx.packet.DiscoverInfo; -import org.jivesoftware.smackx.packet.DiscoverItems; -import org.jivesoftware.smackx.packet.DiscoverInfo.Identity; -import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; +package org.jivesoftware.smackx.pubsub; + +import java.util.Iterator; +import java.util.List; + +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.jivesoftware.smackx.packet.DiscoverItems; +import org.jivesoftware.smackx.packet.DiscoverInfo.Identity; +import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; /** * * @author Robin Collier * - */ -public class EntityUseCases extends SingleUserTestCase -{ - public void testDiscoverPubsubInfo() throws Exception - { - DiscoverInfo supportedFeatures = getManager().getSupportedFeatures(); - assertNotNull(supportedFeatures); - } - - public void testDiscoverNodeInfo() throws Exception - { - LeafNode myNode = getManager().createNode("DiscoNode" + System.currentTimeMillis()); - DiscoverInfo info = myNode.discoverInfo(); - assertTrue(info.getIdentities().hasNext()); - Identity ident = info.getIdentities().next(); - - assertEquals("leaf", ident.getType()); - } - - public void testDiscoverNodeItems() throws Exception - { - LeafNode myNode = getRandomPubnode(getManager(), true, false); - myNode.send(new Item()); - myNode.send(new Item()); - myNode.send(new Item()); - myNode.send(new Item()); - DiscoverItems items = myNode.discoverItems(); - - int count = 0; - - for(Iterator it = items.getItems(); it.hasNext(); it.next(),count++); - - assertEquals(4, count); - } - - public void testDiscoverSubscriptions() throws Exception - { - getManager().getSubscriptions(); - } - - public void testDiscoverNodeSubscriptions() throws Exception - { - LeafNode myNode = getRandomPubnode(getManager(), true, true); - myNode.subscribe(getConnection(0).getUser()); - List subscriptions = myNode.getSubscriptions(); - - assertTrue(subscriptions.size() < 3); - - for (Subscription subscription : subscriptions) - { - assertNull(subscription.getNode()); - } - } - - public void testRetrieveAffiliation() throws Exception - { - getManager().getAffiliations(); - } -} + */ +public class EntityUseCases extends SingleUserTestCase +{ + public void testDiscoverPubsubInfo() throws Exception + { + DiscoverInfo supportedFeatures = getManager().getSupportedFeatures(); + assertNotNull(supportedFeatures); + } + + public void testDiscoverNodeInfo() throws Exception + { + LeafNode myNode = getManager().createNode("DiscoNode" + System.currentTimeMillis()); + DiscoverInfo info = myNode.discoverInfo(); + assertTrue(info.getIdentities().hasNext()); + Identity ident = info.getIdentities().next(); + + assertEquals("leaf", ident.getType()); + } + + public void testDiscoverNodeItems() throws Exception + { + LeafNode myNode = getRandomPubnode(getManager(), true, false); + myNode.send(new Item()); + myNode.send(new Item()); + myNode.send(new Item()); + myNode.send(new Item()); + DiscoverItems items = myNode.discoverItems(); + + int count = 0; + + for(Iterator it = items.getItems(); it.hasNext(); it.next(),count++); + + assertEquals(4, count); + } + + public void testDiscoverSubscriptions() throws Exception + { + getManager().getSubscriptions(); + } + + public void testDiscoverNodeSubscriptions() throws Exception + { + LeafNode myNode = getRandomPubnode(getManager(), true, true); + myNode.subscribe(getConnection(0).getUser()); + List subscriptions = myNode.getSubscriptions(); + + assertTrue(subscriptions.size() < 3); + + for (Subscription subscription : subscriptions) + { + assertNull(subscription.getNode()); + } + } + + public void testRetrieveAffiliation() throws Exception + { + getManager().getAffiliations(); + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/MultiUserSubscriptionUseCases.java b/integration-test/org/jivesoftware/smackx/pubsub/MultiUserSubscriptionUseCases.java index e3f38d3a1..3a4bb6caa 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/MultiUserSubscriptionUseCases.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/MultiUserSubscriptionUseCases.java @@ -14,69 +14,69 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import java.util.Collection; -import java.util.List; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smackx.pubsub.test.PubSubTestCase; +package org.jivesoftware.smackx.pubsub; + +import java.util.Collection; +import java.util.List; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.pubsub.test.PubSubTestCase; /** * * @author Robin Collier * - */ -public class MultiUserSubscriptionUseCases extends PubSubTestCase -{ - - @Override - protected int getMaxConnections() - { - return 2; - } - - public void testGetItemsWithSingleSubscription() throws XMPPException - { - LeafNode node = getRandomPubnode(getManager(0), true, false); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - - LeafNode user2Node = (LeafNode) getManager(1).getNode(node.getId()); - user2Node.subscribe(getBareJID(1)); - - Collection items = user2Node.getItems(); - assertTrue(items.size() == 5); - } - - public void testGetItemsWithMultiSubscription() throws XMPPException - { - LeafNode node = getRandomPubnode(getManager(0), true, false); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - - LeafNode user2Node = (LeafNode) getManager(1).getNode(node.getId()); - Subscription sub1 = user2Node.subscribe(getBareJID(1)); - - Subscription sub2 = user2Node.subscribe(getBareJID(1)); - - try - { - user2Node.getItems(); - } - catch (XMPPException exc) - { - assertEquals("bad-request", exc.getXMPPError().getCondition()); - assertEquals(XMPPError.Type.MODIFY, exc.getXMPPError().getType()); - } - List items = user2Node.getItems(sub1.getId()); - assertTrue(items.size() == 5); - } -} + */ +public class MultiUserSubscriptionUseCases extends PubSubTestCase +{ + + @Override + protected int getMaxConnections() + { + return 2; + } + + public void testGetItemsWithSingleSubscription() throws XMPPException + { + LeafNode node = getRandomPubnode(getManager(0), true, false); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + + LeafNode user2Node = (LeafNode) getManager(1).getNode(node.getId()); + user2Node.subscribe(getBareJID(1)); + + Collection items = user2Node.getItems(); + assertTrue(items.size() == 5); + } + + public void testGetItemsWithMultiSubscription() throws XMPPException + { + LeafNode node = getRandomPubnode(getManager(0), true, false); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + + LeafNode user2Node = (LeafNode) getManager(1).getNode(node.getId()); + Subscription sub1 = user2Node.subscribe(getBareJID(1)); + + Subscription sub2 = user2Node.subscribe(getBareJID(1)); + + try + { + user2Node.getItems(); + } + catch (XMPPException exc) + { + assertEquals("bad-request", exc.getXMPPError().getCondition()); + assertEquals(XMPPError.Type.MODIFY, exc.getXMPPError().getType()); + } + List items = user2Node.getItems(sub1.getId()); + assertTrue(items.size() == 5); + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/OwnerUseCases.java b/integration-test/org/jivesoftware/smackx/pubsub/OwnerUseCases.java index e600af17a..abf6c3f7a 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/OwnerUseCases.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/OwnerUseCases.java @@ -14,135 +14,135 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import java.util.Collection; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; +package org.jivesoftware.smackx.pubsub; + +import java.util.Collection; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; /** * * @author Robin Collier * - */ -public class OwnerUseCases extends SingleUserTestCase -{ - public void testCreateInstantNode() throws Exception - { - LeafNode node = getManager().createNode(); - assertNotNull(node); - assertNotNull(node.getId()); - } - - public void testCreateNamedNode() throws Exception - { - String id = "TestNamedNode" + System.currentTimeMillis(); - LeafNode node = getManager().createNode(id); - assertEquals(id, node.getId()); - } - - public void testCreateConfiguredNode() throws Exception - { - // Generate reasonably unique for multiple tests - String id = "TestConfigNode" + System.currentTimeMillis(); - - // Create and configure a node - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setAccessModel(AccessModel.open); - form.setDeliverPayloads(false); - form.setNotifyRetract(true); - form.setPersistentItems(true); - form.setPublishModel(PublishModel.open); - - LeafNode node = (LeafNode)getManager().createNode(id, form); - - ConfigureForm currentForm = node.getNodeConfiguration(); - assertEquals(AccessModel.open, currentForm.getAccessModel()); - assertFalse(currentForm.isDeliverPayloads()); - assertTrue(currentForm.isNotifyRetract()); - assertTrue(currentForm.isPersistItems()); - assertEquals(PublishModel.open, currentForm.getPublishModel()); - } - - public void testCreateAndUpdateConfiguredNode() throws Exception - { - // Generate reasonably unique for multiple tests - String id = "TestConfigNode2" + System.currentTimeMillis(); - - // Create and configure a node - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setAccessModel(AccessModel.open); - form.setDeliverPayloads(false); - form.setNotifyRetract(true); - form.setPersistentItems(true); - form.setPublishModel(PublishModel.open); - - LeafNode myNode = (LeafNode)getManager().createNode(id, form); - ConfigureForm config = myNode.getNodeConfiguration(); - - assertEquals(AccessModel.open, config.getAccessModel()); - assertFalse(config.isDeliverPayloads()); - assertTrue(config.isNotifyRetract()); - assertTrue(config.isPersistItems()); - assertEquals(PublishModel.open, config.getPublishModel()); - - ConfigureForm submitForm = new ConfigureForm(config.createAnswerForm()); - submitForm.setAccessModel(AccessModel.whitelist); - submitForm.setDeliverPayloads(true); - submitForm.setNotifyRetract(false); - submitForm.setPersistentItems(false); - submitForm.setPublishModel(PublishModel.publishers); - myNode.sendConfigurationForm(submitForm); - - ConfigureForm newConfig = myNode.getNodeConfiguration(); - assertEquals(AccessModel.whitelist, newConfig.getAccessModel()); - assertTrue(newConfig.isDeliverPayloads()); - assertFalse(newConfig.isNotifyRetract()); - assertFalse(newConfig.isPersistItems()); - assertEquals(PublishModel.publishers, newConfig.getPublishModel()); - } - - public void testGetDefaultConfig() throws Exception - { - ConfigureForm form = getManager().getDefaultConfiguration(); - assertNotNull(form); - } - - public void testDeleteNode() throws Exception - { - LeafNode myNode = getManager().createNode(); - assertNotNull(getManager().getNode(myNode.getId())); - - getManager(0).deleteNode(myNode.getId()); - - try - { - assertNull(getManager().getNode(myNode.getId())); - fail("Node should not exist"); - } - catch (XMPPException e) - { - } - } - - public void testPurgeItems() throws XMPPException - { - LeafNode node = getRandomPubnode(getManager(), true, false); - - node.send(new Item()); - node.send(new Item()); - node.send(new Item()); - node.send(new Item()); - node.send(new Item()); - - Collection items = node.getItems(); - assertTrue(items.size() == 5); - - node.deleteAllItems(); - items = node.getItems(); - - // Pubsub service may keep the last notification (in spec), so 0 or 1 may be returned on get items. - assertTrue(items.size() < 2); - } -} + */ +public class OwnerUseCases extends SingleUserTestCase +{ + public void testCreateInstantNode() throws Exception + { + LeafNode node = getManager().createNode(); + assertNotNull(node); + assertNotNull(node.getId()); + } + + public void testCreateNamedNode() throws Exception + { + String id = "TestNamedNode" + System.currentTimeMillis(); + LeafNode node = getManager().createNode(id); + assertEquals(id, node.getId()); + } + + public void testCreateConfiguredNode() throws Exception + { + // Generate reasonably unique for multiple tests + String id = "TestConfigNode" + System.currentTimeMillis(); + + // Create and configure a node + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setAccessModel(AccessModel.open); + form.setDeliverPayloads(false); + form.setNotifyRetract(true); + form.setPersistentItems(true); + form.setPublishModel(PublishModel.open); + + LeafNode node = (LeafNode)getManager().createNode(id, form); + + ConfigureForm currentForm = node.getNodeConfiguration(); + assertEquals(AccessModel.open, currentForm.getAccessModel()); + assertFalse(currentForm.isDeliverPayloads()); + assertTrue(currentForm.isNotifyRetract()); + assertTrue(currentForm.isPersistItems()); + assertEquals(PublishModel.open, currentForm.getPublishModel()); + } + + public void testCreateAndUpdateConfiguredNode() throws Exception + { + // Generate reasonably unique for multiple tests + String id = "TestConfigNode2" + System.currentTimeMillis(); + + // Create and configure a node + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setAccessModel(AccessModel.open); + form.setDeliverPayloads(false); + form.setNotifyRetract(true); + form.setPersistentItems(true); + form.setPublishModel(PublishModel.open); + + LeafNode myNode = (LeafNode)getManager().createNode(id, form); + ConfigureForm config = myNode.getNodeConfiguration(); + + assertEquals(AccessModel.open, config.getAccessModel()); + assertFalse(config.isDeliverPayloads()); + assertTrue(config.isNotifyRetract()); + assertTrue(config.isPersistItems()); + assertEquals(PublishModel.open, config.getPublishModel()); + + ConfigureForm submitForm = new ConfigureForm(config.createAnswerForm()); + submitForm.setAccessModel(AccessModel.whitelist); + submitForm.setDeliverPayloads(true); + submitForm.setNotifyRetract(false); + submitForm.setPersistentItems(false); + submitForm.setPublishModel(PublishModel.publishers); + myNode.sendConfigurationForm(submitForm); + + ConfigureForm newConfig = myNode.getNodeConfiguration(); + assertEquals(AccessModel.whitelist, newConfig.getAccessModel()); + assertTrue(newConfig.isDeliverPayloads()); + assertFalse(newConfig.isNotifyRetract()); + assertFalse(newConfig.isPersistItems()); + assertEquals(PublishModel.publishers, newConfig.getPublishModel()); + } + + public void testGetDefaultConfig() throws Exception + { + ConfigureForm form = getManager().getDefaultConfiguration(); + assertNotNull(form); + } + + public void testDeleteNode() throws Exception + { + LeafNode myNode = getManager().createNode(); + assertNotNull(getManager().getNode(myNode.getId())); + + getManager(0).deleteNode(myNode.getId()); + + try + { + assertNull(getManager().getNode(myNode.getId())); + fail("Node should not exist"); + } + catch (XMPPException e) + { + } + } + + public void testPurgeItems() throws XMPPException + { + LeafNode node = getRandomPubnode(getManager(), true, false); + + node.send(new Item()); + node.send(new Item()); + node.send(new Item()); + node.send(new Item()); + node.send(new Item()); + + Collection items = node.getItems(); + assertTrue(items.size() == 5); + + node.deleteAllItems(); + items = node.getItems(); + + // Pubsub service may keep the last notification (in spec), so 0 or 1 may be returned on get items. + assertTrue(items.size() < 2); + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/PublisherUseCases.java b/integration-test/org/jivesoftware/smackx/pubsub/PublisherUseCases.java index 8ceae3799..dd27005a0 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/PublisherUseCases.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/PublisherUseCases.java @@ -14,153 +14,153 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import java.util.Collection; -import java.util.List; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.packet.XMPPError.Condition; -import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; -import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; +package org.jivesoftware.smackx.pubsub; + +import java.util.Collection; +import java.util.List; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.packet.XMPPError.Condition; +import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; +import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; /** * * @author Robin Collier * - */ -public class PublisherUseCases extends SingleUserTestCase -{ - public void testSendNodeTrNot() throws Exception - { - getPubnode(false, false).send(); - } - - public void testSendNodeTrPay_WithOutPayload() throws XMPPException - { - LeafNode node = getPubnode(false, true); - - try - { - node.send(new Item()); - fail("Exception should be thrown when there is no payload"); - } - catch (XMPPException e) { - XMPPError err = e.getXMPPError(); - assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); - assertTrue(err.getCondition().equals(Condition.bad_request.toString())); - assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); - } - - try - { - node.send(new Item("test" + System.currentTimeMillis())); - fail("Exception should be thrown when there is no payload"); - } - catch (XMPPException e) { - XMPPError err = e.getXMPPError(); - assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); - assertTrue(err.getCondition().equals(Condition.bad_request.toString())); - assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); - } - } - - public void testSendNodeTrPay_WithPayload() throws XMPPException - { - LeafNode node = getPubnode(false, true); - node.send(new PayloadItem(null, - new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); - node.send(new PayloadItem("test" + System.currentTimeMillis(), - new SimplePayload("book", "pubsub:test:book", "Two Towers"))); - } - - public void testSendNodePerNot() throws Exception - { - LeafNode node = getPubnode(true, false); - node.send(new Item()); - node.send(new Item("test" + System.currentTimeMillis())); - node.send(new PayloadItem(null, - new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); - node.send(new PayloadItem("test" + System.currentTimeMillis(), - new SimplePayload("book", "pubsub:test:book", "Two Towers"))); - } - - public void testSendPerPay_WithPayload() throws Exception - { - LeafNode node = getPubnode(true, true); - node.send(new PayloadItem(null, - new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); - node.send(new PayloadItem("test" + System.currentTimeMillis(), - new SimplePayload("book", "pubsub:test:book", "Two Towers"))); - } - - public void testSendPerPay_NoPayload() throws Exception - { - LeafNode node = getPubnode(true, true); - try - { - node.send(new Item()); - fail("Exception should be thrown when there is no payload"); - } - catch (XMPPException e) { - XMPPError err = e.getXMPPError(); - assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); - assertTrue(err.getCondition().equals(Condition.bad_request.toString())); - assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); - } - - try - { - node.send(new Item("test" + System.currentTimeMillis())); - fail("Exception should be thrown when there is no payload"); - } - catch (XMPPException e) { - XMPPError err = e.getXMPPError(); - assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); - assertTrue(err.getCondition().equals(Condition.bad_request.toString())); - assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); - } - } - - public void testDeleteItems() throws XMPPException - { - LeafNode node = getPubnode(true, false); - - node.send(new Item("1")); - node.send(new Item("2")); - node.send(new Item("3")); - node.send(new Item("4")); - - node.deleteItem("1"); - Collection items = node.getItems(); - - assertEquals(3, items.size()); - } - - public void testPersistItems() throws XMPPException - { - LeafNode node = getPubnode(true, false); - - node.send(new Item("1")); - node.send(new Item("2")); - node.send(new Item("3")); - node.send(new Item("4")); - - Collection items = node.getItems(); - - assertTrue(items.size() == 4); - } - - public void testItemOverwritten() throws XMPPException - { - LeafNode node = getPubnode(true, false); - - node.send(new PayloadItem("1", new SimplePayload("test", null, ""))); - node.send(new PayloadItem("1", new SimplePayload("test2", null, ""))); - - List items = node.getItems(); - assertEquals(1, items.size()); - assertEquals("1", items.get(0).getId()); - } -} + */ +public class PublisherUseCases extends SingleUserTestCase +{ + public void testSendNodeTrNot() throws Exception + { + getPubnode(false, false).send(); + } + + public void testSendNodeTrPay_WithOutPayload() throws XMPPException + { + LeafNode node = getPubnode(false, true); + + try + { + node.send(new Item()); + fail("Exception should be thrown when there is no payload"); + } + catch (XMPPException e) { + XMPPError err = e.getXMPPError(); + assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); + assertTrue(err.getCondition().equals(Condition.bad_request.toString())); + assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); + } + + try + { + node.send(new Item("test" + System.currentTimeMillis())); + fail("Exception should be thrown when there is no payload"); + } + catch (XMPPException e) { + XMPPError err = e.getXMPPError(); + assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); + assertTrue(err.getCondition().equals(Condition.bad_request.toString())); + assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); + } + } + + public void testSendNodeTrPay_WithPayload() throws XMPPException + { + LeafNode node = getPubnode(false, true); + node.send(new PayloadItem(null, + new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); + node.send(new PayloadItem("test" + System.currentTimeMillis(), + new SimplePayload("book", "pubsub:test:book", "Two Towers"))); + } + + public void testSendNodePerNot() throws Exception + { + LeafNode node = getPubnode(true, false); + node.send(new Item()); + node.send(new Item("test" + System.currentTimeMillis())); + node.send(new PayloadItem(null, + new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); + node.send(new PayloadItem("test" + System.currentTimeMillis(), + new SimplePayload("book", "pubsub:test:book", "Two Towers"))); + } + + public void testSendPerPay_WithPayload() throws Exception + { + LeafNode node = getPubnode(true, true); + node.send(new PayloadItem(null, + new SimplePayload("book", "pubsub:test:book", "Lord of the Rings"))); + node.send(new PayloadItem("test" + System.currentTimeMillis(), + new SimplePayload("book", "pubsub:test:book", "Two Towers"))); + } + + public void testSendPerPay_NoPayload() throws Exception + { + LeafNode node = getPubnode(true, true); + try + { + node.send(new Item()); + fail("Exception should be thrown when there is no payload"); + } + catch (XMPPException e) { + XMPPError err = e.getXMPPError(); + assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); + assertTrue(err.getCondition().equals(Condition.bad_request.toString())); + assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); + } + + try + { + node.send(new Item("test" + System.currentTimeMillis())); + fail("Exception should be thrown when there is no payload"); + } + catch (XMPPException e) { + XMPPError err = e.getXMPPError(); + assertTrue(err.getType().equals(XMPPError.Type.MODIFY)); + assertTrue(err.getCondition().equals(Condition.bad_request.toString())); + assertNotNull(err.getExtension("payload-required", PubSubNamespace.ERROR.getXmlns())); + } + } + + public void testDeleteItems() throws XMPPException + { + LeafNode node = getPubnode(true, false); + + node.send(new Item("1")); + node.send(new Item("2")); + node.send(new Item("3")); + node.send(new Item("4")); + + node.deleteItem("1"); + Collection items = node.getItems(); + + assertEquals(3, items.size()); + } + + public void testPersistItems() throws XMPPException + { + LeafNode node = getPubnode(true, false); + + node.send(new Item("1")); + node.send(new Item("2")); + node.send(new Item("3")); + node.send(new Item("4")); + + Collection items = node.getItems(); + + assertTrue(items.size() == 4); + } + + public void testItemOverwritten() throws XMPPException + { + LeafNode node = getPubnode(true, false); + + node.send(new PayloadItem("1", new SimplePayload("test", null, ""))); + node.send(new PayloadItem("1", new SimplePayload("test2", null, ""))); + + List items = node.getItems(); + assertEquals(1, items.size()); + assertEquals("1", items.get(0).getId()); + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/SubscriberUseCases.java b/integration-test/org/jivesoftware/smackx/pubsub/SubscriberUseCases.java index 56073181a..5464bb82e 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/SubscriberUseCases.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/SubscriberUseCases.java @@ -14,267 +14,267 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.FormField; -import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; +package org.jivesoftware.smackx.pubsub; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.FormField; +import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; /** * * @author Robin Collier * - */ -public class SubscriberUseCases extends SingleUserTestCase -{ - public void testSubscribe() throws Exception - { - LeafNode node = getPubnode(false, false); - Subscription sub = node.subscribe(getJid()); - - assertEquals(getJid(), sub.getJid()); - assertNotNull(sub.getId()); - assertEquals(node.getId(), sub.getNode()); - assertEquals(Subscription.State.subscribed, sub.getState()); - } - - public void testSubscribeBadJid() throws Exception - { - LeafNode node = getPubnode(false, false); - - try - { - node.subscribe("this@over.here"); - fail(); - } - catch (XMPPException e) - { - } - } - - public void testSubscribeWithOptions() throws Exception - { - SubscribeForm form = new SubscribeForm(FormType.submit); - form.setDeliverOn(true); - Calendar expire = Calendar.getInstance(); - expire.set(2020, 1, 1); - form.setExpiry(expire.getTime()); - LeafNode node = getPubnode(false, false); - node.subscribe(getJid(), form); - } - - public void testSubscribeConfigRequired() throws Exception - { - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setAccessModel(AccessModel.open); - - // Openfire specific field - nothing in the spec yet - FormField required = new FormField("pubsub#subscription_required"); - required.setType(FormField.TYPE_BOOLEAN); - form.addField(required); - form.setAnswer("pubsub#subscription_required", true); - LeafNode node = (LeafNode)getManager().createNode("Pubnode" + System.currentTimeMillis(), form); - - Subscription sub = node.subscribe(getJid()); - - assertEquals(getJid(), sub.getJid()); - assertNotNull(sub.getId()); - assertEquals(node.getId(), sub.getNode()); - assertEquals(true, sub.isConfigRequired()); - } - - public void testUnsubscribe() throws Exception - { - LeafNode node = getPubnode(false, false); - node.subscribe(getJid()); - Collection subs = node.getSubscriptions(); - - node.unsubscribe(getJid()); - Collection afterSubs = node.getSubscriptions(); - assertEquals(subs.size()-1, afterSubs.size()); - } - - public void testUnsubscribeWithMultipleNoSubId() throws Exception - { - LeafNode node = getPubnode(false, false); - node.subscribe(getBareJID(0)); - node.subscribe(getBareJID(0)); - node.subscribe(getBareJID(0)); - - try - { - node.unsubscribe(getBareJID(0)); - fail("Unsubscribe with no subid should fail"); - } - catch (XMPPException e) - { - } - } - - public void testUnsubscribeWithMultipleWithSubId() throws Exception - { - LeafNode node = getPubnode(false, false); - node.subscribe(getJid()); - Subscription sub = node.subscribe(getJid()); - node.subscribe(getJid()); - node.unsubscribe(getJid(), sub.getId()); - } - - public void testGetOptions() throws Exception - { - LeafNode node = getPubnode(false, false); - Subscription sub = node.subscribe(getJid()); - SubscribeForm form = node.getSubscriptionOptions(getJid(), sub.getId()); - assertNotNull(form); - } - -// public void testSubscribeWithConfig() throws Exception -// { -// LeafNode node = getPubnode(false, false); -// -// Subscription sub = node.subscribe(getBareJID(0)); -// -// assertEquals(getBareJID(0), sub.getJid()); -// assertNotNull(sub.getId()); -// assertEquals(node.getId(), sub.getNode()); -// assertEquals(true, sub.isConfigRequired()); -// } -// - public void testGetItems() throws Exception - { - LeafNode node = getPubnode(true, false); - runNodeTests(node); - } - - private void runNodeTests(LeafNode node) throws Exception - { - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - node.send((Item)null); - - Collection items = node.getItems(); - assertTrue(items.size() == 5); - - long curTime = System.currentTimeMillis(); - node.send(new Item("1-" + curTime)); - node.send(new Item("2-" + curTime)); - node.send(new Item("3-" + curTime)); - node.send(new Item("4-" + curTime)); - node.send(new Item("5-" + curTime)); - - items = node.getItems(); - assertTrue(items.size() == 10); - - LeafNode payloadNode = getPubnode(true, true); - - Map idPayload = new HashMap(); - idPayload.put("6-" + curTime, ""); - idPayload.put("7-" + curTime, ""); - idPayload.put("8-" + curTime, "text"); - idPayload.put("9-" + curTime, ""); - - for (Map.Entry payload : idPayload.entrySet()) - { - payloadNode.send(new PayloadItem(payload.getKey(), new SimplePayload("a", "pubsub:test", payload.getValue()))); - } - - payloadNode.send(new PayloadItem("6-" + curTime, new SimplePayload("a", "pubsub:test", ""))); - payloadNode.send(new PayloadItem("7-" + curTime, new SimplePayload("a", "pubsub:test", ""))); - payloadNode.send(new PayloadItem("8-" + curTime, new SimplePayload("entity", "pubsub:test", "texta"))); - payloadNode.send(new PayloadItem("9-" + curTime, new SimplePayload("entity", "pubsub:test", "b"))); - - List> payloadItems = payloadNode.getItems(); - Map> idMap = new HashMap>(); - - for (PayloadItem payloadItem : payloadItems) - { - idMap.put(payloadItem.getId(), payloadItem); - } - - assertEquals(4, payloadItems.size()); - - PayloadItem testItem = idMap.get("6-" + curTime); - assertNotNull(testItem); - assertXMLEqual("", testItem.getPayload().toXML()); - - testItem = idMap.get("7-" + curTime); - assertNotNull(testItem); - assertXMLEqual("", testItem.getPayload().toXML()); - - testItem = idMap.get("8-" + curTime); - assertNotNull(testItem); - assertXMLEqual("texta", testItem.getPayload().toXML()); - - testItem = idMap.get("9-" + curTime); - assertNotNull(testItem); - assertXMLEqual("b", testItem.getPayload().toXML()); - } - - public void testGetSpecifiedItems() throws Exception - { - LeafNode node = getPubnode(true, true); - - node.send(new PayloadItem("1", new SimplePayload("a", "pubsub:test", ""))); - node.send(new PayloadItem("2", new SimplePayload("a", "pubsub:test", ""))); - node.send(new PayloadItem("3", new SimplePayload("a", "pubsub:test", ""))); - node.send(new PayloadItem("4", new SimplePayload("a", "pubsub:test", ""))); - node.send(new PayloadItem("5", new SimplePayload("a", "pubsub:test", ""))); - - Collection ids = new ArrayList(3); - ids.add("1"); - ids.add("3"); - ids.add("4"); - - List> items = node.getItems(ids); - assertEquals(3, items.size()); - assertEquals("1", items.get(0).getId()); - assertXMLEqual("", items.get(0).getPayload().toXML()); - assertEquals( "3", items.get(1).getId()); - assertXMLEqual("", items.get(1).getPayload().toXML()); - assertEquals("4", items.get(2).getId()); - assertXMLEqual("", items.get(2).getPayload().toXML()); - } - - public void testGetLastNItems() throws XMPPException - { - LeafNode node = getPubnode(true, false); - - node.send(new Item("1")); - node.send(new Item("2")); - node.send(new Item("3")); - node.send(new Item("4")); - node.send(new Item("5")); - - List items = node.getItems(2); - assertEquals(2, items.size()); - assertTrue(listContainsId("4", items)); - assertTrue(listContainsId("5", items)); - } - - private static boolean listContainsId(String id, List items) - { - for (Item item : items) - { - if (item.getId().equals(id)) - return true; - } - return false; - } - - private String getJid() - { - return getConnection(0).getUser(); - } - -} + */ +public class SubscriberUseCases extends SingleUserTestCase +{ + public void testSubscribe() throws Exception + { + LeafNode node = getPubnode(false, false); + Subscription sub = node.subscribe(getJid()); + + assertEquals(getJid(), sub.getJid()); + assertNotNull(sub.getId()); + assertEquals(node.getId(), sub.getNode()); + assertEquals(Subscription.State.subscribed, sub.getState()); + } + + public void testSubscribeBadJid() throws Exception + { + LeafNode node = getPubnode(false, false); + + try + { + node.subscribe("this@over.here"); + fail(); + } + catch (XMPPException e) + { + } + } + + public void testSubscribeWithOptions() throws Exception + { + SubscribeForm form = new SubscribeForm(FormType.submit); + form.setDeliverOn(true); + Calendar expire = Calendar.getInstance(); + expire.set(2020, 1, 1); + form.setExpiry(expire.getTime()); + LeafNode node = getPubnode(false, false); + node.subscribe(getJid(), form); + } + + public void testSubscribeConfigRequired() throws Exception + { + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setAccessModel(AccessModel.open); + + // Openfire specific field - nothing in the spec yet + FormField required = new FormField("pubsub#subscription_required"); + required.setType(FormField.TYPE_BOOLEAN); + form.addField(required); + form.setAnswer("pubsub#subscription_required", true); + LeafNode node = (LeafNode)getManager().createNode("Pubnode" + System.currentTimeMillis(), form); + + Subscription sub = node.subscribe(getJid()); + + assertEquals(getJid(), sub.getJid()); + assertNotNull(sub.getId()); + assertEquals(node.getId(), sub.getNode()); + assertEquals(true, sub.isConfigRequired()); + } + + public void testUnsubscribe() throws Exception + { + LeafNode node = getPubnode(false, false); + node.subscribe(getJid()); + Collection subs = node.getSubscriptions(); + + node.unsubscribe(getJid()); + Collection afterSubs = node.getSubscriptions(); + assertEquals(subs.size()-1, afterSubs.size()); + } + + public void testUnsubscribeWithMultipleNoSubId() throws Exception + { + LeafNode node = getPubnode(false, false); + node.subscribe(getBareJID(0)); + node.subscribe(getBareJID(0)); + node.subscribe(getBareJID(0)); + + try + { + node.unsubscribe(getBareJID(0)); + fail("Unsubscribe with no subid should fail"); + } + catch (XMPPException e) + { + } + } + + public void testUnsubscribeWithMultipleWithSubId() throws Exception + { + LeafNode node = getPubnode(false, false); + node.subscribe(getJid()); + Subscription sub = node.subscribe(getJid()); + node.subscribe(getJid()); + node.unsubscribe(getJid(), sub.getId()); + } + + public void testGetOptions() throws Exception + { + LeafNode node = getPubnode(false, false); + Subscription sub = node.subscribe(getJid()); + SubscribeForm form = node.getSubscriptionOptions(getJid(), sub.getId()); + assertNotNull(form); + } + +// public void testSubscribeWithConfig() throws Exception +// { +// LeafNode node = getPubnode(false, false); +// +// Subscription sub = node.subscribe(getBareJID(0)); +// +// assertEquals(getBareJID(0), sub.getJid()); +// assertNotNull(sub.getId()); +// assertEquals(node.getId(), sub.getNode()); +// assertEquals(true, sub.isConfigRequired()); +// } +// + public void testGetItems() throws Exception + { + LeafNode node = getPubnode(true, false); + runNodeTests(node); + } + + private void runNodeTests(LeafNode node) throws Exception + { + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + node.send((Item)null); + + Collection items = node.getItems(); + assertTrue(items.size() == 5); + + long curTime = System.currentTimeMillis(); + node.send(new Item("1-" + curTime)); + node.send(new Item("2-" + curTime)); + node.send(new Item("3-" + curTime)); + node.send(new Item("4-" + curTime)); + node.send(new Item("5-" + curTime)); + + items = node.getItems(); + assertTrue(items.size() == 10); + + LeafNode payloadNode = getPubnode(true, true); + + Map idPayload = new HashMap(); + idPayload.put("6-" + curTime, ""); + idPayload.put("7-" + curTime, ""); + idPayload.put("8-" + curTime, "text"); + idPayload.put("9-" + curTime, ""); + + for (Map.Entry payload : idPayload.entrySet()) + { + payloadNode.send(new PayloadItem(payload.getKey(), new SimplePayload("a", "pubsub:test", payload.getValue()))); + } + + payloadNode.send(new PayloadItem("6-" + curTime, new SimplePayload("a", "pubsub:test", ""))); + payloadNode.send(new PayloadItem("7-" + curTime, new SimplePayload("a", "pubsub:test", ""))); + payloadNode.send(new PayloadItem("8-" + curTime, new SimplePayload("entity", "pubsub:test", "texta"))); + payloadNode.send(new PayloadItem("9-" + curTime, new SimplePayload("entity", "pubsub:test", "b"))); + + List> payloadItems = payloadNode.getItems(); + Map> idMap = new HashMap>(); + + for (PayloadItem payloadItem : payloadItems) + { + idMap.put(payloadItem.getId(), payloadItem); + } + + assertEquals(4, payloadItems.size()); + + PayloadItem testItem = idMap.get("6-" + curTime); + assertNotNull(testItem); + assertXMLEqual("", testItem.getPayload().toXML()); + + testItem = idMap.get("7-" + curTime); + assertNotNull(testItem); + assertXMLEqual("", testItem.getPayload().toXML()); + + testItem = idMap.get("8-" + curTime); + assertNotNull(testItem); + assertXMLEqual("texta", testItem.getPayload().toXML()); + + testItem = idMap.get("9-" + curTime); + assertNotNull(testItem); + assertXMLEqual("b", testItem.getPayload().toXML()); + } + + public void testGetSpecifiedItems() throws Exception + { + LeafNode node = getPubnode(true, true); + + node.send(new PayloadItem("1", new SimplePayload("a", "pubsub:test", ""))); + node.send(new PayloadItem("2", new SimplePayload("a", "pubsub:test", ""))); + node.send(new PayloadItem("3", new SimplePayload("a", "pubsub:test", ""))); + node.send(new PayloadItem("4", new SimplePayload("a", "pubsub:test", ""))); + node.send(new PayloadItem("5", new SimplePayload("a", "pubsub:test", ""))); + + Collection ids = new ArrayList(3); + ids.add("1"); + ids.add("3"); + ids.add("4"); + + List> items = node.getItems(ids); + assertEquals(3, items.size()); + assertEquals("1", items.get(0).getId()); + assertXMLEqual("", items.get(0).getPayload().toXML()); + assertEquals( "3", items.get(1).getId()); + assertXMLEqual("", items.get(1).getPayload().toXML()); + assertEquals("4", items.get(2).getId()); + assertXMLEqual("", items.get(2).getPayload().toXML()); + } + + public void testGetLastNItems() throws XMPPException + { + LeafNode node = getPubnode(true, false); + + node.send(new Item("1")); + node.send(new Item("2")); + node.send(new Item("3")); + node.send(new Item("4")); + node.send(new Item("5")); + + List items = node.getItems(2); + assertEquals(2, items.size()); + assertTrue(listContainsId("4", items)); + assertTrue(listContainsId("5", items)); + } + + private static boolean listContainsId(String id, List items) + { + for (Item item : items) + { + if (item.getId().equals(id)) + return true; + } + return false; + } + + private String getJid() + { + return getConnection(0).getUser(); + } + +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/TestAPI.java b/integration-test/org/jivesoftware/smackx/pubsub/TestAPI.java index 30793751f..cb71b10f9 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/TestAPI.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/TestAPI.java @@ -14,27 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; +package org.jivesoftware.smackx.pubsub; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.pubsub.test.SingleUserTestCase; /** * * @author Robin Collier * - */ -public class TestAPI extends SingleUserTestCase -{ - public void testGetNonexistentNode() - { - try - { - getManager().getNode("" + System.currentTimeMillis()); - assertTrue(false); - } - catch (XMPPException e) - { - } - } -} + */ +public class TestAPI extends SingleUserTestCase +{ + public void testGetNonexistentNode() + { + try + { + getManager().getNode("" + System.currentTimeMillis()); + assertTrue(false); + } + catch (XMPPException e) + { + } + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/TestEvents.java b/integration-test/org/jivesoftware/smackx/pubsub/TestEvents.java index 1878bed0a..7d68d3c0a 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/TestEvents.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/TestEvents.java @@ -14,585 +14,585 @@ * 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.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.XMPPError.Type; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener; -import org.jivesoftware.smackx.pubsub.listener.ItemEventListener; -import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener; +package org.jivesoftware.smackx.pubsub; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError.Type; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener; +import org.jivesoftware.smackx.pubsub.listener.ItemEventListener; +import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener; /** * * @author Robin Collier * - */ -public class TestEvents extends SmackTestCase -{ - - public TestEvents(String str) - { - super(str); - } - - @Override - protected int getMaxConnections() - { - return 2; - } - - private String getService() - { - return "pubsub." + getServiceName(); - } - - public void testCreateAndGetNode() throws Exception - { - String nodeId = "MyTestNode"; - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = null; - try - { - creatorNode = (LeafNode)creatorMgr.getNode(nodeId); - } - catch (XMPPException e) - { - if (e.getXMPPError().getType() == Type.CANCEL && e.getXMPPError().getCondition().equals("item-not-found")) - creatorNode = creatorMgr.createNode(nodeId); - else - throw e; - } - - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - assertNotNull(subNode); - } - - public void testConfigureAndNotify() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, false, true); - - BlockingQueue queue = new ArrayBlockingQueue(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - NodeConfigListener sub1Handler = new NodeConfigCoordinator(queue, "sub1"); - subNode.subscribe(getConnection(1).getUser()); - subNode.addConfigurationListener(sub1Handler); - - ConfigureForm currentConfig = creatorNode.getNodeConfiguration(); - ConfigureForm form = new ConfigureForm(currentConfig.createAnswerForm()); - form.setPersistentItems(true); - form.setDeliverPayloads(false); - form.setNotifyConfig(true); - creatorNode.sendConfigurationForm(form); - - ConfigurationEvent event = queue.poll(5, TimeUnit.SECONDS).event; - assertEquals(nodeId, event.getNode()); - assertNull(event.getConfiguration()); - - currentConfig = creatorNode.getNodeConfiguration(); - form = new ConfigureForm(currentConfig.createAnswerForm()); - form.setDeliverPayloads(true); - creatorNode.sendConfigurationForm(form); - - event = queue.poll(5, TimeUnit.SECONDS).event; - assertEquals(nodeId, event.getNode()); - assertNotNull(event.getConfiguration()); - assertTrue(event.getConfiguration().isPersistItems()); - assertTrue(event.getConfiguration().isDeliverPayloads()); - } - - public void testSendAndReceiveNoPayload() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - BlockingQueue> queue = new ArrayBlockingQueue>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - creatorNode.send(new Item(itemId)); - + */ +public class TestEvents extends SmackTestCase +{ + + public TestEvents(String str) + { + super(str); + } + + @Override + protected int getMaxConnections() + { + return 2; + } + + private String getService() + { + return "pubsub." + getServiceName(); + } + + public void testCreateAndGetNode() throws Exception + { + String nodeId = "MyTestNode"; + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = null; + try + { + creatorNode = (LeafNode)creatorMgr.getNode(nodeId); + } + catch (XMPPException e) + { + if (e.getXMPPError().getType() == Type.CANCEL && e.getXMPPError().getCondition().equals("item-not-found")) + creatorNode = creatorMgr.createNode(nodeId); + else + throw e; + } + + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + assertNotNull(subNode); + } + + public void testConfigureAndNotify() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, false, true); + + BlockingQueue queue = new ArrayBlockingQueue(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + NodeConfigListener sub1Handler = new NodeConfigCoordinator(queue, "sub1"); + subNode.subscribe(getConnection(1).getUser()); + subNode.addConfigurationListener(sub1Handler); + + ConfigureForm currentConfig = creatorNode.getNodeConfiguration(); + ConfigureForm form = new ConfigureForm(currentConfig.createAnswerForm()); + form.setPersistentItems(true); + form.setDeliverPayloads(false); + form.setNotifyConfig(true); + creatorNode.sendConfigurationForm(form); + + ConfigurationEvent event = queue.poll(5, TimeUnit.SECONDS).event; + assertEquals(nodeId, event.getNode()); + assertNull(event.getConfiguration()); + + currentConfig = creatorNode.getNodeConfiguration(); + form = new ConfigureForm(currentConfig.createAnswerForm()); + form.setDeliverPayloads(true); + creatorNode.sendConfigurationForm(form); + + event = queue.poll(5, TimeUnit.SECONDS).event; + assertEquals(nodeId, event.getNode()); + assertNotNull(event.getConfiguration()); + assertTrue(event.getConfiguration().isPersistItems()); + assertTrue(event.getConfiguration().isDeliverPayloads()); + } + + public void testSendAndReceiveNoPayload() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + BlockingQueue> queue = new ArrayBlockingQueue>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + creatorNode.send(new Item(itemId)); + ItemEventCoordinator coord = queue.poll(5, TimeUnit.SECONDS); - assertEquals(1, coord.events.getItems().size()); - assertEquals(itemId, coord.events.getItems().iterator().next().getId()); - } - - public void testPublishAndReceiveNoPayload() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - BlockingQueue> queue = new ArrayBlockingQueue>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - creatorNode.publish(new Item(itemId)); - - ItemEventCoordinator coord = queue.poll(5, TimeUnit.SECONDS); - assertEquals(1, coord.events.getItems().size()); - assertEquals(itemId, coord.events.getItems().get(0).getId()); - } - - public void testSendAndReceiveSimplePayload() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); - - BlockingQueue>> queue = new ArrayBlockingQueue>>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - String payloadString = "Sir Arthur Conan Doyle"; - creatorNode.send(new PayloadItem(itemId, new SimplePayload("book", "pubsub:test:book", payloadString))); - - ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); - assertEquals(1, coord.events.getItems().size()); - PayloadItem item = coord.events.getItems().get(0); - assertEquals(itemId, item.getId()); - assertTrue(item.getPayload() instanceof SimplePayload); - assertEquals(payloadString, item.getPayload().toXML()); - assertEquals("book", item.getPayload().getElementName()); - } - - /* - * For this test, the following extension needs to be added to the meta-inf/smack.providers file - * - * - * car - * pubsub:test:vehicle - * org.jivesoftware.smackx.pubsub.CarExtensionProvider - * - */ - /* - public void testSendAndReceiveCarPayload() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); - - BlockingQueue>> queue = new ArrayBlockingQueue>>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - Node subNode = subMgr.getNode(nodeId); - - ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - String payloadString = ""; - creatorNode.send(new PayloadItem(itemId, new SimplePayload("car", "pubsub:test:vehicle", payloadString))); - - ItemEventCoordinator> coord = queue.take(); - assertEquals(1, coord.events.getItems().size()); - PayloadItem item = coord.events.getItems().get(0); - assertEquals(itemId, item.getId()); - assertTrue(item.getPayload() instanceof CarExtension); - - CarExtension car = (CarExtension)item.getPayload(); - assertEquals("green", car.getColor()); - assertEquals(4, car.getNumTires()); - } -*/ - - public void testSendAndReceiveMultipleSubs() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - BlockingQueue> queue = new ArrayBlockingQueue>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - ItemEventCoordinator sub2Handler = new ItemEventCoordinator(queue, "sub2"); - subNode.addItemEventListener(sub2Handler); - Subscription sub2 = subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - creatorNode.send(new Item(itemId)); - - for(int i=0; i<2; i++) - { + assertEquals(1, coord.events.getItems().size()); + assertEquals(itemId, coord.events.getItems().iterator().next().getId()); + } + + public void testPublishAndReceiveNoPayload() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + BlockingQueue> queue = new ArrayBlockingQueue>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + creatorNode.publish(new Item(itemId)); + + ItemEventCoordinator coord = queue.poll(5, TimeUnit.SECONDS); + assertEquals(1, coord.events.getItems().size()); + assertEquals(itemId, coord.events.getItems().get(0).getId()); + } + + public void testSendAndReceiveSimplePayload() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); + + BlockingQueue>> queue = new ArrayBlockingQueue>>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + String payloadString = "Sir Arthur Conan Doyle"; + creatorNode.send(new PayloadItem(itemId, new SimplePayload("book", "pubsub:test:book", payloadString))); + + ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); + assertEquals(1, coord.events.getItems().size()); + PayloadItem item = coord.events.getItems().get(0); + assertEquals(itemId, item.getId()); + assertTrue(item.getPayload() instanceof SimplePayload); + assertEquals(payloadString, item.getPayload().toXML()); + assertEquals("book", item.getPayload().getElementName()); + } + + /* + * For this test, the following extension needs to be added to the meta-inf/smack.providers file + * + * + * car + * pubsub:test:vehicle + * org.jivesoftware.smackx.pubsub.CarExtensionProvider + * + */ + /* + public void testSendAndReceiveCarPayload() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); + + BlockingQueue>> queue = new ArrayBlockingQueue>>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + Node subNode = subMgr.getNode(nodeId); + + ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + String payloadString = ""; + creatorNode.send(new PayloadItem(itemId, new SimplePayload("car", "pubsub:test:vehicle", payloadString))); + + ItemEventCoordinator> coord = queue.take(); + assertEquals(1, coord.events.getItems().size()); + PayloadItem item = coord.events.getItems().get(0); + assertEquals(itemId, item.getId()); + assertTrue(item.getPayload() instanceof CarExtension); + + CarExtension car = (CarExtension)item.getPayload(); + assertEquals("green", car.getColor()); + assertEquals(4, car.getNumTires()); + } +*/ + + public void testSendAndReceiveMultipleSubs() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + BlockingQueue> queue = new ArrayBlockingQueue>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator sub1Handler = new ItemEventCoordinator(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + ItemEventCoordinator sub2Handler = new ItemEventCoordinator(queue, "sub2"); + subNode.addItemEventListener(sub2Handler); + Subscription sub2 = subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + creatorNode.send(new Item(itemId)); + + for(int i=0; i<2; i++) + { ItemEventCoordinator coord = queue.poll(10, TimeUnit.SECONDS); if (coord == null) - fail(); - assertEquals(1, coord.events.getItems().size()); - assertEquals(itemId, coord.events.getItems().iterator().next().getId()); - - if (coord.id.equals("sub1") || coord.id.equals("sub2")) - { - assertEquals(2, coord.events.getSubscriptions().size()); - } - } - } - - public void testSendAndReceiveMultipleItems() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); - - BlockingQueue>> queue = new ArrayBlockingQueue>>(3); - ItemEventCoordinator> creatorHandler = new ItemEventCoordinator>(queue, "creator"); - creatorNode.addItemEventListener(creatorHandler); - creatorNode.subscribe(getConnection(0).getUser()); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - ItemEventCoordinator> sub2Handler = new ItemEventCoordinator>(queue, "sub2"); - subNode.addItemEventListener(sub2Handler); - Subscription sub2 = subNode.subscribe(getConnection(1).getUser()); - - assertEquals(Subscription.State.subscribed, sub1.getState()); - assertEquals(Subscription.State.subscribed, sub2.getState()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - - Collection> items = new ArrayList>(3); - String ids[] = {"First-" + itemId, "Second-" + itemId, "Third-" + itemId}; - items.add(new PayloadItem(ids[0], new SimplePayload("a", "pubsub:test", ""))); - items.add(new PayloadItem(ids[1], new SimplePayload("a", "pubsub:test", ""))); - items.add(new PayloadItem(ids[2], new SimplePayload("a", "pubsub:test", ""))); - creatorNode.send(items); - - for(int i=0; i<3; i++) - { - ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); - if (coord == creatorHandler) - assertEquals(1, coord.events.getSubscriptions().size()); - else - assertEquals(2, coord.events.getSubscriptions().size()); - List> itemResults = coord.events.getItems(); - assertEquals(3, itemResults.size()); - -// assertEquals(ids[0], itemResults.get(0).getId()); -// assertEquals("", itemResults.get(0).getPayload().toXML().replace('\"', '\'')); -// assertEquals(ids[1], itemResults.get(1).getId()); -// assertEquals("", itemResults.get(1).getPayload().toXML().replace('\"', '\'')); -// assertEquals(ids[21], itemResults.get(2).getId()); -// assertEquals("", itemResults.get(3).getPayload().toXML().replace('\"', '\'')); - } - } - - public void testSendAndReceiveDelayed() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - // Send event - String itemId = String.valueOf("DelayId-" + System.currentTimeMillis()); - String payloadString = "Sir Arthur Conan Doyle"; - creatorNode.send(new PayloadItem(itemId, new SimplePayload("book", "pubsub:test:book", payloadString))); - - Thread.sleep(1000); - - BlockingQueue>> queue = new ArrayBlockingQueue>>(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); - subNode.addItemEventListener(sub1Handler); - Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); - - ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); - assertTrue(coord.events.isDelayed()); - assertNotNull(coord.events.getPublishedDate()); - } - - public void testDeleteItemAndNotify() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - BlockingQueue queue = new ArrayBlockingQueue(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1"); - subNode.addItemDeleteListener(sub1Handler); - subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - - Collection items = new ArrayList(3); - String id1 = "First-" + itemId; - String id2 = "Second-" + itemId; - String id3 = "Third-" + itemId; - items.add(new Item(id1)); - items.add(new Item(id2)); - items.add(new Item(id3)); - creatorNode.send(items); - - creatorNode.deleteItem(id1); - - ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS); - assertEquals(1, coord.event.getItemIds().size()); - assertEquals(id1, coord.event.getItemIds().get(0)); - - creatorNode.deleteItem(Arrays.asList(id2, id3)); - - coord = queue.poll(5, TimeUnit.SECONDS); - assertEquals(2, coord.event.getItemIds().size()); - assertTrue(coord.event.getItemIds().contains(id2)); - assertTrue(coord.event.getItemIds().contains(id3)); - } - - public void testPurgeAndNotify() throws Exception - { - // Setup event source - String nodeId = "TestNode" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - - LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); - - BlockingQueue queue = new ArrayBlockingQueue(3); - - // Setup event receiver - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); - - ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1"); - subNode.addItemDeleteListener(sub1Handler); - subNode.subscribe(getConnection(1).getUser()); - - // Send event - String itemId = String.valueOf(System.currentTimeMillis()); - - Collection items = new ArrayList(3); - String id1 = "First-" + itemId; - String id2 = "Second-" + itemId; - String id3 = "Third-" + itemId; - items.add(new Item(id1)); - items.add(new Item(id2)); - items.add(new Item(id3)); - creatorNode.send(items); - - creatorNode.deleteAllItems(); - - ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS); - assertNull(nodeId, coord.event); - } - - public void testListenerMultipleNodes() throws Exception - { - // Setup event source - String nodeId1 = "Node-1-" + System.currentTimeMillis(); - PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); - String nodeId2 = "Node-2-" + System.currentTimeMillis(); - - LeafNode creatorNode1 = getPubnode(creatorMgr, nodeId1, true, false); - LeafNode creatorNode2 = getPubnode(creatorMgr, nodeId2, true, false); - - BlockingQueue> queue = new ArrayBlockingQueue>(3); - - PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); - LeafNode subNode1 = (LeafNode)subMgr.getNode(nodeId1); - LeafNode subNode2 = (LeafNode)subMgr.getNode(nodeId2); - - subNode1.addItemEventListener(new ItemEventCoordinator(queue, "sub1")); - subNode2.addItemEventListener(new ItemEventCoordinator(queue, "sub2")); - - subNode1.subscribe(getConnection(1).getUser()); - subNode2.subscribe(getConnection(1).getUser()); - - creatorNode1.send(new Item("item1")); - creatorNode2.send(new Item("item2")); - boolean check1 = false; - boolean check2 = false; - - for (int i=0; i<2; i++) - { - ItemEventCoordinator event = queue.poll(5, TimeUnit.SECONDS); - - if (event.id.equals("sub1")) - { - assertEquals(event.events.getNodeId(), nodeId1); - check1 = true; - } - else - { - assertEquals(event.events.getNodeId(), nodeId2); - check2 = true; - } - } - assertTrue(check1); - assertTrue(check2); - } - - class ItemEventCoordinator implements ItemEventListener - { - private BlockingQueue> theQueue; - private ItemPublishEvent events; - private String id; - - ItemEventCoordinator(BlockingQueue> queue, String id) - { - theQueue = queue; - this.id = id; - } - - public void handlePublishedItems(ItemPublishEvent items) + fail(); + assertEquals(1, coord.events.getItems().size()); + assertEquals(itemId, coord.events.getItems().iterator().next().getId()); + + if (coord.id.equals("sub1") || coord.id.equals("sub2")) + { + assertEquals(2, coord.events.getSubscriptions().size()); + } + } + } + + public void testSendAndReceiveMultipleItems() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, true); + + BlockingQueue>> queue = new ArrayBlockingQueue>>(3); + ItemEventCoordinator> creatorHandler = new ItemEventCoordinator>(queue, "creator"); + creatorNode.addItemEventListener(creatorHandler); + creatorNode.subscribe(getConnection(0).getUser()); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + ItemEventCoordinator> sub2Handler = new ItemEventCoordinator>(queue, "sub2"); + subNode.addItemEventListener(sub2Handler); + Subscription sub2 = subNode.subscribe(getConnection(1).getUser()); + + assertEquals(Subscription.State.subscribed, sub1.getState()); + assertEquals(Subscription.State.subscribed, sub2.getState()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + + Collection> items = new ArrayList>(3); + String ids[] = {"First-" + itemId, "Second-" + itemId, "Third-" + itemId}; + items.add(new PayloadItem(ids[0], new SimplePayload("a", "pubsub:test", ""))); + items.add(new PayloadItem(ids[1], new SimplePayload("a", "pubsub:test", ""))); + items.add(new PayloadItem(ids[2], new SimplePayload("a", "pubsub:test", ""))); + creatorNode.send(items); + + for(int i=0; i<3; i++) + { + ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); + if (coord == creatorHandler) + assertEquals(1, coord.events.getSubscriptions().size()); + else + assertEquals(2, coord.events.getSubscriptions().size()); + List> itemResults = coord.events.getItems(); + assertEquals(3, itemResults.size()); + +// assertEquals(ids[0], itemResults.get(0).getId()); +// assertEquals("", itemResults.get(0).getPayload().toXML().replace('\"', '\'')); +// assertEquals(ids[1], itemResults.get(1).getId()); +// assertEquals("", itemResults.get(1).getPayload().toXML().replace('\"', '\'')); +// assertEquals(ids[21], itemResults.get(2).getId()); +// assertEquals("", itemResults.get(3).getPayload().toXML().replace('\"', '\'')); + } + } + + public void testSendAndReceiveDelayed() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + // Send event + String itemId = String.valueOf("DelayId-" + System.currentTimeMillis()); + String payloadString = "Sir Arthur Conan Doyle"; + creatorNode.send(new PayloadItem(itemId, new SimplePayload("book", "pubsub:test:book", payloadString))); + + Thread.sleep(1000); + + BlockingQueue>> queue = new ArrayBlockingQueue>>(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemEventCoordinator> sub1Handler = new ItemEventCoordinator>(queue, "sub1"); + subNode.addItemEventListener(sub1Handler); + Subscription sub1 = subNode.subscribe(getConnection(1).getUser()); + + ItemEventCoordinator> coord = queue.poll(5, TimeUnit.SECONDS); + assertTrue(coord.events.isDelayed()); + assertNotNull(coord.events.getPublishedDate()); + } + + public void testDeleteItemAndNotify() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + BlockingQueue queue = new ArrayBlockingQueue(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1"); + subNode.addItemDeleteListener(sub1Handler); + subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + + Collection items = new ArrayList(3); + String id1 = "First-" + itemId; + String id2 = "Second-" + itemId; + String id3 = "Third-" + itemId; + items.add(new Item(id1)); + items.add(new Item(id2)); + items.add(new Item(id3)); + creatorNode.send(items); + + creatorNode.deleteItem(id1); + + ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS); + assertEquals(1, coord.event.getItemIds().size()); + assertEquals(id1, coord.event.getItemIds().get(0)); + + creatorNode.deleteItem(Arrays.asList(id2, id3)); + + coord = queue.poll(5, TimeUnit.SECONDS); + assertEquals(2, coord.event.getItemIds().size()); + assertTrue(coord.event.getItemIds().contains(id2)); + assertTrue(coord.event.getItemIds().contains(id3)); + } + + public void testPurgeAndNotify() throws Exception + { + // Setup event source + String nodeId = "TestNode" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + + LeafNode creatorNode = getPubnode(creatorMgr, nodeId, true, false); + + BlockingQueue queue = new ArrayBlockingQueue(3); + + // Setup event receiver + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode = (LeafNode)subMgr.getNode(nodeId); + + ItemDeleteCoordinator sub1Handler = new ItemDeleteCoordinator(queue, "sub1"); + subNode.addItemDeleteListener(sub1Handler); + subNode.subscribe(getConnection(1).getUser()); + + // Send event + String itemId = String.valueOf(System.currentTimeMillis()); + + Collection items = new ArrayList(3); + String id1 = "First-" + itemId; + String id2 = "Second-" + itemId; + String id3 = "Third-" + itemId; + items.add(new Item(id1)); + items.add(new Item(id2)); + items.add(new Item(id3)); + creatorNode.send(items); + + creatorNode.deleteAllItems(); + + ItemDeleteCoordinator coord = queue.poll(5, TimeUnit.SECONDS); + assertNull(nodeId, coord.event); + } + + public void testListenerMultipleNodes() throws Exception + { + // Setup event source + String nodeId1 = "Node-1-" + System.currentTimeMillis(); + PubSubManager creatorMgr = new PubSubManager(getConnection(0), getService()); + String nodeId2 = "Node-2-" + System.currentTimeMillis(); + + LeafNode creatorNode1 = getPubnode(creatorMgr, nodeId1, true, false); + LeafNode creatorNode2 = getPubnode(creatorMgr, nodeId2, true, false); + + BlockingQueue> queue = new ArrayBlockingQueue>(3); + + PubSubManager subMgr = new PubSubManager(getConnection(1), getService()); + LeafNode subNode1 = (LeafNode)subMgr.getNode(nodeId1); + LeafNode subNode2 = (LeafNode)subMgr.getNode(nodeId2); + + subNode1.addItemEventListener(new ItemEventCoordinator(queue, "sub1")); + subNode2.addItemEventListener(new ItemEventCoordinator(queue, "sub2")); + + subNode1.subscribe(getConnection(1).getUser()); + subNode2.subscribe(getConnection(1).getUser()); + + creatorNode1.send(new Item("item1")); + creatorNode2.send(new Item("item2")); + boolean check1 = false; + boolean check2 = false; + + for (int i=0; i<2; i++) { - events = items; - theQueue.add(this); - } - - @Override - public String toString() - { - return "ItemEventCoordinator: " + id; - } - - } - - class NodeConfigCoordinator implements NodeConfigListener - { - private BlockingQueue theQueue; - private String id; - private ConfigurationEvent event; - - NodeConfigCoordinator(BlockingQueue queue, String id) - { - theQueue = queue; - this.id = id; - } - - public void handleNodeConfiguration(ConfigurationEvent config) - { - event = config; - theQueue.add(this); - } - - @Override - public String toString() - { - return "NodeConfigCoordinator: " + id; - } - - } - - class ItemDeleteCoordinator implements ItemDeleteListener - { - private BlockingQueue theQueue; - private String id; - private ItemDeleteEvent event; - - ItemDeleteCoordinator(BlockingQueue queue, String id) - { - theQueue = queue; - this.id = id; - } - - public void handleDeletedItems(ItemDeleteEvent delEvent) - { - event = delEvent; - theQueue.add(this); - } - - - public void handlePurge() - { - event = null; - theQueue.add(this); - } - - @Override - public String toString() - { - return "ItemDeleteCoordinator: " + id; - } - } - - static private LeafNode getPubnode(PubSubManager manager, String id, boolean persistItems, boolean deliverPayload) - throws XMPPException - { - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setPersistentItems(persistItems); - form.setDeliverPayloads(deliverPayload); - form.setAccessModel(AccessModel.open); - return (LeafNode)manager.createNode(id, form); - } - -} + ItemEventCoordinator event = queue.poll(5, TimeUnit.SECONDS); + + if (event.id.equals("sub1")) + { + assertEquals(event.events.getNodeId(), nodeId1); + check1 = true; + } + else + { + assertEquals(event.events.getNodeId(), nodeId2); + check2 = true; + } + } + assertTrue(check1); + assertTrue(check2); + } + + class ItemEventCoordinator implements ItemEventListener + { + private BlockingQueue> theQueue; + private ItemPublishEvent events; + private String id; + + ItemEventCoordinator(BlockingQueue> queue, String id) + { + theQueue = queue; + this.id = id; + } + + public void handlePublishedItems(ItemPublishEvent items) + { + events = items; + theQueue.add(this); + } + + @Override + public String toString() + { + return "ItemEventCoordinator: " + id; + } + + } + + class NodeConfigCoordinator implements NodeConfigListener + { + private BlockingQueue theQueue; + private String id; + private ConfigurationEvent event; + + NodeConfigCoordinator(BlockingQueue queue, String id) + { + theQueue = queue; + this.id = id; + } + + public void handleNodeConfiguration(ConfigurationEvent config) + { + event = config; + theQueue.add(this); + } + + @Override + public String toString() + { + return "NodeConfigCoordinator: " + id; + } + + } + + class ItemDeleteCoordinator implements ItemDeleteListener + { + private BlockingQueue theQueue; + private String id; + private ItemDeleteEvent event; + + ItemDeleteCoordinator(BlockingQueue queue, String id) + { + theQueue = queue; + this.id = id; + } + + public void handleDeletedItems(ItemDeleteEvent delEvent) + { + event = delEvent; + theQueue.add(this); + } + + + public void handlePurge() + { + event = null; + theQueue.add(this); + } + + @Override + public String toString() + { + return "ItemDeleteCoordinator: " + id; + } + } + + static private LeafNode getPubnode(PubSubManager manager, String id, boolean persistItems, boolean deliverPayload) + throws XMPPException + { + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setPersistentItems(persistItems); + form.setDeliverPayloads(deliverPayload); + form.setAccessModel(AccessModel.open); + return (LeafNode)manager.createNode(id, form); + } + +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/TestMessageContent.java b/integration-test/org/jivesoftware/smackx/pubsub/TestMessageContent.java index 8e7ff7c48..39cbb3e5e 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/TestMessageContent.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/TestMessageContent.java @@ -14,121 +14,121 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub; - -import junit.framework.TestCase; +package org.jivesoftware.smackx.pubsub; + +import junit.framework.TestCase; /** * * @author Robin Collier * - */ -public class TestMessageContent extends TestCase -{ - String payloadXmlWithNS = ""; - - public void testItemWithId() - { - Item item = new Item("123"); - assertEquals("", item.toXML()); - assertEquals("item", item.getElementName()); - assertNull(item.getNamespace()); - } - - public void testItemWithNoId() - { - Item item = new Item(); - assertEquals("", item.toXML()); - - Item itemNull = new Item(null); - assertEquals("", itemNull.toXML()); - } - - public void testSimplePayload() - { - SimplePayload payloadNS = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); - - assertEquals(payloadXmlWithNS, payloadNS.toXML()); - - String payloadXmlWithNoNS = ""; - SimplePayload payloadNoNS = new SimplePayload("book", null, ""); - - assertEquals(payloadXmlWithNoNS, payloadNoNS.toXML()); - - } - - public void testPayloadItemWithId() - { - SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); - PayloadItem item = new PayloadItem("123", payload); - - String xml = "" + payloadXmlWithNS + ""; - assertEquals(xml, item.toXML()); - assertEquals("item", item.getElementName()); - } - - public void testPayloadItemWithNoId() - { - SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); - PayloadItem item = new PayloadItem(null, payload); - - String xml = "" + payloadXmlWithNS + ""; - assertEquals(xml, item.toXML()); - } - - public void testPayloadItemWithIdNoPayload() - { - try - { - PayloadItem item = new PayloadItem("123", null); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - } - } - - public void testPayloadItemWithNoIdNoPayload() - { - try - { - PayloadItem item = new PayloadItem(null, null); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - } - } - - public void testRetractItem() - { - RetractItem item = new RetractItem("1234"); - - assertEquals("", item.toXML()); - assertEquals("retract", item.getElementName()); - - try - { - new RetractItem(null); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - } - } - - public void testGetItemsRequest() - { - GetItemsRequest request = new GetItemsRequest("testId"); - assertEquals("", request.toXML()); - - request = new GetItemsRequest("testId", 5); - assertEquals("", request.toXML()); - - request = new GetItemsRequest("testId", "qwerty"); - assertEquals("", request.toXML()); - - request = new GetItemsRequest("testId", "qwerty", 5); - assertEquals("", request.toXML()); - } -} + */ +public class TestMessageContent extends TestCase +{ + String payloadXmlWithNS = ""; + + public void testItemWithId() + { + Item item = new Item("123"); + assertEquals("", item.toXML()); + assertEquals("item", item.getElementName()); + assertNull(item.getNamespace()); + } + + public void testItemWithNoId() + { + Item item = new Item(); + assertEquals("", item.toXML()); + + Item itemNull = new Item(null); + assertEquals("", itemNull.toXML()); + } + + public void testSimplePayload() + { + SimplePayload payloadNS = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); + + assertEquals(payloadXmlWithNS, payloadNS.toXML()); + + String payloadXmlWithNoNS = ""; + SimplePayload payloadNoNS = new SimplePayload("book", null, ""); + + assertEquals(payloadXmlWithNoNS, payloadNoNS.toXML()); + + } + + public void testPayloadItemWithId() + { + SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); + PayloadItem item = new PayloadItem("123", payload); + + String xml = "" + payloadXmlWithNS + ""; + assertEquals(xml, item.toXML()); + assertEquals("item", item.getElementName()); + } + + public void testPayloadItemWithNoId() + { + SimplePayload payload = new SimplePayload("book", "pubsub:test:book", payloadXmlWithNS); + PayloadItem item = new PayloadItem(null, payload); + + String xml = "" + payloadXmlWithNS + ""; + assertEquals(xml, item.toXML()); + } + + public void testPayloadItemWithIdNoPayload() + { + try + { + PayloadItem item = new PayloadItem("123", null); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + } + } + + public void testPayloadItemWithNoIdNoPayload() + { + try + { + PayloadItem item = new PayloadItem(null, null); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + } + } + + public void testRetractItem() + { + RetractItem item = new RetractItem("1234"); + + assertEquals("", item.toXML()); + assertEquals("retract", item.getElementName()); + + try + { + new RetractItem(null); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + } + } + + public void testGetItemsRequest() + { + GetItemsRequest request = new GetItemsRequest("testId"); + assertEquals("", request.toXML()); + + request = new GetItemsRequest("testId", 5); + assertEquals("", request.toXML()); + + request = new GetItemsRequest("testId", "qwerty"); + assertEquals("", request.toXML()); + + request = new GetItemsRequest("testId", "qwerty", 5); + assertEquals("", request.toXML()); + } +} diff --git a/integration-test/org/jivesoftware/smackx/pubsub/test/PubSubTestCase.java b/integration-test/org/jivesoftware/smackx/pubsub/test/PubSubTestCase.java index 262057e43..28c9445ae 100644 --- a/integration-test/org/jivesoftware/smackx/pubsub/test/PubSubTestCase.java +++ b/integration-test/org/jivesoftware/smackx/pubsub/test/PubSubTestCase.java @@ -14,79 +14,79 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.pubsub.test; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.pubsub.AccessModel; -import org.jivesoftware.smackx.pubsub.ConfigureForm; -import org.jivesoftware.smackx.pubsub.FormType; -import org.jivesoftware.smackx.pubsub.LeafNode; -import org.jivesoftware.smackx.pubsub.PubSubManager; +package org.jivesoftware.smackx.pubsub.test; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.pubsub.AccessModel; +import org.jivesoftware.smackx.pubsub.ConfigureForm; +import org.jivesoftware.smackx.pubsub.FormType; +import org.jivesoftware.smackx.pubsub.LeafNode; +import org.jivesoftware.smackx.pubsub.PubSubManager; /** * * @author Robin Collier * - */ -abstract public class PubSubTestCase extends SmackTestCase -{ - private PubSubManager[] manager; - - public PubSubTestCase(String arg0) - { - super(arg0); - } - - public PubSubTestCase() - { - super("PubSub Test Case"); - } - - protected LeafNode getRandomPubnode(PubSubManager pubMgr, boolean persistItems, boolean deliverPayload) throws XMPPException - { - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setPersistentItems(persistItems); - form.setDeliverPayloads(deliverPayload); - form.setAccessModel(AccessModel.open); - return (LeafNode)pubMgr.createNode("/test/Pubnode" + System.currentTimeMillis(), form); - } - - protected LeafNode getPubnode(PubSubManager pubMgr, boolean persistItems, boolean deliverPayload, String nodeId) throws XMPPException - { - LeafNode node = null; - - try - { - node = (LeafNode)pubMgr.getNode(nodeId); - } - catch (XMPPException e) - { - ConfigureForm form = new ConfigureForm(FormType.submit); - form.setPersistentItems(persistItems); - form.setDeliverPayloads(deliverPayload); - form.setAccessModel(AccessModel.open); - node = (LeafNode)pubMgr.createNode(nodeId, form); - } - return node; - } - - protected PubSubManager getManager(int idx) - { - if (manager == null) - { - manager = new PubSubManager[getMaxConnections()]; - - for(int i=0; i - * The goal of this class is to provide a flexible way to make JingleManager control jmf streaming APIs without implement them. - * For instance you can implement a file transfer using java sockets or a VOIP Media Manager using JMF. - * You can implement many JingleMediaManager according to you necessity. - * - * @author Thiago Camargo - */ -public abstract class JingleMediaManager { - - public static final String MEDIA_NAME = "JingleMediaManager"; - - // Each media manager must keep track of the transport manager that it uses. - private JingleTransportManager transportManager; - - public JingleMediaManager(JingleTransportManager transportManager) { - this.transportManager = transportManager; - } - - /** - * Return The transport manager that goes with this media manager. - */ - public JingleTransportManager getTransportManager() { - return transportManager; - } - - /** - * Return all supported Payloads for this Manager - * - * @return The Payload List - */ - public abstract List getPayloads(); - - /** - * Returns the Preferred PayloadType of the Media Manager - * - * @return The PayloadType - */ - public PayloadType getPreferredPayloadType() { - return getPayloads().size() > 0 ? getPayloads().get(0) : null; - } - - /** - * Create a Media Session Implementation - * - * @param payloadType - * @param remote - * @param local - * @return - */ - public abstract JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, - final TransportCandidate local, JingleSession jingleSession); - - // This is to set the attributes of the element of the Jingle packet. - public String getName() { - return MEDIA_NAME; - } - -} \ No newline at end of file +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.media; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +import java.util.List; + +/** + * This class provides necessary Jingle Session jmf methods and behavior. + *

    + * The goal of this class is to provide a flexible way to make JingleManager control jmf streaming APIs without implement them. + * For instance you can implement a file transfer using java sockets or a VOIP Media Manager using JMF. + * You can implement many JingleMediaManager according to you necessity. + * + * @author Thiago Camargo + */ +public abstract class JingleMediaManager { + + public static final String MEDIA_NAME = "JingleMediaManager"; + + // Each media manager must keep track of the transport manager that it uses. + private JingleTransportManager transportManager; + + public JingleMediaManager(JingleTransportManager transportManager) { + this.transportManager = transportManager; + } + + /** + * Return The transport manager that goes with this media manager. + */ + public JingleTransportManager getTransportManager() { + return transportManager; + } + + /** + * Return all supported Payloads for this Manager + * + * @return The Payload List + */ + public abstract List getPayloads(); + + /** + * Returns the Preferred PayloadType of the Media Manager + * + * @return The PayloadType + */ + public PayloadType getPreferredPayloadType() { + return getPayloads().size() > 0 ? getPayloads().get(0) : null; + } + + /** + * Create a Media Session Implementation + * + * @param payloadType + * @param remote + * @param local + * @return + */ + public abstract JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, + final TransportCandidate local, JingleSession jingleSession); + + // This is to set the attributes of the element of the Jingle packet. + public String getName() { + return MEDIA_NAME; + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/JingleMediaSession.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/JingleMediaSession.java index 7f2db459b..2b746ec49 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/JingleMediaSession.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/JingleMediaSession.java @@ -1,188 +1,188 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.media; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; - -import java.util.ArrayList; -import java.util.List; - -/** - * Public Abstract Class provides a clear interface between Media Session and Jingle API. - *

    - * When a Jingle Session is fully stablished, we will have a Payload Type and two transport candidates defined for it. - * Smack Jingle API don't implement Media Transmit and Receive methods. - * But provides an interface to let the user implements it using another API. For instance: JMF. - *

    - * The Class that implements this one, must have the support to transmit and receive the jmf. - * This interface let the user choose his own jmf API. - * - * @author Thiago Camargo - */ -public abstract class JingleMediaSession { - - // Payload Type of the Session - private PayloadType payloadType; - // Local Transport details - private TransportCandidate local; - // Remote Transport details - private TransportCandidate remote; - // Media Locator - private String mediaLocator; - // Media Received Listener - private List mediaReceivedListeners = new ArrayList(); - // Jingle Session - private JingleSession jingleSession; - - /** - * Creates a new JingleMediaSession Instance to handle Media methods. - * - * @param payloadType Payload Type of the transmittion - * @param remote Remote accepted Transport Candidate - * @param local Local accepted Transport Candidate - * @param mediaLocator Media Locator of the capture device - */ - public JingleMediaSession(PayloadType payloadType, TransportCandidate remote, - TransportCandidate local, String mediaLocator, JingleSession jingleSession) { - this.local = local; - this.remote = remote; - this.payloadType = payloadType; - this.mediaLocator = mediaLocator; - this.jingleSession = jingleSession; - } - - /** - * Returns the PayloadType of the Media Session - * - * @return - */ - public PayloadType getPayloadType() { - return payloadType; - } - - /** - * Returns the Media Session local Candidate - * - * @return - */ - public TransportCandidate getLocal() { - return local; - } - - /** - * Returns the Media Session remote Candidate - * - * @return - */ - public TransportCandidate getRemote() { - return remote; - } - - /** - * Return the media locator or null if not defined - * - * @return media locator - */ - public String getMediaLocator() { - return mediaLocator; - } - - /** - * Set the media locator - * - * @param mediaLocator media locator or null to use default - */ - public void setMediaLocator(String mediaLocator) { - this.mediaLocator = mediaLocator; - } - - /** - * Adds a Media Received Listener - * - * @param mediaReceivedListener - */ - public void addMediaReceivedListener(MediaReceivedListener mediaReceivedListener) { - mediaReceivedListeners.add(mediaReceivedListener); - } - - /** - * Removes a Media Received Listener - * - * @param mediaReceivedListener - */ - public void removeMediaReceivedListener(MediaReceivedListener mediaReceivedListener) { - mediaReceivedListeners.remove(mediaReceivedListener); - } - - /** - * Removes all Media Received Listeners - */ - public void removeAllMediaReceivedListener() { - mediaReceivedListeners.clear(); - } - - /** - * Initialize the RTP Channel preparing to transmit and receive. - */ - public abstract void initialize(); - - /** - * Starts a RTP / UDP / TCP Transmission to the remote Candidate - */ - public abstract void startTrasmit(); - - /** - * Starts a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate - */ - public abstract void startReceive(); - - /** - * Set transmit activity. If the active is true, the instance should trasmit. - * If it is set to false, the instance should pause transmit. - * - * @param active - */ - public abstract void setTrasmit(boolean active); - - /** - * Stops a RTP / UDP / TCP Transmission to the remote Candidate - */ - public abstract void stopTrasmit(); - - /** - * Stops a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate - */ - public abstract void stopReceive(); - - /** - * Called when new Media is received. - */ - public void mediaReceived(String participant) { - for (MediaReceivedListener mediaReceivedListener : mediaReceivedListeners) { - mediaReceivedListener.mediaReceived(participant); - } - } - - /** - * Gets associated JingleSession - * @return associated JingleSession - */ - public JingleSession getJingleSession() { - return jingleSession; - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.media; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +import java.util.ArrayList; +import java.util.List; + +/** + * Public Abstract Class provides a clear interface between Media Session and Jingle API. + *

    + * When a Jingle Session is fully stablished, we will have a Payload Type and two transport candidates defined for it. + * Smack Jingle API don't implement Media Transmit and Receive methods. + * But provides an interface to let the user implements it using another API. For instance: JMF. + *

    + * The Class that implements this one, must have the support to transmit and receive the jmf. + * This interface let the user choose his own jmf API. + * + * @author Thiago Camargo + */ +public abstract class JingleMediaSession { + + // Payload Type of the Session + private PayloadType payloadType; + // Local Transport details + private TransportCandidate local; + // Remote Transport details + private TransportCandidate remote; + // Media Locator + private String mediaLocator; + // Media Received Listener + private List mediaReceivedListeners = new ArrayList(); + // Jingle Session + private JingleSession jingleSession; + + /** + * Creates a new JingleMediaSession Instance to handle Media methods. + * + * @param payloadType Payload Type of the transmittion + * @param remote Remote accepted Transport Candidate + * @param local Local accepted Transport Candidate + * @param mediaLocator Media Locator of the capture device + */ + public JingleMediaSession(PayloadType payloadType, TransportCandidate remote, + TransportCandidate local, String mediaLocator, JingleSession jingleSession) { + this.local = local; + this.remote = remote; + this.payloadType = payloadType; + this.mediaLocator = mediaLocator; + this.jingleSession = jingleSession; + } + + /** + * Returns the PayloadType of the Media Session + * + * @return + */ + public PayloadType getPayloadType() { + return payloadType; + } + + /** + * Returns the Media Session local Candidate + * + * @return + */ + public TransportCandidate getLocal() { + return local; + } + + /** + * Returns the Media Session remote Candidate + * + * @return + */ + public TransportCandidate getRemote() { + return remote; + } + + /** + * Return the media locator or null if not defined + * + * @return media locator + */ + public String getMediaLocator() { + return mediaLocator; + } + + /** + * Set the media locator + * + * @param mediaLocator media locator or null to use default + */ + public void setMediaLocator(String mediaLocator) { + this.mediaLocator = mediaLocator; + } + + /** + * Adds a Media Received Listener + * + * @param mediaReceivedListener + */ + public void addMediaReceivedListener(MediaReceivedListener mediaReceivedListener) { + mediaReceivedListeners.add(mediaReceivedListener); + } + + /** + * Removes a Media Received Listener + * + * @param mediaReceivedListener + */ + public void removeMediaReceivedListener(MediaReceivedListener mediaReceivedListener) { + mediaReceivedListeners.remove(mediaReceivedListener); + } + + /** + * Removes all Media Received Listeners + */ + public void removeAllMediaReceivedListener() { + mediaReceivedListeners.clear(); + } + + /** + * Initialize the RTP Channel preparing to transmit and receive. + */ + public abstract void initialize(); + + /** + * Starts a RTP / UDP / TCP Transmission to the remote Candidate + */ + public abstract void startTrasmit(); + + /** + * Starts a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate + */ + public abstract void startReceive(); + + /** + * Set transmit activity. If the active is true, the instance should trasmit. + * If it is set to false, the instance should pause transmit. + * + * @param active + */ + public abstract void setTrasmit(boolean active); + + /** + * Stops a RTP / UDP / TCP Transmission to the remote Candidate + */ + public abstract void stopTrasmit(); + + /** + * Stops a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate + */ + public abstract void stopReceive(); + + /** + * Called when new Media is received. + */ + public void mediaReceived(String participant) { + for (MediaReceivedListener mediaReceivedListener : mediaReceivedListeners) { + mediaReceivedListener.mediaReceived(participant); + } + } + + /** + * Gets associated JingleSession + * @return associated JingleSession + */ + public JingleSession getJingleSession() { + return jingleSession; + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/PayloadType.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/PayloadType.java index 64d459165..3c708466e 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/PayloadType.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/media/PayloadType.java @@ -362,4 +362,4 @@ public class PayloadType { return true; } } -} \ No newline at end of file +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java index e87d7345e..ce0cda3d0 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/JMFInit.java @@ -12,287 +12,287 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.jingle.mediaimpl; - -import java.awt.Frame; -import java.awt.TextArea; -import java.awt.Toolkit; -import java.util.Vector; - -import javax.media.Format; -import javax.media.PlugInManager; -import javax.media.Renderer; -import javax.media.format.AudioFormat; - -import org.jivesoftware.smackx.jingle.SmackLogger; - -import com.sun.media.ExclusiveUse; -import com.sun.media.util.Registry; - -public class JMFInit extends Frame implements Runnable { - - private static final long serialVersionUID = 6476412003260641680L; - - private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class); - - private String tempDir = "/tmp"; - - private boolean done = false; - - private String userHome; - - private boolean visible = false; - - public JMFInit(String[] args, boolean visible) { - super("Initializing JMF..."); - - this.visible = visible; - - Registry.set("secure.allowCaptureFromApplets", true); - Registry.set("secure.allowSaveFileFromApplets", true); - - updateTemp(args); - - try { - Registry.commit(); - } - catch (Exception e) { - - message("Failed to commit to JMFRegistry!"); - } - - Thread detectThread = new Thread(this); - detectThread.run(); - - /* - * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try { - * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { } - * slept += 500; } - * - * if (!done) { console.error("Detection is taking too long! - * Aborting!"); message("Detection is taking too long! Aborting!"); } - * - * try { Thread.currentThread().sleep(2000); } catch - * (InterruptedException ie) { } - */ - } - - public void run() { - detectDirectAudio(); - detectS8DirectAudio(); - detectCaptureDevices(); - done = true; - } - - private void updateTemp(String[] args) { - if (args != null && args.length > 0) { - tempDir = args[0]; - - message("Setting cache directory to " + tempDir); - Registry r = new Registry(); - try { - r.set("secure.cacheDir", tempDir); - r.commit(); - - message("Updated registry"); - } - catch (Exception e) { - message("Couldn't update registry!"); - } - } - } - - private void detectCaptureDevices() { - // check if JavaSound capture is available - message("Looking for Audio capturer"); - Class dsauto; - try { - dsauto = Class.forName("DirectSoundAuto"); - dsauto.newInstance(); - message("Finished detecting DirectSound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - //Do nothing - } - - Class jsauto; - try { - jsauto = Class.forName("JavaSoundAuto"); - jsauto.newInstance(); - message("Finished detecting javasound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - message("JavaSound capturer detection failed!"); - } - - /* - // Check if VFWAuto or SunVideoAuto is available - message("Looking for video capture devices"); - Class auto = null; - Class autoPlus = null; - try { - auto = Class.forName("VFWAuto"); - } - catch (Exception e) { - } - if (auto == null) { - try { - auto = Class.forName("SunVideoAuto"); - } - catch (Exception ee) { - - } - try { - autoPlus = Class.forName("SunVideoPlusAuto"); - } - catch (Exception ee) { - - } - } - if (auto == null) { - try { - auto = Class.forName("V4LAuto"); - } - catch (Exception ee) { - - } - } - try { - Object instance = auto.newInstance(); - if (autoPlus != null) { - Object instancePlus = autoPlus.newInstance(); - } - - message("Finished detecting video capture devices"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - - message("Capture device detection failed!"); - } - */ - } - - private void detectDirectAudio() { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - try { - // Check if this is the Windows Performance Pack - hack - cls = Class.forName("VFWAuto"); - // Check if DS capture is supported, otherwise fail DS renderer - // since NT doesn't have capture - cls = Class.forName("com.sun.media.protocol.dsound.DSound"); - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - try { - // Set the format and open the device - AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16, - 2); - rend.setInputFormat(af); - rend.open(); - Format[] inputFormats = rend.getSupportedInputFormats(); - // Register the device - PlugInManager.addPlugIn(dar, inputFormats, new Format[0], - plType); - // Move it to the top of the list - Vector rendList = PlugInManager.getPlugInList(null, null, - plType); - int listSize = rendList.size(); - if (rendList.elementAt(listSize - 1).equals(dar)) { - rendList.removeElementAt(listSize - 1); - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - // Log.debug("registered"); - } - rend.close(); - } - catch (Throwable t) { - // Log.debug("Error " + t); - } - } - catch (Throwable tt) { - //Do nothing - } - } - - private void detectS8DirectAudio() { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - try { - // Check if this is the solaris Performance Pack - hack - cls = Class.forName("SunVideoAuto"); - - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - - if (rend instanceof ExclusiveUse - && !((ExclusiveUse) rend).isExclusive()) { - // sol8+, DAR supports mixing - Vector rendList = PlugInManager.getPlugInList(null, null, - plType); - int listSize = rendList.size(); - boolean found = false; - String rname = null; - - for (int i = 0; i < listSize; i++) { - rname = (String) (rendList.elementAt(i)); - if (rname.equals(dar)) { // DAR is in the registry - found = true; - rendList.removeElementAt(i); - break; - } - } - - if (found) { - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - } - } - } - catch (Throwable tt) { - //Do nothing - } - } - - private void message(String mesg) { - LOGGER.debug(mesg); - } - - private void createGUI() { - TextArea textBox = new TextArea(5, 50); - add("Center", textBox); - textBox.setEditable(false); - addNotify(); - pack(); - - int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize() - .getWidth(); - int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize() - .getHeight(); - - setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2); - - setVisible(visible); - - } - - public static void start(boolean visible) { - new JMFInit(null, visible); - } -} +package org.jivesoftware.smackx.jingle.mediaimpl; + +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.Toolkit; +import java.util.Vector; + +import javax.media.Format; +import javax.media.PlugInManager; +import javax.media.Renderer; +import javax.media.format.AudioFormat; + +import org.jivesoftware.smackx.jingle.SmackLogger; + +import com.sun.media.ExclusiveUse; +import com.sun.media.util.Registry; + +public class JMFInit extends Frame implements Runnable { + + private static final long serialVersionUID = 6476412003260641680L; + + private static final SmackLogger LOGGER = SmackLogger.getLogger(JMFInit.class); + + private String tempDir = "/tmp"; + + private boolean done = false; + + private String userHome; + + private boolean visible = false; + + public JMFInit(String[] args, boolean visible) { + super("Initializing JMF..."); + + this.visible = visible; + + Registry.set("secure.allowCaptureFromApplets", true); + Registry.set("secure.allowSaveFileFromApplets", true); + + updateTemp(args); + + try { + Registry.commit(); + } + catch (Exception e) { + + message("Failed to commit to JMFRegistry!"); + } + + Thread detectThread = new Thread(this); + detectThread.run(); + + /* + * int slept = 0; while (!done && slept < 60 * 1000 * 2) { try { + * Thread.currentThread().sleep(500); } catch (InterruptedException ie) { } + * slept += 500; } + * + * if (!done) { console.error("Detection is taking too long! + * Aborting!"); message("Detection is taking too long! Aborting!"); } + * + * try { Thread.currentThread().sleep(2000); } catch + * (InterruptedException ie) { } + */ + } + + public void run() { + detectDirectAudio(); + detectS8DirectAudio(); + detectCaptureDevices(); + done = true; + } + + private void updateTemp(String[] args) { + if (args != null && args.length > 0) { + tempDir = args[0]; + + message("Setting cache directory to " + tempDir); + Registry r = new Registry(); + try { + r.set("secure.cacheDir", tempDir); + r.commit(); + + message("Updated registry"); + } + catch (Exception e) { + message("Couldn't update registry!"); + } + } + } + + private void detectCaptureDevices() { + // check if JavaSound capture is available + message("Looking for Audio capturer"); + Class dsauto; + try { + dsauto = Class.forName("DirectSoundAuto"); + dsauto.newInstance(); + message("Finished detecting DirectSound capturer"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + //Do nothing + } + + Class jsauto; + try { + jsauto = Class.forName("JavaSoundAuto"); + jsauto.newInstance(); + message("Finished detecting javasound capturer"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + message("JavaSound capturer detection failed!"); + } + + /* + // Check if VFWAuto or SunVideoAuto is available + message("Looking for video capture devices"); + Class auto = null; + Class autoPlus = null; + try { + auto = Class.forName("VFWAuto"); + } + catch (Exception e) { + } + if (auto == null) { + try { + auto = Class.forName("SunVideoAuto"); + } + catch (Exception ee) { + + } + try { + autoPlus = Class.forName("SunVideoPlusAuto"); + } + catch (Exception ee) { + + } + } + if (auto == null) { + try { + auto = Class.forName("V4LAuto"); + } + catch (Exception ee) { + + } + } + try { + Object instance = auto.newInstance(); + if (autoPlus != null) { + Object instancePlus = autoPlus.newInstance(); + } + + message("Finished detecting video capture devices"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + + message("Capture device detection failed!"); + } + */ + } + + private void detectDirectAudio() { + Class cls; + int plType = PlugInManager.RENDERER; + String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; + try { + // Check if this is the Windows Performance Pack - hack + cls = Class.forName("VFWAuto"); + // Check if DS capture is supported, otherwise fail DS renderer + // since NT doesn't have capture + cls = Class.forName("com.sun.media.protocol.dsound.DSound"); + // Find the renderer class and instantiate it. + cls = Class.forName(dar); + + Renderer rend = (Renderer) cls.newInstance(); + try { + // Set the format and open the device + AudioFormat af = new AudioFormat(AudioFormat.LINEAR, 44100, 16, + 2); + rend.setInputFormat(af); + rend.open(); + Format[] inputFormats = rend.getSupportedInputFormats(); + // Register the device + PlugInManager.addPlugIn(dar, inputFormats, new Format[0], + plType); + // Move it to the top of the list + Vector rendList = PlugInManager.getPlugInList(null, null, + plType); + int listSize = rendList.size(); + if (rendList.elementAt(listSize - 1).equals(dar)) { + rendList.removeElementAt(listSize - 1); + rendList.insertElementAt(dar, 0); + PlugInManager.setPlugInList(rendList, plType); + PlugInManager.commit(); + // Log.debug("registered"); + } + rend.close(); + } + catch (Throwable t) { + // Log.debug("Error " + t); + } + } + catch (Throwable tt) { + //Do nothing + } + } + + private void detectS8DirectAudio() { + Class cls; + int plType = PlugInManager.RENDERER; + String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; + try { + // Check if this is the solaris Performance Pack - hack + cls = Class.forName("SunVideoAuto"); + + // Find the renderer class and instantiate it. + cls = Class.forName(dar); + + Renderer rend = (Renderer) cls.newInstance(); + + if (rend instanceof ExclusiveUse + && !((ExclusiveUse) rend).isExclusive()) { + // sol8+, DAR supports mixing + Vector rendList = PlugInManager.getPlugInList(null, null, + plType); + int listSize = rendList.size(); + boolean found = false; + String rname = null; + + for (int i = 0; i < listSize; i++) { + rname = (String) (rendList.elementAt(i)); + if (rname.equals(dar)) { // DAR is in the registry + found = true; + rendList.removeElementAt(i); + break; + } + } + + if (found) { + rendList.insertElementAt(dar, 0); + PlugInManager.setPlugInList(rendList, plType); + PlugInManager.commit(); + } + } + } + catch (Throwable tt) { + //Do nothing + } + } + + private void message(String mesg) { + LOGGER.debug(mesg); + } + + private void createGUI() { + TextArea textBox = new TextArea(5, 50); + add("Center", textBox); + textBox.setEditable(false); + addNotify(); + pack(); + + int scrWidth = (int) Toolkit.getDefaultToolkit().getScreenSize() + .getWidth(); + int scrHeight = (int) Toolkit.getDefaultToolkit().getScreenSize() + .getHeight(); + + setLocation((scrWidth - getWidth()) / 2, (scrHeight - getHeight()) / 2); + + setVisible(visible); + + } + + public static void start(boolean visible) { + new JMFInit(null, visible); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java index 0cd3457d1..9ff807af5 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/demo/Demo.java @@ -1,176 +1,176 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.demo; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleManager; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.JingleSessionRequest; -import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager; -import org.jivesoftware.smackx.jingle.nat.ICETransportManager; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.List; - -/** - * Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls. - * Parameters: Server User Pass. - */ -public class Demo extends JFrame { - - private static final long serialVersionUID = -6584021277434403855L; - private JingleTransportManager transportManager = null; - private Connection xmppConnection = null; - - private String server = null; - private String user = null; - private String pass = null; - - private JingleManager jm = null; - private JingleSession incoming = null; - private JingleSession outgoing = null; - - private JTextField jid; - - public Demo(String server, String user, String pass) { - - this.server = server; - this.user = user; - this.pass = pass; - - if (user.equals("jeffw")) { - jid = new JTextField("eowyn" + "@" + server + "/Smack"); - } else { - jid = new JTextField("jeffw" + "@" + server + "/Smack"); - } - - xmppConnection = new XMPPConnection(server); - try { - xmppConnection.connect(); - xmppConnection.login(user, pass); - initialize(); - } - catch (XMPPException e) { - e.printStackTrace(); - } - } - - public void initialize() { - ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478); - List mediaManagers = new ArrayList(); - //mediaManagers.add(new JmfMediaManager(icetm0)); - mediaManagers.add(new SpeexMediaManager(icetm0)); - mediaManagers.add(new ScreenShareMediaManager(icetm0)); - jm = new JingleManager(xmppConnection, mediaManagers); - jm.addCreationListener(icetm0); - - jm.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(JingleSessionRequest request) { - -// if (incoming != null) -// return; - - try { - // Accept the call - incoming = request.accept(); - - // Start the call - incoming.startIncoming(); - } - catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - createGUI(); - } - - public void createGUI() { - - JPanel jPanel = new JPanel(); - - jPanel.add(jid); - - jPanel.add(new JButton(new AbstractAction("Call") { - private static final long serialVersionUID = 4308448034795312815L; - - public void actionPerformed(ActionEvent e) { - if (outgoing != null) return; - try { - outgoing = jm.createOutgoingJingleSession(jid.getText()); - outgoing.startOutgoing(); - } - catch (XMPPException e1) { - e1.printStackTrace(); - } - } - })); - - jPanel.add(new JButton(new AbstractAction("Hangup") { - private static final long serialVersionUID = -4508007389146723587L; - - public void actionPerformed(ActionEvent e) { - if (outgoing != null) - try { - outgoing.terminate(); - } - catch (XMPPException e1) { - e1.printStackTrace(); - } - finally { - outgoing = null; - } - if (incoming != null) - try { - incoming.terminate(); - } - catch (XMPPException e1) { - e1.printStackTrace(); - } - finally { - incoming = null; - } - } - })); - - this.add(jPanel); - - } - - public static void main(String args[]) { - - Demo demo = null; - - if (args.length > 2) { - demo = new Demo(args[0], args[1], args[2]); - demo.pack(); - demo.setVisible(true); - demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - - } - -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.mediaimpl.demo; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleManager; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.JingleSessionRequest; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager; +import org.jivesoftware.smackx.jingle.nat.ICETransportManager; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +/** + * Jingle Demo Application. It register in a XMPP Server and let users place calls using a full JID and auto-receive calls. + * Parameters: Server User Pass. + */ +public class Demo extends JFrame { + + private static final long serialVersionUID = -6584021277434403855L; + private JingleTransportManager transportManager = null; + private Connection xmppConnection = null; + + private String server = null; + private String user = null; + private String pass = null; + + private JingleManager jm = null; + private JingleSession incoming = null; + private JingleSession outgoing = null; + + private JTextField jid; + + public Demo(String server, String user, String pass) { + + this.server = server; + this.user = user; + this.pass = pass; + + if (user.equals("jeffw")) { + jid = new JTextField("eowyn" + "@" + server + "/Smack"); + } else { + jid = new JTextField("jeffw" + "@" + server + "/Smack"); + } + + xmppConnection = new XMPPConnection(server); + try { + xmppConnection.connect(); + xmppConnection.login(user, pass); + initialize(); + } + catch (XMPPException e) { + e.printStackTrace(); + } + } + + public void initialize() { + ICETransportManager icetm0 = new ICETransportManager(xmppConnection, "10.47.47.53", 3478); + List mediaManagers = new ArrayList(); + //mediaManagers.add(new JmfMediaManager(icetm0)); + mediaManagers.add(new SpeexMediaManager(icetm0)); + mediaManagers.add(new ScreenShareMediaManager(icetm0)); + jm = new JingleManager(xmppConnection, mediaManagers); + jm.addCreationListener(icetm0); + + jm.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(JingleSessionRequest request) { + +// if (incoming != null) +// return; + + try { + // Accept the call + incoming = request.accept(); + + // Start the call + incoming.startIncoming(); + } + catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + createGUI(); + } + + public void createGUI() { + + JPanel jPanel = new JPanel(); + + jPanel.add(jid); + + jPanel.add(new JButton(new AbstractAction("Call") { + private static final long serialVersionUID = 4308448034795312815L; + + public void actionPerformed(ActionEvent e) { + if (outgoing != null) return; + try { + outgoing = jm.createOutgoingJingleSession(jid.getText()); + outgoing.startOutgoing(); + } + catch (XMPPException e1) { + e1.printStackTrace(); + } + } + })); + + jPanel.add(new JButton(new AbstractAction("Hangup") { + private static final long serialVersionUID = -4508007389146723587L; + + public void actionPerformed(ActionEvent e) { + if (outgoing != null) + try { + outgoing.terminate(); + } + catch (XMPPException e1) { + e1.printStackTrace(); + } + finally { + outgoing = null; + } + if (incoming != null) + try { + incoming.terminate(); + } + catch (XMPPException e1) { + e1.printStackTrace(); + } + finally { + incoming = null; + } + } + })); + + this.add(jPanel); + + } + + public static void main(String args[]) { + + Demo demo = null; + + if (args.length > 2) { + demo = new Demo(args[0], args[1], args[2]); + demo.pack(); + demo.setVisible(true); + demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java index 266fe9b21..5d3913eb4 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioChannel.java @@ -1,550 +1,550 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -import javax.media.Codec; -import javax.media.Controller; -import javax.media.ControllerClosedEvent; -import javax.media.ControllerEvent; -import javax.media.ControllerListener; -import javax.media.Format; -import javax.media.MediaLocator; -import javax.media.NoProcessorException; -import javax.media.Processor; -import javax.media.UnsupportedPlugInException; -import javax.media.control.BufferControl; -import javax.media.control.PacketSizeControl; -import javax.media.control.TrackControl; -import javax.media.format.AudioFormat; -import javax.media.protocol.ContentDescriptor; -import javax.media.protocol.DataSource; -import javax.media.protocol.PushBufferDataSource; -import javax.media.protocol.PushBufferStream; -import javax.media.rtp.InvalidSessionAddressException; -import javax.media.rtp.RTPManager; -import javax.media.rtp.SendStream; -import javax.media.rtp.SessionAddress; - -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; - -/** - * An Easy to use Audio Channel implemented using JMF. - * It sends and receives jmf for and from desired IPs and ports. - * Also has a rport Symetric behavior for better NAT Traversal. - * It send data from a defined port and receive data in the same port, making NAT binds easier. - *

    - * Send from portA to portB and receive from portB in portA. - *

    - * Sending - * portA ---> portB - *

    - * Receiving - * portB ---> portA - *

    - * Transmit and Receive are interdependents. To receive you MUST trasmit. - * - * @author Thiago Camargo - */ -public class AudioChannel { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class); - - private MediaLocator locator; - private String localIpAddress; - private String remoteIpAddress; - private int localPort; - private int portBase; - private Format format; - - private Processor processor = null; - private RTPManager rtpMgrs[]; - private DataSource dataOutput = null; - private AudioReceiver audioReceiver; - - private List sendStreams = new ArrayList(); - - private JingleMediaSession jingleMediaSession; - - private boolean started = false; - - /** - * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://") - * - * @param locator media locator - * @param localIpAddress local IP address - * @param remoteIpAddress remote IP address - * @param localPort local port number - * @param remotePort remote port number - * @param format audio format - */ - public AudioChannel(MediaLocator locator, - String localIpAddress, - String remoteIpAddress, - int localPort, - int remotePort, - Format format, JingleMediaSession jingleMediaSession) { - - this.locator = locator; - this.localIpAddress = localIpAddress; - this.remoteIpAddress = remoteIpAddress; - this.localPort = localPort; - this.portBase = remotePort; - this.format = format; - this.jingleMediaSession = jingleMediaSession; - } - - /** - * Starts the transmission. Returns null if transmission started ok. - * Otherwise it returns a string with the reason why the setup failed. - * Starts receive also. - * - * @return result description - */ - public synchronized String start() { - if (started) return null; - - // Create a processor for the specified jmf locator - String result = createProcessor(); - if (result != null) { - started = false; - } - - // Create an RTP session to transmit the output of the - // processor to the specified IP address and port no. - result = createTransmitter(); - if (result != null) { - processor.close(); - processor = null; - started = false; - } - else { - started = true; - } - - // Start the transmission - processor.start(); - - return null; - } - - /** - * Stops the transmission if already started. - * Stops the receiver also. - */ - public void stop() { - if (!started) return; - synchronized (this) { - try { - started = false; - if (processor != null) { - processor.stop(); - processor = null; - - for (RTPManager rtpMgr : rtpMgrs) { - rtpMgr.removeReceiveStreamListener(audioReceiver); - rtpMgr.removeSessionListener(audioReceiver); - rtpMgr.removeTargets("Session ended."); - rtpMgr.dispose(); - } - - sendStreams.clear(); - - } - } - catch (Exception e) { - e.printStackTrace(); - } - } - } - - private String createProcessor() { - if (locator == null) - return "Locator is null"; - - DataSource ds; - - try { - ds = javax.media.Manager.createDataSource(locator); - } - catch (Exception e) { - // Try JavaSound Locator as a last resort - try { - ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://")); - } - catch (Exception ee) { - return "Couldn't create DataSource"; - } - } - - // Try to create a processor to handle the input jmf locator - try { - processor = javax.media.Manager.createProcessor(ds); - } - catch (NoProcessorException npe) { - npe.printStackTrace(); - return "Couldn't create processor"; - } - catch (IOException ioe) { - ioe.printStackTrace(); - return "IOException creating processor"; - } - - // Wait for it to configure - boolean result = waitForState(processor, Processor.Configured); - if (!result){ - return "Couldn't configure processor"; - } - - // Get the tracks from the processor - TrackControl[] tracks = processor.getTrackControls(); - - // Do we have atleast one track? - if (tracks == null || tracks.length < 1){ - return "Couldn't find tracks in processor"; - } - - // Set the output content descriptor to RAW_RTP - // This will limit the supported formats reported from - // Track.getSupportedFormats to only valid RTP formats. - ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); - processor.setContentDescriptor(cd); - - Format supported[]; - Format chosen = null; - boolean atLeastOneTrack = false; - - // Program the tracks. - for (int i = 0; i < tracks.length; i++) { - if (tracks[i].isEnabled()) { - - supported = tracks[i].getSupportedFormats(); - - if (supported.length > 0) { - for (Format format : supported) { - if (format instanceof AudioFormat) { - if (this.format.matches(format)) - chosen = format; - } - } - if (chosen != null) { - tracks[i].setFormat(chosen); - LOGGER.error("Track " + i + " is set to transmit as:"); - LOGGER.error(" " + chosen); - - if (tracks[i].getFormat() instanceof AudioFormat) { - int packetRate = 20; - PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName()); - if (pktCtrl != null) { - try { - pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate)); - } - catch (IllegalArgumentException e) { - pktCtrl.setPacketSize(80); - // Do nothing - } - } - - if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) { - Codec codec[] = new Codec[3]; - - codec[0] = new com.ibm.media.codec.audio.rc.RCModule(); - codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder(); - codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer(); - ((com.sun.media.codec.audio.ulaw.Packetizer) codec - [2]).setPacketSize(160); - - try { - tracks[i].setCodecChain(codec); - } - catch (UnsupportedPlugInException e) { - e.printStackTrace(); - } - } - - } - - atLeastOneTrack = true; - } - else - tracks[i].setEnabled(false); - } - else - tracks[i].setEnabled(false); - } - } - - if (!atLeastOneTrack) - return "Couldn't set any of the tracks to a valid RTP format"; - - result = waitForState(processor, Controller.Realized); - if (!result) - return "Couldn't realize processor"; - - // Get the output data source of the processor - dataOutput = processor.getDataOutput(); - - return null; - } - - /** - * Get the best packet size for a given codec and a codec rate - * - * @param codecFormat - * @param milliseconds - * @return - * @throws IllegalArgumentException - */ - private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException { - String encoding = codecFormat.getEncoding(); - if (encoding.equalsIgnoreCase(AudioFormat.GSM) || - encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) { - return milliseconds * 4; // 1 byte per millisec - } - else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) || - encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) { - return milliseconds * 8; - } - else { - throw new IllegalArgumentException("Unknown codec type"); - } - } - - /** - * Use the RTPManager API to create sessions for each jmf - * track of the processor. - * - * @return description - */ - private String createTransmitter() { - - // Cheated. Should have checked the type. - PushBufferDataSource pbds = (PushBufferDataSource) dataOutput; - PushBufferStream pbss[] = pbds.getStreams(); - - rtpMgrs = new RTPManager[pbss.length]; - SessionAddress localAddr, destAddr; - InetAddress ipAddr; - SendStream sendStream; - audioReceiver = new AudioReceiver(this, jingleMediaSession); - int port; - - for (int i = 0; i < pbss.length; i++) { - try { - rtpMgrs[i] = RTPManager.newInstance(); - - port = portBase + 2 * i; - ipAddr = InetAddress.getByName(remoteIpAddress); - - localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress), - localPort); - - destAddr = new SessionAddress(ipAddr, port); - - rtpMgrs[i].addReceiveStreamListener(audioReceiver); - rtpMgrs[i].addSessionListener(audioReceiver); - - BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl"); - if (bc != null) { - int bl = 160; - bc.setBufferLength(bl); - } - - try { - - rtpMgrs[i].initialize(localAddr); - - } - catch (InvalidSessionAddressException e) { - // In case the local address is not allowed to read, we user another local address - SessionAddress sessAddr = new SessionAddress(); - localAddr = new SessionAddress(sessAddr.getDataAddress(), - localPort); - rtpMgrs[i].initialize(localAddr); - } - - rtpMgrs[i].addTarget(destAddr); - - LOGGER.error("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port); - - sendStream = rtpMgrs[i].createSendStream(dataOutput, i); - - sendStreams.add(sendStream); - - sendStream.start(); - - } - catch (Exception e) { - e.printStackTrace(); - return e.getMessage(); - } - } - - return null; - } - - /** - * Set transmit activity. If the active is true, the instance should trasmit. - * If it is set to false, the instance should pause transmit. - * - * @param active active state - */ - public void setTrasmit(boolean active) { - for (SendStream sendStream : sendStreams) { - try { - if (active) { - sendStream.start(); - LOGGER.debug("START"); - } - else { - sendStream.stop(); - LOGGER.debug("STOP"); - } - } - catch (IOException e) { - e.printStackTrace(); - } - - } - } - - /** - * ************************************************************* - * Convenience methods to handle processor's state changes. - * ************************************************************** - */ - - private Integer stateLock = 0; - private boolean failed = false; - - Integer getStateLock() { - return stateLock; - } - - void setFailed() { - failed = true; - } - - private synchronized boolean waitForState(Processor p, int state) { - p.addControllerListener(new StateListener()); - failed = false; - - // Call the required method on the processor - if (state == Processor.Configured) { - p.configure(); - } - else if (state == Processor.Realized) { - p.realize(); - } - - // Wait until we get an event that confirms the - // success of the method, or a failure event. - // See StateListener inner class - while (p.getState() < state && !failed) { - synchronized (getStateLock()) { - try { - getStateLock().wait(); - } - catch (InterruptedException ie) { - return false; - } - } - } - - return !failed; - } - - /** - * ************************************************************* - * Inner Classes - * ************************************************************** - */ - - class StateListener implements ControllerListener { - - public void controllerUpdate(ControllerEvent ce) { - - // If there was an error during configure or - // realize, the processor will be closed - if (ce instanceof ControllerClosedEvent) - setFailed(); - - // All controller events, send a notification - // to the waiting thread in waitForState method. - if (ce != null) { - synchronized (getStateLock()) { - getStateLock().notifyAll(); - } - } - } - } - - public static void main(String args[]) { - - InetAddress localhost; - try { - localhost = InetAddress.getLocalHost(); - - AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP), null); - AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null); - - audioChannel0.start(); - audioChannel1.start(); - - try { - Thread.sleep(5000); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - audioChannel0.setTrasmit(false); - audioChannel1.setTrasmit(false); - - try { - Thread.sleep(5000); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - audioChannel0.setTrasmit(true); - audioChannel1.setTrasmit(true); - - try { - Thread.sleep(5000); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - audioChannel0.stop(); - audioChannel1.stop(); - - } - catch (UnknownHostException e) { - e.printStackTrace(); - } - - } -} \ No newline at end of file +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.mediaimpl.jmf; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import javax.media.Codec; +import javax.media.Controller; +import javax.media.ControllerClosedEvent; +import javax.media.ControllerEvent; +import javax.media.ControllerListener; +import javax.media.Format; +import javax.media.MediaLocator; +import javax.media.NoProcessorException; +import javax.media.Processor; +import javax.media.UnsupportedPlugInException; +import javax.media.control.BufferControl; +import javax.media.control.PacketSizeControl; +import javax.media.control.TrackControl; +import javax.media.format.AudioFormat; +import javax.media.protocol.ContentDescriptor; +import javax.media.protocol.DataSource; +import javax.media.protocol.PushBufferDataSource; +import javax.media.protocol.PushBufferStream; +import javax.media.rtp.InvalidSessionAddressException; +import javax.media.rtp.RTPManager; +import javax.media.rtp.SendStream; +import javax.media.rtp.SessionAddress; + +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; + +/** + * An Easy to use Audio Channel implemented using JMF. + * It sends and receives jmf for and from desired IPs and ports. + * Also has a rport Symetric behavior for better NAT Traversal. + * It send data from a defined port and receive data in the same port, making NAT binds easier. + *

    + * Send from portA to portB and receive from portB in portA. + *

    + * Sending + * portA ---> portB + *

    + * Receiving + * portB ---> portA + *

    + * Transmit and Receive are interdependents. To receive you MUST trasmit. + * + * @author Thiago Camargo + */ +public class AudioChannel { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioChannel.class); + + private MediaLocator locator; + private String localIpAddress; + private String remoteIpAddress; + private int localPort; + private int portBase; + private Format format; + + private Processor processor = null; + private RTPManager rtpMgrs[]; + private DataSource dataOutput = null; + private AudioReceiver audioReceiver; + + private List sendStreams = new ArrayList(); + + private JingleMediaSession jingleMediaSession; + + private boolean started = false; + + /** + * Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://") + * + * @param locator media locator + * @param localIpAddress local IP address + * @param remoteIpAddress remote IP address + * @param localPort local port number + * @param remotePort remote port number + * @param format audio format + */ + public AudioChannel(MediaLocator locator, + String localIpAddress, + String remoteIpAddress, + int localPort, + int remotePort, + Format format, JingleMediaSession jingleMediaSession) { + + this.locator = locator; + this.localIpAddress = localIpAddress; + this.remoteIpAddress = remoteIpAddress; + this.localPort = localPort; + this.portBase = remotePort; + this.format = format; + this.jingleMediaSession = jingleMediaSession; + } + + /** + * Starts the transmission. Returns null if transmission started ok. + * Otherwise it returns a string with the reason why the setup failed. + * Starts receive also. + * + * @return result description + */ + public synchronized String start() { + if (started) return null; + + // Create a processor for the specified jmf locator + String result = createProcessor(); + if (result != null) { + started = false; + } + + // Create an RTP session to transmit the output of the + // processor to the specified IP address and port no. + result = createTransmitter(); + if (result != null) { + processor.close(); + processor = null; + started = false; + } + else { + started = true; + } + + // Start the transmission + processor.start(); + + return null; + } + + /** + * Stops the transmission if already started. + * Stops the receiver also. + */ + public void stop() { + if (!started) return; + synchronized (this) { + try { + started = false; + if (processor != null) { + processor.stop(); + processor = null; + + for (RTPManager rtpMgr : rtpMgrs) { + rtpMgr.removeReceiveStreamListener(audioReceiver); + rtpMgr.removeSessionListener(audioReceiver); + rtpMgr.removeTargets("Session ended."); + rtpMgr.dispose(); + } + + sendStreams.clear(); + + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + + private String createProcessor() { + if (locator == null) + return "Locator is null"; + + DataSource ds; + + try { + ds = javax.media.Manager.createDataSource(locator); + } + catch (Exception e) { + // Try JavaSound Locator as a last resort + try { + ds = javax.media.Manager.createDataSource(new MediaLocator("javasound://")); + } + catch (Exception ee) { + return "Couldn't create DataSource"; + } + } + + // Try to create a processor to handle the input jmf locator + try { + processor = javax.media.Manager.createProcessor(ds); + } + catch (NoProcessorException npe) { + npe.printStackTrace(); + return "Couldn't create processor"; + } + catch (IOException ioe) { + ioe.printStackTrace(); + return "IOException creating processor"; + } + + // Wait for it to configure + boolean result = waitForState(processor, Processor.Configured); + if (!result){ + return "Couldn't configure processor"; + } + + // Get the tracks from the processor + TrackControl[] tracks = processor.getTrackControls(); + + // Do we have atleast one track? + if (tracks == null || tracks.length < 1){ + return "Couldn't find tracks in processor"; + } + + // Set the output content descriptor to RAW_RTP + // This will limit the supported formats reported from + // Track.getSupportedFormats to only valid RTP formats. + ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); + processor.setContentDescriptor(cd); + + Format supported[]; + Format chosen = null; + boolean atLeastOneTrack = false; + + // Program the tracks. + for (int i = 0; i < tracks.length; i++) { + if (tracks[i].isEnabled()) { + + supported = tracks[i].getSupportedFormats(); + + if (supported.length > 0) { + for (Format format : supported) { + if (format instanceof AudioFormat) { + if (this.format.matches(format)) + chosen = format; + } + } + if (chosen != null) { + tracks[i].setFormat(chosen); + LOGGER.error("Track " + i + " is set to transmit as:"); + LOGGER.error(" " + chosen); + + if (tracks[i].getFormat() instanceof AudioFormat) { + int packetRate = 20; + PacketSizeControl pktCtrl = (PacketSizeControl) processor.getControl(PacketSizeControl.class.getName()); + if (pktCtrl != null) { + try { + pktCtrl.setPacketSize(getPacketSize(tracks[i].getFormat(), packetRate)); + } + catch (IllegalArgumentException e) { + pktCtrl.setPacketSize(80); + // Do nothing + } + } + + if (tracks[i].getFormat().getEncoding().equals(AudioFormat.ULAW_RTP)) { + Codec codec[] = new Codec[3]; + + codec[0] = new com.ibm.media.codec.audio.rc.RCModule(); + codec[1] = new com.ibm.media.codec.audio.ulaw.JavaEncoder(); + codec[2] = new com.sun.media.codec.audio.ulaw.Packetizer(); + ((com.sun.media.codec.audio.ulaw.Packetizer) codec + [2]).setPacketSize(160); + + try { + tracks[i].setCodecChain(codec); + } + catch (UnsupportedPlugInException e) { + e.printStackTrace(); + } + } + + } + + atLeastOneTrack = true; + } + else + tracks[i].setEnabled(false); + } + else + tracks[i].setEnabled(false); + } + } + + if (!atLeastOneTrack) + return "Couldn't set any of the tracks to a valid RTP format"; + + result = waitForState(processor, Controller.Realized); + if (!result) + return "Couldn't realize processor"; + + // Get the output data source of the processor + dataOutput = processor.getDataOutput(); + + return null; + } + + /** + * Get the best packet size for a given codec and a codec rate + * + * @param codecFormat + * @param milliseconds + * @return + * @throws IllegalArgumentException + */ + private int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException { + String encoding = codecFormat.getEncoding(); + if (encoding.equalsIgnoreCase(AudioFormat.GSM) || + encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) { + return milliseconds * 4; // 1 byte per millisec + } + else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) || + encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) { + return milliseconds * 8; + } + else { + throw new IllegalArgumentException("Unknown codec type"); + } + } + + /** + * Use the RTPManager API to create sessions for each jmf + * track of the processor. + * + * @return description + */ + private String createTransmitter() { + + // Cheated. Should have checked the type. + PushBufferDataSource pbds = (PushBufferDataSource) dataOutput; + PushBufferStream pbss[] = pbds.getStreams(); + + rtpMgrs = new RTPManager[pbss.length]; + SessionAddress localAddr, destAddr; + InetAddress ipAddr; + SendStream sendStream; + audioReceiver = new AudioReceiver(this, jingleMediaSession); + int port; + + for (int i = 0; i < pbss.length; i++) { + try { + rtpMgrs[i] = RTPManager.newInstance(); + + port = portBase + 2 * i; + ipAddr = InetAddress.getByName(remoteIpAddress); + + localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress), + localPort); + + destAddr = new SessionAddress(ipAddr, port); + + rtpMgrs[i].addReceiveStreamListener(audioReceiver); + rtpMgrs[i].addSessionListener(audioReceiver); + + BufferControl bc = (BufferControl) rtpMgrs[i].getControl("javax.media.control.BufferControl"); + if (bc != null) { + int bl = 160; + bc.setBufferLength(bl); + } + + try { + + rtpMgrs[i].initialize(localAddr); + + } + catch (InvalidSessionAddressException e) { + // In case the local address is not allowed to read, we user another local address + SessionAddress sessAddr = new SessionAddress(); + localAddr = new SessionAddress(sessAddr.getDataAddress(), + localPort); + rtpMgrs[i].initialize(localAddr); + } + + rtpMgrs[i].addTarget(destAddr); + + LOGGER.error("Created RTP session at " + localPort + " to: " + remoteIpAddress + " " + port); + + sendStream = rtpMgrs[i].createSendStream(dataOutput, i); + + sendStreams.add(sendStream); + + sendStream.start(); + + } + catch (Exception e) { + e.printStackTrace(); + return e.getMessage(); + } + } + + return null; + } + + /** + * Set transmit activity. If the active is true, the instance should trasmit. + * If it is set to false, the instance should pause transmit. + * + * @param active active state + */ + public void setTrasmit(boolean active) { + for (SendStream sendStream : sendStreams) { + try { + if (active) { + sendStream.start(); + LOGGER.debug("START"); + } + else { + sendStream.stop(); + LOGGER.debug("STOP"); + } + } + catch (IOException e) { + e.printStackTrace(); + } + + } + } + + /** + * ************************************************************* + * Convenience methods to handle processor's state changes. + * ************************************************************** + */ + + private Integer stateLock = 0; + private boolean failed = false; + + Integer getStateLock() { + return stateLock; + } + + void setFailed() { + failed = true; + } + + private synchronized boolean waitForState(Processor p, int state) { + p.addControllerListener(new StateListener()); + failed = false; + + // Call the required method on the processor + if (state == Processor.Configured) { + p.configure(); + } + else if (state == Processor.Realized) { + p.realize(); + } + + // Wait until we get an event that confirms the + // success of the method, or a failure event. + // See StateListener inner class + while (p.getState() < state && !failed) { + synchronized (getStateLock()) { + try { + getStateLock().wait(); + } + catch (InterruptedException ie) { + return false; + } + } + } + + return !failed; + } + + /** + * ************************************************************* + * Inner Classes + * ************************************************************** + */ + + class StateListener implements ControllerListener { + + public void controllerUpdate(ControllerEvent ce) { + + // If there was an error during configure or + // realize, the processor will be closed + if (ce instanceof ControllerClosedEvent) + setFailed(); + + // All controller events, send a notification + // to the waiting thread in waitForState method. + if (ce != null) { + synchronized (getStateLock()) { + getStateLock().notifyAll(); + } + } + } + } + + public static void main(String args[]) { + + InetAddress localhost; + try { + localhost = InetAddress.getLocalHost(); + + AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP), null); + AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP), null); + + audioChannel0.start(); + audioChannel1.start(); + + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + audioChannel0.setTrasmit(false); + audioChannel1.setTrasmit(false); + + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + audioChannel0.setTrasmit(true); + audioChannel1.setTrasmit(true); + + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + audioChannel0.stop(); + audioChannel1.stop(); + + } + catch (UnknownHostException e) { + e.printStackTrace(); + } + + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java index 4dccacb35..5252c5b92 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioFormatUtils.java @@ -1,52 +1,52 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import org.jivesoftware.smackx.jingle.media.PayloadType; - -import javax.media.format.AudioFormat; - -/** - * Audio Format Utils. - * - * @author Thiago Camargo - */ -public class AudioFormatUtils { - - /** - * Return a JMF AudioFormat for a given Jingle Payload type. - * Return null if the payload is not supported by this jmf API. - * - * @param payloadtype payloadtype - * @return correspondent audioType - */ - public static AudioFormat getAudioFormat(PayloadType payloadtype) { - - switch (payloadtype.getId()) { - case 0: - return new AudioFormat(AudioFormat.ULAW_RTP); - case 3: - return new AudioFormat(AudioFormat.GSM_RTP); - case 4: - return new AudioFormat(AudioFormat.G723_RTP); - default: - return null; - } - - } - -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.mediaimpl.jmf; + +import org.jivesoftware.smackx.jingle.media.PayloadType; + +import javax.media.format.AudioFormat; + +/** + * Audio Format Utils. + * + * @author Thiago Camargo + */ +public class AudioFormatUtils { + + /** + * Return a JMF AudioFormat for a given Jingle Payload type. + * Return null if the payload is not supported by this jmf API. + * + * @param payloadtype payloadtype + * @return correspondent audioType + */ + public static AudioFormat getAudioFormat(PayloadType payloadtype) { + + switch (payloadtype.getId()) { + case 0: + return new AudioFormat(AudioFormat.ULAW_RTP); + case 3: + return new AudioFormat(AudioFormat.GSM_RTP); + case 4: + return new AudioFormat(AudioFormat.G723_RTP); + default: + return null; + } + + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java index 9a14c5c41..5e5733f78 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioMediaSession.java @@ -1,162 +1,162 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.IOException; -import java.net.ServerSocket; - -import javax.media.MediaLocator; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; - -/** - * This Class implements a complete JingleMediaSession. - * It sould be used to transmit and receive audio captured from the Mic. - * This Class should be automaticly controlled by JingleSession. - * But you could also use in any VOIP application. - * For better NAT Traversal support this implementation don't support only receive or only transmit. - * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() - * - * @author Thiago Camargo - */ -public class AudioMediaSession extends JingleMediaSession { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); - - private AudioChannel audioChannel; - - /** - * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates - * - * @param payloadType Payload of the jmf - * @param remote the remote information. The candidate that the jmf will be sent to. - * @param local the local information. The candidate that will receive the jmf - * @param locator media locator - */ - public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, - final TransportCandidate local, String locator, JingleSession jingleSession) { - super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession); - initialize(); - } - - /** - * Initialize the Audio Channel to make it able to send and receive audio - */ - public void initialize() { - - String ip; - String localIp; - int localPort; - int remotePort; - - if (this.getLocal().getSymmetric() != null) { - ip = this.getLocal().getIp(); - localIp = this.getLocal().getLocalIp(); - localPort = getFreePort(); - remotePort = this.getLocal().getSymmetric().getPort(); - - LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); - - } - else { - ip = this.getRemote().getIp(); - localIp = this.getLocal().getLocalIp(); - localPort = this.getLocal().getPort(); - remotePort = this.getRemote().getPort(); - } - - audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this); - } - - /** - * Starts transmission and for NAT Traversal reasons start receiving also. - */ - public void startTrasmit() { - audioChannel.start(); - } - - /** - * Set transmit activity. If the active is true, the instance should trasmit. - * If it is set to false, the instance should pause transmit. - * - * @param active active state - */ - public void setTrasmit(boolean active) { - audioChannel.setTrasmit(active); - } - - /** - * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf - */ - public void startReceive() { - // Do nothing - } - - /** - * Stops transmission and for NAT Traversal reasons stop receiving also. - */ - public void stopTrasmit() { - if (audioChannel != null) - audioChannel.stop(); - } - - /** - * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf - */ - public void stopReceive() { - // Do nothing - } - - /** - * Obtain a free port we can use. - * - * @return A free port number. - */ - protected int getFreePort() { - ServerSocket ss; - int freePort = 0; - - for (int i = 0; i < 10; i++) { - freePort = (int) (10000 + Math.round(Math.random() * 10000)); - freePort = freePort % 2 == 0 ? freePort : freePort + 1; - try { - ss = new ServerSocket(freePort); - freePort = ss.getLocalPort(); - ss.close(); - return freePort; - } - catch (IOException e) { - e.printStackTrace(); - } - } - try { - ss = new ServerSocket(0); - freePort = ss.getLocalPort(); - ss.close(); - } - catch (IOException e) { - e.printStackTrace(); - } - return freePort; - } - -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.mediaimpl.jmf; + +import java.io.IOException; +import java.net.ServerSocket; + +import javax.media.MediaLocator; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +/** + * This Class implements a complete JingleMediaSession. + * It sould be used to transmit and receive audio captured from the Mic. + * This Class should be automaticly controlled by JingleSession. + * But you could also use in any VOIP application. + * For better NAT Traversal support this implementation don't support only receive or only transmit. + * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() + * + * @author Thiago Camargo + */ +public class AudioMediaSession extends JingleMediaSession { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); + + private AudioChannel audioChannel; + + /** + * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates + * + * @param payloadType Payload of the jmf + * @param remote the remote information. The candidate that the jmf will be sent to. + * @param local the local information. The candidate that will receive the jmf + * @param locator media locator + */ + public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, + final TransportCandidate local, String locator, JingleSession jingleSession) { + super(payloadType, remote, local, locator==null?"dsound://":locator,jingleSession); + initialize(); + } + + /** + * Initialize the Audio Channel to make it able to send and receive audio + */ + public void initialize() { + + String ip; + String localIp; + int localPort; + int remotePort; + + if (this.getLocal().getSymmetric() != null) { + ip = this.getLocal().getIp(); + localIp = this.getLocal().getLocalIp(); + localPort = getFreePort(); + remotePort = this.getLocal().getSymmetric().getPort(); + + LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); + + } + else { + ip = this.getRemote().getIp(); + localIp = this.getLocal().getLocalIp(); + localPort = this.getLocal().getPort(); + remotePort = this.getRemote().getPort(); + } + + audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()),this); + } + + /** + * Starts transmission and for NAT Traversal reasons start receiving also. + */ + public void startTrasmit() { + audioChannel.start(); + } + + /** + * Set transmit activity. If the active is true, the instance should trasmit. + * If it is set to false, the instance should pause transmit. + * + * @param active active state + */ + public void setTrasmit(boolean active) { + audioChannel.setTrasmit(active); + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void startReceive() { + // Do nothing + } + + /** + * Stops transmission and for NAT Traversal reasons stop receiving also. + */ + public void stopTrasmit() { + if (audioChannel != null) + audioChannel.stop(); + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void stopReceive() { + // Do nothing + } + + /** + * Obtain a free port we can use. + * + * @return A free port number. + */ + protected int getFreePort() { + ServerSocket ss; + int freePort = 0; + + for (int i = 0; i < 10; i++) { + freePort = (int) (10000 + Math.round(Math.random() * 10000)); + freePort = freePort % 2 == 0 ? freePort : freePort + 1; + try { + ss = new ServerSocket(freePort); + freePort = ss.getLocalPort(); + ss.close(); + return freePort; + } + catch (IOException e) { + e.printStackTrace(); + } + } + try { + ss = new ServerSocket(0); + freePort = ss.getLocalPort(); + ss.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + return freePort; + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java index 1d128ddf3..b2138b23d 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/AudioReceiver.java @@ -1,168 +1,168 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import javax.media.ControllerErrorEvent; -import javax.media.ControllerEvent; -import javax.media.ControllerListener; -import javax.media.Player; -import javax.media.RealizeCompleteEvent; -import javax.media.protocol.DataSource; -import javax.media.rtp.Participant; -import javax.media.rtp.RTPControl; -import javax.media.rtp.ReceiveStream; -import javax.media.rtp.ReceiveStreamListener; -import javax.media.rtp.SessionListener; -import javax.media.rtp.event.ByeEvent; -import javax.media.rtp.event.NewParticipantEvent; -import javax.media.rtp.event.NewReceiveStreamEvent; -import javax.media.rtp.event.ReceiveStreamEvent; -import javax.media.rtp.event.RemotePayloadChangeEvent; -import javax.media.rtp.event.SessionEvent; -import javax.media.rtp.event.StreamMappedEvent; - -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; - -/** - * This class implements receive methods and listeners to be used in AudioChannel - * - * @author Thiago Camargo - */ -public class AudioReceiver implements ReceiveStreamListener, SessionListener, - ControllerListener { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class); - - boolean dataReceived = false; - - Object dataSync; - JingleMediaSession jingleMediaSession; - - public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) { - this.dataSync = dataSync; - this.jingleMediaSession = jingleMediaSession; - } - - /** - * JingleSessionListener. - */ - public synchronized void update(SessionEvent evt) { - if (evt instanceof NewParticipantEvent) { - Participant p = ((NewParticipantEvent) evt).getParticipant(); - LOGGER.error(" - A new participant had just joined: " + p.getCNAME()); - } - } - - /** - * ReceiveStreamListener - */ - public synchronized void update(ReceiveStreamEvent evt) { - - Participant participant = evt.getParticipant(); // could be null. - ReceiveStream stream = evt.getReceiveStream(); // could be null. - - if (evt instanceof RemotePayloadChangeEvent) { - LOGGER.error(" - Received an RTP PayloadChangeEvent."); - LOGGER.error("Sorry, cannot handle payload change."); - - } - else if (evt instanceof NewReceiveStreamEvent) { - - try { - stream = evt.getReceiveStream(); - DataSource ds = stream.getDataSource(); - - // Find out the formats. - RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); - if (ctl != null) { - LOGGER.error(" - Recevied new RTP stream: " + ctl.getFormat()); - } - else - LOGGER.error(" - Recevied new RTP stream"); - - if (participant == null) - LOGGER.error(" The sender of this stream had yet to be identified."); - else { - LOGGER.error(" The stream comes from: " + participant.getCNAME()); - } - - // create a player by passing datasource to the Media Manager - Player p = javax.media.Manager.createPlayer(ds); - if (p == null) - return; - - p.addControllerListener(this); - p.realize(); - jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : ""); - - // Notify intialize() that a new stream had arrived. - synchronized (dataSync) { - dataReceived = true; - dataSync.notifyAll(); - } - - } - catch (Exception e) { - LOGGER.error("NewReceiveStreamEvent exception " + e.getMessage()); - return; - } - - } - else if (evt instanceof StreamMappedEvent) { - - if (stream != null && stream.getDataSource() != null) { - DataSource ds = stream.getDataSource(); - // Find out the formats. - RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); - LOGGER.error(" - The previously unidentified stream "); - if (ctl != null) - LOGGER.error(" " + ctl.getFormat()); - LOGGER.error(" had now been identified as sent by: " + participant.getCNAME()); - } - } - else if (evt instanceof ByeEvent) { - - LOGGER.error(" - Got \"bye\" from: " + participant.getCNAME()); - - } - - } - - /** - * ControllerListener for the Players. - */ - public synchronized void controllerUpdate(ControllerEvent ce) { - - Player p = (Player) ce.getSourceController(); - - if (p == null) - return; - - // Get this when the internal players are realized. - if (ce instanceof RealizeCompleteEvent) { - p.start(); - } - - if (ce instanceof ControllerErrorEvent) { - p.removeControllerListener(this); - LOGGER.error("Receiver internal error: " + ce); - } - - } -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.mediaimpl.jmf; + +import javax.media.ControllerErrorEvent; +import javax.media.ControllerEvent; +import javax.media.ControllerListener; +import javax.media.Player; +import javax.media.RealizeCompleteEvent; +import javax.media.protocol.DataSource; +import javax.media.rtp.Participant; +import javax.media.rtp.RTPControl; +import javax.media.rtp.ReceiveStream; +import javax.media.rtp.ReceiveStreamListener; +import javax.media.rtp.SessionListener; +import javax.media.rtp.event.ByeEvent; +import javax.media.rtp.event.NewParticipantEvent; +import javax.media.rtp.event.NewReceiveStreamEvent; +import javax.media.rtp.event.ReceiveStreamEvent; +import javax.media.rtp.event.RemotePayloadChangeEvent; +import javax.media.rtp.event.SessionEvent; +import javax.media.rtp.event.StreamMappedEvent; + +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; + +/** + * This class implements receive methods and listeners to be used in AudioChannel + * + * @author Thiago Camargo + */ +public class AudioReceiver implements ReceiveStreamListener, SessionListener, + ControllerListener { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioReceiver.class); + + boolean dataReceived = false; + + Object dataSync; + JingleMediaSession jingleMediaSession; + + public AudioReceiver(final Object dataSync, final JingleMediaSession jingleMediaSession) { + this.dataSync = dataSync; + this.jingleMediaSession = jingleMediaSession; + } + + /** + * JingleSessionListener. + */ + public synchronized void update(SessionEvent evt) { + if (evt instanceof NewParticipantEvent) { + Participant p = ((NewParticipantEvent) evt).getParticipant(); + LOGGER.error(" - A new participant had just joined: " + p.getCNAME()); + } + } + + /** + * ReceiveStreamListener + */ + public synchronized void update(ReceiveStreamEvent evt) { + + Participant participant = evt.getParticipant(); // could be null. + ReceiveStream stream = evt.getReceiveStream(); // could be null. + + if (evt instanceof RemotePayloadChangeEvent) { + LOGGER.error(" - Received an RTP PayloadChangeEvent."); + LOGGER.error("Sorry, cannot handle payload change."); + + } + else if (evt instanceof NewReceiveStreamEvent) { + + try { + stream = evt.getReceiveStream(); + DataSource ds = stream.getDataSource(); + + // Find out the formats. + RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); + if (ctl != null) { + LOGGER.error(" - Recevied new RTP stream: " + ctl.getFormat()); + } + else + LOGGER.error(" - Recevied new RTP stream"); + + if (participant == null) + LOGGER.error(" The sender of this stream had yet to be identified."); + else { + LOGGER.error(" The stream comes from: " + participant.getCNAME()); + } + + // create a player by passing datasource to the Media Manager + Player p = javax.media.Manager.createPlayer(ds); + if (p == null) + return; + + p.addControllerListener(this); + p.realize(); + jingleMediaSession.mediaReceived(participant != null ? participant.getCNAME() : ""); + + // Notify intialize() that a new stream had arrived. + synchronized (dataSync) { + dataReceived = true; + dataSync.notifyAll(); + } + + } + catch (Exception e) { + LOGGER.error("NewReceiveStreamEvent exception " + e.getMessage()); + return; + } + + } + else if (evt instanceof StreamMappedEvent) { + + if (stream != null && stream.getDataSource() != null) { + DataSource ds = stream.getDataSource(); + // Find out the formats. + RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl"); + LOGGER.error(" - The previously unidentified stream "); + if (ctl != null) + LOGGER.error(" " + ctl.getFormat()); + LOGGER.error(" had now been identified as sent by: " + participant.getCNAME()); + } + } + else if (evt instanceof ByeEvent) { + + LOGGER.error(" - Got \"bye\" from: " + participant.getCNAME()); + + } + + } + + /** + * ControllerListener for the Players. + */ + public synchronized void controllerUpdate(ControllerEvent ce) { + + Player p = (Player) ce.getSourceController(); + + if (p == null) + return; + + // Get this when the internal players are realized. + if (ce instanceof RealizeCompleteEvent) { + p.start(); + } + + if (ce instanceof ControllerErrorEvent) { + p.removeControllerListener(this); + LOGGER.error("Receiver internal error: " + ce); + } + + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java index 53c5781d8..a8a2ad2b4 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jmf/JmfMediaManager.java @@ -1,167 +1,167 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jmf; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; - -/** - * Implements a jingleMediaManager using JMF based API. - * It supports GSM and G723 codecs. - * This API only currently works on windows and Mac. - * - * @author Thiago Camargo - */ -public class JmfMediaManager extends JingleMediaManager { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class); - - public static final String MEDIA_NAME = "JMF"; - - - private List payloads = new ArrayList(); - private String mediaLocator = null; - - /** - * Creates a Media Manager instance - */ - public JmfMediaManager(JingleTransportManager transportManager) { - super(transportManager); - setupPayloads(); - } - - /** - * Creates a Media Manager instance - * - * @param mediaLocator Media Locator - */ - public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) { - super(transportManager); - this.mediaLocator = mediaLocator; - setupPayloads(); - } - - /** - * Returns a new jingleMediaSession - * - * @param payloadType payloadType - * @param remote remote Candidate - * @param local local Candidate - * @return JingleMediaSession - */ - public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { - return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession); - } - - /** - * Setup API supported Payloads - */ - private void setupPayloads() { - payloads.add(new PayloadType.Audio(3, "gsm")); - payloads.add(new PayloadType.Audio(4, "g723")); - payloads.add(new PayloadType.Audio(0, "PCMU", 16000)); - } - - /** - * Return all supported Payloads for this Manager - * - * @return The Payload List - */ - public List getPayloads() { - return payloads; - } - - /** - * Return the media locator or null if not defined - * - * @return media locator - */ - public String getMediaLocator() { - return mediaLocator; - } - - /** - * Set the media locator - * - * @param mediaLocator media locator or null to use default - */ - public void setMediaLocator(String mediaLocator) { - this.mediaLocator = mediaLocator; - } - - /** - * Runs JMFInit the first time the application is started so that capture - * devices are properly detected and initialized by JMF. - */ - public static void setupJMF() { - // .jmf is the place where we store the jmf.properties file used - // by JMF. if the directory does not exist or it does not contain - // a jmf.properties file. or if the jmf.properties file has 0 length - // then this is the first time we're running and should continue to - // with JMFInit - String homeDir = System.getProperty("user.home"); - File jmfDir = new File(homeDir, ".jmf"); - String classpath = System.getProperty("java.class.path"); - classpath += System.getProperty("path.separator") - + jmfDir.getAbsolutePath(); - System.setProperty("java.class.path", classpath); - - if (!jmfDir.exists()) - jmfDir.mkdir(); - - File jmfProperties = new File(jmfDir, "jmf.properties"); - - if (!jmfProperties.exists()) { - try { - jmfProperties.createNewFile(); - } - catch (IOException ex) { - LOGGER.debug("Failed to create jmf.properties"); - ex.printStackTrace(); - } - } - - // if we're running on linux checkout that libjmutil.so is where it - // should be and put it there. - runLinuxPreInstall(); - - //if (jmfProperties.length() == 0) { - new JMFInit(null, false); - //} - - } - - private static void runLinuxPreInstall() { - // @TODO Implement Linux Pre-Install - } - - public String getName() { - return MEDIA_NAME; - } -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.mediaimpl.jmf; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +/** + * Implements a jingleMediaManager using JMF based API. + * It supports GSM and G723 codecs. + * This API only currently works on windows and Mac. + * + * @author Thiago Camargo + */ +public class JmfMediaManager extends JingleMediaManager { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(JmfMediaManager.class); + + public static final String MEDIA_NAME = "JMF"; + + + private List payloads = new ArrayList(); + private String mediaLocator = null; + + /** + * Creates a Media Manager instance + */ + public JmfMediaManager(JingleTransportManager transportManager) { + super(transportManager); + setupPayloads(); + } + + /** + * Creates a Media Manager instance + * + * @param mediaLocator Media Locator + */ + public JmfMediaManager(String mediaLocator, JingleTransportManager transportManager) { + super(transportManager); + this.mediaLocator = mediaLocator; + setupPayloads(); + } + + /** + * Returns a new jingleMediaSession + * + * @param payloadType payloadType + * @param remote remote Candidate + * @param local local Candidate + * @return JingleMediaSession + */ + public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { + return new AudioMediaSession(payloadType, remote, local, mediaLocator, jingleSession); + } + + /** + * Setup API supported Payloads + */ + private void setupPayloads() { + payloads.add(new PayloadType.Audio(3, "gsm")); + payloads.add(new PayloadType.Audio(4, "g723")); + payloads.add(new PayloadType.Audio(0, "PCMU", 16000)); + } + + /** + * Return all supported Payloads for this Manager + * + * @return The Payload List + */ + public List getPayloads() { + return payloads; + } + + /** + * Return the media locator or null if not defined + * + * @return media locator + */ + public String getMediaLocator() { + return mediaLocator; + } + + /** + * Set the media locator + * + * @param mediaLocator media locator or null to use default + */ + public void setMediaLocator(String mediaLocator) { + this.mediaLocator = mediaLocator; + } + + /** + * Runs JMFInit the first time the application is started so that capture + * devices are properly detected and initialized by JMF. + */ + public static void setupJMF() { + // .jmf is the place where we store the jmf.properties file used + // by JMF. if the directory does not exist or it does not contain + // a jmf.properties file. or if the jmf.properties file has 0 length + // then this is the first time we're running and should continue to + // with JMFInit + String homeDir = System.getProperty("user.home"); + File jmfDir = new File(homeDir, ".jmf"); + String classpath = System.getProperty("java.class.path"); + classpath += System.getProperty("path.separator") + + jmfDir.getAbsolutePath(); + System.setProperty("java.class.path", classpath); + + if (!jmfDir.exists()) + jmfDir.mkdir(); + + File jmfProperties = new File(jmfDir, "jmf.properties"); + + if (!jmfProperties.exists()) { + try { + jmfProperties.createNewFile(); + } + catch (IOException ex) { + LOGGER.debug("Failed to create jmf.properties"); + ex.printStackTrace(); + } + } + + // if we're running on linux checkout that libjmutil.so is where it + // should be and put it there. + runLinuxPreInstall(); + + //if (jmfProperties.length() == 0) { + new JMFInit(null, false); + //} + + } + + private static void runLinuxPreInstall() { + // @TODO Implement Linux Pre-Install + } + + public String getName() { + return MEDIA_NAME; + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java index 070b7296f..a2086d953 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/AudioMediaSession.java @@ -1,242 +1,242 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; - -import java.io.IOException; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.security.GeneralSecurityException; - -import javax.media.NoProcessorException; -import javax.media.format.UnsupportedFormatException; -import javax.media.rtp.rtcp.SenderReport; -import javax.media.rtp.rtcp.SourceDescription; - -import mil.jfcom.cie.media.session.MediaSession; -import mil.jfcom.cie.media.session.MediaSessionListener; -import mil.jfcom.cie.media.session.StreamPlayer; -import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; - -/** - * This Class implements a complete JingleMediaSession. - * It sould be used to transmit and receive audio captured from the Mic. - * This Class should be automaticly controlled by JingleSession. - * But you could also use in any VOIP application. - * For better NAT Traversal support this implementation don't support only receive or only transmit. - * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() - * - * @author Thiago Camargo - */ - -public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); - - private MediaSession mediaSession; - - /** - * Create a Session using Speex Codec - * - * @param localhost localHost - * @param localPort localPort - * @param remoteHost remoteHost - * @param remotePort remotePort - * @param eventHandler eventHandler - * @param quality quality - * @param secure secure - * @param micOn micOn - * @return MediaSession - * @throws NoProcessorException - * @throws UnsupportedFormatException - * @throws IOException - * @throws GeneralSecurityException - */ - public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException { - - SpeexFormat.setFramesPerPacket(1); - /** - * The master key. Hardcoded for now. - */ - byte[] masterKey = new byte[]{(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39}; - - /** - * The master salt. Hardcoded for now. - */ - byte[] masterSalt = new byte[]{0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6}; - - DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort); - MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt); - session.setListener(eventHandler); - - session.setSourceDescription(new SourceDescription[]{new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "Superman", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "cdcie.tester@je.jfcom.mil", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_LOC, InetAddress.getByName(localhost) + " Port " + session.getLocalDataPort(), 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JFCOM CDCIE Audio Chat", 1, false)}); - return session; - } - - - /** - * Creates a org.jivesoftware.jingleaudio.jspeex.AudioMediaSession with defined payload type, remote and local candidates - * - * @param payloadType Payload of the jmf - * @param remote the remote information. The candidate that the jmf will be sent to. - * @param local the local information. The candidate that will receive the jmf - * @param locator media locator - */ - public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, - final TransportCandidate local, String locator, JingleSession jingleSession) { - super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); - initialize(); - } - - /** - * Initialize the Audio Channel to make it able to send and receive audio - */ - public void initialize() { - - String ip; - String localIp; - int localPort; - int remotePort; - - if (this.getLocal().getSymmetric() != null) { - ip = this.getLocal().getIp(); - localIp = this.getLocal().getLocalIp(); - localPort = getFreePort(); - remotePort = this.getLocal().getSymmetric().getPort(); - - LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); - - } - else { - ip = this.getRemote().getIp(); - localIp = this.getLocal().getLocalIp(); - localPort = this.getLocal().getPort(); - remotePort = this.getRemote().getPort(); - } - - try { - mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true); - } - catch (NoProcessorException e) { - e.printStackTrace(); - } - catch (UnsupportedFormatException e) { - e.printStackTrace(); - } - catch (IOException e) { - e.printStackTrace(); - } - catch (GeneralSecurityException e) { - e.printStackTrace(); - } - } - - /** - * Starts transmission and for NAT Traversal reasons start receiving also. - */ - public void startTrasmit() { - try { - LOGGER.debug("start"); - mediaSession.start(true); - this.mediaReceived(""); - } - catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Set transmit activity. If the active is true, the instance should trasmit. - * If it is set to false, the instance should pause transmit. - * - * @param active active state - */ - public void setTrasmit(boolean active) { - // Do nothing - } - - /** - * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf - */ - public void startReceive() { - // Do nothing - } - - /** - * Stops transmission and for NAT Traversal reasons stop receiving also. - */ - public void stopTrasmit() { - if (mediaSession != null) - mediaSession.close(); - } - - /** - * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf - */ - public void stopReceive() { - // Do nothing - } - - public void newStreamIdentified(StreamPlayer streamPlayer) { - } - - public void senderReportReceived(SenderReport report) { - } - - public void streamClosed(StreamPlayer stream, boolean timeout) { - } - - /** - * Obtain a free port we can use. - * - * @return A free port number. - */ - protected int getFreePort() { - ServerSocket ss; - int freePort = 0; - - for (int i = 0; i < 10; i++) { - freePort = (int) (10000 + Math.round(Math.random() * 10000)); - freePort = freePort % 2 == 0 ? freePort : freePort + 1; - try { - ss = new ServerSocket(freePort); - freePort = ss.getLocalPort(); - ss.close(); - return freePort; - } - catch (IOException e) { - e.printStackTrace(); - } - } - try { - ss = new ServerSocket(0); - freePort = ss.getLocalPort(); - ss.close(); - } - catch (IOException e) { - e.printStackTrace(); - } - return freePort; - } -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.security.GeneralSecurityException; + +import javax.media.NoProcessorException; +import javax.media.format.UnsupportedFormatException; +import javax.media.rtp.rtcp.SenderReport; +import javax.media.rtp.rtcp.SourceDescription; + +import mil.jfcom.cie.media.session.MediaSession; +import mil.jfcom.cie.media.session.MediaSessionListener; +import mil.jfcom.cie.media.session.StreamPlayer; +import mil.jfcom.cie.media.srtp.packetizer.SpeexFormat; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +/** + * This Class implements a complete JingleMediaSession. + * It sould be used to transmit and receive audio captured from the Mic. + * This Class should be automaticly controlled by JingleSession. + * But you could also use in any VOIP application. + * For better NAT Traversal support this implementation don't support only receive or only transmit. + * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() + * + * @author Thiago Camargo + */ + +public class AudioMediaSession extends JingleMediaSession implements MediaSessionListener { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(AudioMediaSession.class); + + private MediaSession mediaSession; + + /** + * Create a Session using Speex Codec + * + * @param localhost localHost + * @param localPort localPort + * @param remoteHost remoteHost + * @param remotePort remotePort + * @param eventHandler eventHandler + * @param quality quality + * @param secure secure + * @param micOn micOn + * @return MediaSession + * @throws NoProcessorException + * @throws UnsupportedFormatException + * @throws IOException + * @throws GeneralSecurityException + */ + public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException { + + SpeexFormat.setFramesPerPacket(1); + /** + * The master key. Hardcoded for now. + */ + byte[] masterKey = new byte[]{(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39}; + + /** + * The master salt. Hardcoded for now. + */ + byte[] masterSalt = new byte[]{0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6}; + + DatagramSocket[] localPorts = MediaSession.getLocalPorts(InetAddress.getByName(localhost), localPort); + MediaSession session = MediaSession.createInstance(remoteHost, remotePort, localPorts, quality, secure, masterKey, masterSalt); + session.setListener(eventHandler); + + session.setSourceDescription(new SourceDescription[]{new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "Superman", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "cdcie.tester@je.jfcom.mil", 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_LOC, InetAddress.getByName(localhost) + " Port " + session.getLocalDataPort(), 1, false), new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JFCOM CDCIE Audio Chat", 1, false)}); + return session; + } + + + /** + * Creates a org.jivesoftware.jingleaudio.jspeex.AudioMediaSession with defined payload type, remote and local candidates + * + * @param payloadType Payload of the jmf + * @param remote the remote information. The candidate that the jmf will be sent to. + * @param local the local information. The candidate that will receive the jmf + * @param locator media locator + */ + public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, + final TransportCandidate local, String locator, JingleSession jingleSession) { + super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); + initialize(); + } + + /** + * Initialize the Audio Channel to make it able to send and receive audio + */ + public void initialize() { + + String ip; + String localIp; + int localPort; + int remotePort; + + if (this.getLocal().getSymmetric() != null) { + ip = this.getLocal().getIp(); + localIp = this.getLocal().getLocalIp(); + localPort = getFreePort(); + remotePort = this.getLocal().getSymmetric().getPort(); + + LOGGER.debug(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); + + } + else { + ip = this.getRemote().getIp(); + localIp = this.getLocal().getLocalIp(); + localPort = this.getLocal().getPort(); + remotePort = this.getRemote().getPort(); + } + + try { + mediaSession = createSession(localIp, localPort, ip, remotePort, this, 2, false, true); + } + catch (NoProcessorException e) { + e.printStackTrace(); + } + catch (UnsupportedFormatException e) { + e.printStackTrace(); + } + catch (IOException e) { + e.printStackTrace(); + } + catch (GeneralSecurityException e) { + e.printStackTrace(); + } + } + + /** + * Starts transmission and for NAT Traversal reasons start receiving also. + */ + public void startTrasmit() { + try { + LOGGER.debug("start"); + mediaSession.start(true); + this.mediaReceived(""); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Set transmit activity. If the active is true, the instance should trasmit. + * If it is set to false, the instance should pause transmit. + * + * @param active active state + */ + public void setTrasmit(boolean active) { + // Do nothing + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void startReceive() { + // Do nothing + } + + /** + * Stops transmission and for NAT Traversal reasons stop receiving also. + */ + public void stopTrasmit() { + if (mediaSession != null) + mediaSession.close(); + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void stopReceive() { + // Do nothing + } + + public void newStreamIdentified(StreamPlayer streamPlayer) { + } + + public void senderReportReceived(SenderReport report) { + } + + public void streamClosed(StreamPlayer stream, boolean timeout) { + } + + /** + * Obtain a free port we can use. + * + * @return A free port number. + */ + protected int getFreePort() { + ServerSocket ss; + int freePort = 0; + + for (int i = 0; i < 10; i++) { + freePort = (int) (10000 + Math.round(Math.random() * 10000)); + freePort = freePort % 2 == 0 ? freePort : freePort + 1; + try { + ss = new ServerSocket(freePort); + freePort = ss.getLocalPort(); + ss.close(); + return freePort; + } + catch (IOException e) { + e.printStackTrace(); + } + } + try { + ss = new ServerSocket(0); + freePort = ss.getLocalPort(); + ss.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + return freePort; + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java index ac0d01ef7..2ee6a851a 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/jspeex/SpeexMediaManager.java @@ -1,131 +1,131 @@ -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.media.JingleMediaSession; -import org.jivesoftware.smackx.jingle.media.PayloadType; -import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; -import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; -import org.jivesoftware.smackx.jingle.nat.TransportCandidate; - -/** - * Implements a jingleMediaManager using JMF based API and JSpeex. - * It supports Speex codec. - * This API only currently works on windows. - * - * @author Thiago Camargo - */ -public class SpeexMediaManager extends JingleMediaManager { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class); - - public static final String MEDIA_NAME = "Speex"; - - private List payloads = new ArrayList(); - - public SpeexMediaManager(JingleTransportManager transportManager) { - super(transportManager); - setupPayloads(); - setupJMF(); - } - - /** - * Returns a new jingleMediaSession - * - * @param payloadType payloadType - * @param remote remote Candidate - * @param local local Candidate - * @return JingleMediaSession - */ - public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { - return new AudioMediaSession(payloadType, remote, local, null,null); - } - - /** - * Setup API supported Payloads - */ - private void setupPayloads() { - payloads.add(new PayloadType.Audio(15, "speex")); - } - - /** - * Return all supported Payloads for this Manager - * - * @return The Payload List - */ - public List getPayloads() { - return payloads; - } - - /** - * Runs JMFInit the first time the application is started so that capture - * devices are properly detected and initialized by JMF. - */ - public static void setupJMF() { - // .jmf is the place where we store the jmf.properties file used - // by JMF. if the directory does not exist or it does not contain - // a jmf.properties file. or if the jmf.properties file has 0 length - // then this is the first time we're running and should continue to - // with JMFInit - String homeDir = System.getProperty("user.home"); - File jmfDir = new File(homeDir, ".jmf"); - String classpath = System.getProperty("java.class.path"); - classpath += System.getProperty("path.separator") - + jmfDir.getAbsolutePath(); - System.setProperty("java.class.path", classpath); - - if (!jmfDir.exists()) - jmfDir.mkdir(); - - File jmfProperties = new File(jmfDir, "jmf.properties"); - - if (!jmfProperties.exists()) { - try { - jmfProperties.createNewFile(); - } - catch (IOException ex) { - LOGGER.debug("Failed to create jmf.properties"); - ex.printStackTrace(); - } - } - - // if we're running on linux checkout that libjmutil.so is where it - // should be and put it there. - runLinuxPreInstall(); - - if (jmfProperties.length() == 0) { - new JMFInit(null, false); - } - - } - - private static void runLinuxPreInstall() { - // @TODO Implement Linux Pre-Install - } - - public String getName() { - return MEDIA_NAME; - } -} +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.mediaimpl.jspeex; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.mediaimpl.JMFInit; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +/** + * Implements a jingleMediaManager using JMF based API and JSpeex. + * It supports Speex codec. + * This API only currently works on windows. + * + * @author Thiago Camargo + */ +public class SpeexMediaManager extends JingleMediaManager { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(SpeexMediaManager.class); + + public static final String MEDIA_NAME = "Speex"; + + private List payloads = new ArrayList(); + + public SpeexMediaManager(JingleTransportManager transportManager) { + super(transportManager); + setupPayloads(); + setupJMF(); + } + + /** + * Returns a new jingleMediaSession + * + * @param payloadType payloadType + * @param remote remote Candidate + * @param local local Candidate + * @return JingleMediaSession + */ + public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final JingleSession jingleSession) { + return new AudioMediaSession(payloadType, remote, local, null,null); + } + + /** + * Setup API supported Payloads + */ + private void setupPayloads() { + payloads.add(new PayloadType.Audio(15, "speex")); + } + + /** + * Return all supported Payloads for this Manager + * + * @return The Payload List + */ + public List getPayloads() { + return payloads; + } + + /** + * Runs JMFInit the first time the application is started so that capture + * devices are properly detected and initialized by JMF. + */ + public static void setupJMF() { + // .jmf is the place where we store the jmf.properties file used + // by JMF. if the directory does not exist or it does not contain + // a jmf.properties file. or if the jmf.properties file has 0 length + // then this is the first time we're running and should continue to + // with JMFInit + String homeDir = System.getProperty("user.home"); + File jmfDir = new File(homeDir, ".jmf"); + String classpath = System.getProperty("java.class.path"); + classpath += System.getProperty("path.separator") + + jmfDir.getAbsolutePath(); + System.setProperty("java.class.path", classpath); + + if (!jmfDir.exists()) + jmfDir.mkdir(); + + File jmfProperties = new File(jmfDir, "jmf.properties"); + + if (!jmfProperties.exists()) { + try { + jmfProperties.createNewFile(); + } + catch (IOException ex) { + LOGGER.debug("Failed to create jmf.properties"); + ex.printStackTrace(); + } + } + + // if we're running on linux checkout that libjmutil.so is where it + // should be and put it there. + runLinuxPreInstall(); + + if (jmfProperties.length() == 0) { + new JMFInit(null, false); + } + + } + + private static void runLinuxPreInstall() { + // @TODO Implement Linux Pre-Install + } + + public String getName() { + return MEDIA_NAME; + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java index 08a85bc31..3e0a741d8 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageReceiver.java @@ -12,154 +12,154 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; - -/** - * UDP Image Receiver. - * It uses PNG Tiles into UDP packets. - * - * @author Thiago Rocha Camargo - */ -public class ImageReceiver extends Canvas { - - private static final long serialVersionUID = -7000112305305269025L; - private boolean on = true; - private DatagramSocket socket; - private BufferedImage tiles[][]; - private static final int tileWidth = ImageTransmitter.tileWidth; - private InetAddress localHost; - private InetAddress remoteHost; - private int localPort; - private int remotePort; - private ImageDecoder decoder; - - public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) { - tiles = new BufferedImage[width][height]; - - try { - - socket = new DatagramSocket(localPort); - localHost = socket.getLocalAddress(); - this.remoteHost = remoteHost; - this.remotePort = remotePort; - this.localPort = localPort; - this.decoder = new DefaultDecoder(); - - new Thread(new Runnable() { - public void run() { - byte buf[] = new byte[1024]; - DatagramPacket p = new DatagramPacket(buf, 1024); - try { - while (on) { - socket.receive(p); - - int length = p.getLength(); - - BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2)); - - if (bufferedImage != null) { - - int x = p.getData()[length - 2]; - int y = p.getData()[length - 1]; - - drawTile(x, y, bufferedImage); - - } - - } - } - catch (IOException e) { - e.printStackTrace(); - } - } - }).start(); - - new Thread(new Runnable() { - public void run() { - byte buf[] = new byte[1024]; - DatagramPacket p = new DatagramPacket(buf, 1024); - try { - while (on) { - - p.setAddress(remoteHost); - p.setPort(remotePort); - socket.send(p); - - try { - Thread.sleep(1000); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - } - } - catch (IOException e) { - e.printStackTrace(); - } - } - }).start(); - - } - catch (SocketException e) { - e.printStackTrace(); - } - this.setSize(width, height); - } - - public InetAddress getLocalHost() { - return localHost; - } - - public InetAddress getRemoteHost() { - return remoteHost; - } - - public int getLocalPort() { - return localPort; - } - - public int getRemotePort() { - return remotePort; - } - - public DatagramSocket getDatagramSocket() { - return socket; - } - - public void drawTile(int x, int y, BufferedImage bufferedImage) { - tiles[x][y] = bufferedImage; - //repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth); - this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this); - } - - public void paint(Graphics g) { - for (int i = 0; i < tiles.length; i++) { - for (int j = 0; j < tiles[0].length; j++) { - g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this); - } - } - } - - public ImageDecoder getDecoder() { - return decoder; - } - - public void setDecoder(ImageDecoder decoder) { - this.decoder = decoder; - } - - public void stop(){ - this.on=false; - socket.close(); - } -} +package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +/** + * UDP Image Receiver. + * It uses PNG Tiles into UDP packets. + * + * @author Thiago Rocha Camargo + */ +public class ImageReceiver extends Canvas { + + private static final long serialVersionUID = -7000112305305269025L; + private boolean on = true; + private DatagramSocket socket; + private BufferedImage tiles[][]; + private static final int tileWidth = ImageTransmitter.tileWidth; + private InetAddress localHost; + private InetAddress remoteHost; + private int localPort; + private int remotePort; + private ImageDecoder decoder; + + public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) { + tiles = new BufferedImage[width][height]; + + try { + + socket = new DatagramSocket(localPort); + localHost = socket.getLocalAddress(); + this.remoteHost = remoteHost; + this.remotePort = remotePort; + this.localPort = localPort; + this.decoder = new DefaultDecoder(); + + new Thread(new Runnable() { + public void run() { + byte buf[] = new byte[1024]; + DatagramPacket p = new DatagramPacket(buf, 1024); + try { + while (on) { + socket.receive(p); + + int length = p.getLength(); + + BufferedImage bufferedImage = decoder.decode(new ByteArrayInputStream(p.getData(), 0, length - 2)); + + if (bufferedImage != null) { + + int x = p.getData()[length - 2]; + int y = p.getData()[length - 1]; + + drawTile(x, y, bufferedImage); + + } + + } + } + catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + + new Thread(new Runnable() { + public void run() { + byte buf[] = new byte[1024]; + DatagramPacket p = new DatagramPacket(buf, 1024); + try { + while (on) { + + p.setAddress(remoteHost); + p.setPort(remotePort); + socket.send(p); + + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + } + } + catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + + } + catch (SocketException e) { + e.printStackTrace(); + } + this.setSize(width, height); + } + + public InetAddress getLocalHost() { + return localHost; + } + + public InetAddress getRemoteHost() { + return remoteHost; + } + + public int getLocalPort() { + return localPort; + } + + public int getRemotePort() { + return remotePort; + } + + public DatagramSocket getDatagramSocket() { + return socket; + } + + public void drawTile(int x, int y, BufferedImage bufferedImage) { + tiles[x][y] = bufferedImage; + //repaint(x * tileWidth, y * tileWidth, tileWidth, tileWidth); + this.getGraphics().drawImage(bufferedImage, tileWidth * x, tileWidth * y, this); + } + + public void paint(Graphics g) { + for (int i = 0; i < tiles.length; i++) { + for (int j = 0; j < tiles[0].length; j++) { + g.drawImage(tiles[i][j], tileWidth * i, tileWidth * j, this); + } + } + } + + public ImageDecoder getDecoder() { + return decoder; + } + + public void setDecoder(ImageDecoder decoder) { + this.decoder = decoder; + } + + public void stop(){ + this.on=false; + socket.close(); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java index 0dac307c5..7a9cb4546 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/mediaimpl/sshare/api/ImageTransmitter.java @@ -12,207 +12,207 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; - -import java.awt.AWTException; -import java.awt.Rectangle; -import java.awt.Robot; -import java.awt.image.BufferedImage; -import java.awt.image.PixelGrabber; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.util.Arrays; - -import org.jivesoftware.smackx.jingle.SmackLogger; - -/** - * UDP Image Receiver. - * It uses PNG Tiles into UDP packets. - * - * @author Thiago Rocha Camargo - */ -public class ImageTransmitter implements Runnable { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class); - - private Robot robot; - private InetAddress localHost; - private InetAddress remoteHost; - private int localPort; - private int remotePort; - public static final int tileWidth = 25; - private boolean on = true; - private boolean transmit = false; - private DatagramSocket socket; - private Rectangle area; - private int tiles[][][]; - private int maxI; - private int maxJ; - private ImageEncoder encoder; - public final static int KEYFRAME = 10; - - public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) { - - try { - robot = new Robot(); - - maxI = (int) Math.ceil(area.getWidth() / tileWidth); - maxJ = (int) Math.ceil(area.getHeight() / tileWidth); - - tiles = new int[maxI][maxJ][tileWidth * tileWidth]; - - this.area = area; - this.socket = socket; - localHost = socket.getLocalAddress(); - localPort = socket.getLocalPort(); - this.remoteHost = remoteHost; - this.remotePort = remotePort; - this.encoder = new DefaultEncoder(); - - transmit = true; - - } - catch (AWTException e) { - e.printStackTrace(); - } - - } - - public void start() { - byte buf[] = new byte[1024]; - final DatagramPacket p = new DatagramPacket(buf, 1024); - - int keyframe = 0; - - while (on) { - if (transmit) { - - BufferedImage capture = robot.createScreenCapture(area); - - QuantizeFilter filter = new QuantizeFilter(); - capture = filter.filter(capture, null); - - long trace = System.currentTimeMillis(); - - if (++keyframe > KEYFRAME) { - keyframe = 0; - } - LOGGER.debug("KEYFRAME:" + keyframe); - - for (int i = 0; i < maxI; i++) { - for (int j = 0; j < maxJ; j++) { - - final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth); - - int pixels[] = new int[tileWidth * tileWidth]; - - PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth); - - try { - if (pg.grabPixels()) { - - if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) { - - ByteArrayOutputStream baos = encoder.encode(bufferedImage); - - if (baos != null) { - - try { - - Thread.sleep(1); - - baos.write(i); - baos.write(j); - - byte[] bytesOut = baos.toByteArray(); - - if (bytesOut.length > 1000) - LOGGER.error("Bytes out > 1000. Equals " + bytesOut.length); - - p.setData(bytesOut); - p.setAddress(remoteHost); - p.setPort(remotePort); - - try { - socket.send(p); - } - catch (IOException e) { - e.printStackTrace(); - } - - tiles[i][j] = pixels; - - } - catch (Exception e) { - } - - } - - } - - } - } - catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - trace = (System.currentTimeMillis() - trace); - LOGGER.debug("Loop Time:" + trace); - - if (trace < 500) { - try { - Thread.sleep(500 - trace); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - } - - public void run() { - start(); - } - - /** - * Set Transmit Enabled/Disabled - * - * @param transmit boolean Enabled/Disabled - */ - public void setTransmit(boolean transmit) { - this.transmit = transmit; - } - - /** - * Get the encoder used to encode Images Tiles - * - * @return encoder - */ - public ImageEncoder getEncoder() { - return encoder; - } - - /** - * Set the encoder used to encode Image Tiles - * - * @param encoder encoder - */ - public void setEncoder(ImageEncoder encoder) { - this.encoder = encoder; - } - - /** - * Stops Transmitter - */ - public void stop() { - this.transmit = false; - this.on = false; - socket.close(); - } -} +package org.jivesoftware.smackx.jingle.mediaimpl.sshare.api; + +import java.awt.AWTException; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.awt.image.PixelGrabber; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.Arrays; + +import org.jivesoftware.smackx.jingle.SmackLogger; + +/** + * UDP Image Receiver. + * It uses PNG Tiles into UDP packets. + * + * @author Thiago Rocha Camargo + */ +public class ImageTransmitter implements Runnable { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(ImageTransmitter.class); + + private Robot robot; + private InetAddress localHost; + private InetAddress remoteHost; + private int localPort; + private int remotePort; + public static final int tileWidth = 25; + private boolean on = true; + private boolean transmit = false; + private DatagramSocket socket; + private Rectangle area; + private int tiles[][][]; + private int maxI; + private int maxJ; + private ImageEncoder encoder; + public final static int KEYFRAME = 10; + + public ImageTransmitter(DatagramSocket socket, InetAddress remoteHost, int remotePort, Rectangle area) { + + try { + robot = new Robot(); + + maxI = (int) Math.ceil(area.getWidth() / tileWidth); + maxJ = (int) Math.ceil(area.getHeight() / tileWidth); + + tiles = new int[maxI][maxJ][tileWidth * tileWidth]; + + this.area = area; + this.socket = socket; + localHost = socket.getLocalAddress(); + localPort = socket.getLocalPort(); + this.remoteHost = remoteHost; + this.remotePort = remotePort; + this.encoder = new DefaultEncoder(); + + transmit = true; + + } + catch (AWTException e) { + e.printStackTrace(); + } + + } + + public void start() { + byte buf[] = new byte[1024]; + final DatagramPacket p = new DatagramPacket(buf, 1024); + + int keyframe = 0; + + while (on) { + if (transmit) { + + BufferedImage capture = robot.createScreenCapture(area); + + QuantizeFilter filter = new QuantizeFilter(); + capture = filter.filter(capture, null); + + long trace = System.currentTimeMillis(); + + if (++keyframe > KEYFRAME) { + keyframe = 0; + } + LOGGER.debug("KEYFRAME:" + keyframe); + + for (int i = 0; i < maxI; i++) { + for (int j = 0; j < maxJ; j++) { + + final BufferedImage bufferedImage = capture.getSubimage(i * tileWidth, j * tileWidth, tileWidth, tileWidth); + + int pixels[] = new int[tileWidth * tileWidth]; + + PixelGrabber pg = new PixelGrabber(bufferedImage, 0, 0, tileWidth, tileWidth, pixels, 0, tileWidth); + + try { + if (pg.grabPixels()) { + + if (keyframe == KEYFRAME || !Arrays.equals(tiles[i][j], pixels)) { + + ByteArrayOutputStream baos = encoder.encode(bufferedImage); + + if (baos != null) { + + try { + + Thread.sleep(1); + + baos.write(i); + baos.write(j); + + byte[] bytesOut = baos.toByteArray(); + + if (bytesOut.length > 1000) + LOGGER.error("Bytes out > 1000. Equals " + bytesOut.length); + + p.setData(bytesOut); + p.setAddress(remoteHost); + p.setPort(remotePort); + + try { + socket.send(p); + } + catch (IOException e) { + e.printStackTrace(); + } + + tiles[i][j] = pixels; + + } + catch (Exception e) { + } + + } + + } + + } + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + trace = (System.currentTimeMillis() - trace); + LOGGER.debug("Loop Time:" + trace); + + if (trace < 500) { + try { + Thread.sleep(500 - trace); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + + public void run() { + start(); + } + + /** + * Set Transmit Enabled/Disabled + * + * @param transmit boolean Enabled/Disabled + */ + public void setTransmit(boolean transmit) { + this.transmit = transmit; + } + + /** + * Get the encoder used to encode Images Tiles + * + * @return encoder + */ + public ImageEncoder getEncoder() { + return encoder; + } + + /** + * Set the encoder used to encode Image Tiles + * + * @param encoder encoder + */ + public void setEncoder(ImageEncoder encoder) { + this.encoder = encoder; + } + + /** + * Stops Transmitter + */ + public void stop() { + this.transmit = false; + this.on = false; + socket.close(); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BasicTransportManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BasicTransportManager.java index e3c55d17b..ee7fd20f5 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BasicTransportManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BasicTransportManager.java @@ -1,31 +1,31 @@ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smackx.jingle.JingleSession; - -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * A Basic Jingle Transport Manager implementation. - * - */ -public class BasicTransportManager extends JingleTransportManager{ - - protected TransportResolver createResolver(JingleSession session) { - return new BasicResolver(); - } -} +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smackx.jingle.JingleSession; + +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A Basic Jingle Transport Manager implementation. + * + */ +public class BasicTransportManager extends JingleTransportManager{ + + protected TransportResolver createResolver(JingleSession session) { + return new BasicResolver(); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedResolver.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedResolver.java index e6a76e6aa..006164ac4 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedResolver.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedResolver.java @@ -1,147 +1,147 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleSession; - -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; -import java.util.Random; - -/** - * Bridged Resolver use a RTPBridge Service to add a relayed candidate. - * A very reliable solution for NAT Traversal. - *

    - * The resolver verify is the XMPP Server that the client is connected offer this service. - * If the server supports, a candidate is requested from the service. - * The resolver adds this candidate - */ -public class BridgedResolver extends TransportResolver { - - Connection connection; - - Random random = new Random(); - - long sid; - - /** - * Constructor. - * A Bridged Resolver need a Connection to connect to a RTP Bridge. - */ - public BridgedResolver(Connection connection) { - super(); - this.connection = connection; - } - - /** - * Resolve Bridged Candidate. - *

    - * The BridgedResolver takes the IP addresse and ports of a jmf proxy service. - */ - public synchronized void resolve(JingleSession session) throws XMPPException { - - setResolveInit(); - - clearCandidates(); - - sid = Math.abs(random.nextLong()); - - RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid)); - - String localIp = getLocalHost(); - - TransportCandidate localCandidate = new TransportCandidate.Fixed( - rtpBridge.getIp(), rtpBridge.getPortA()); - localCandidate.setLocalIp(localIp); - - TransportCandidate remoteCandidate = new TransportCandidate.Fixed( - rtpBridge.getIp(), rtpBridge.getPortB()); - remoteCandidate.setLocalIp(localIp); - - localCandidate.setSymmetric(remoteCandidate); - remoteCandidate.setSymmetric(localCandidate); - - localCandidate.setPassword(rtpBridge.getPass()); - remoteCandidate.setPassword(rtpBridge.getPass()); - - localCandidate.setSessionId(rtpBridge.getSid()); - remoteCandidate.setSessionId(rtpBridge.getSid()); - - localCandidate.setConnection(this.connection); - remoteCandidate.setConnection(this.connection); - - addCandidate(localCandidate); - - setResolveEnd(); - } - - public void initialize() throws XMPPException { - - clearCandidates(); - - if (!RTPBridge.serviceAvailable(connection)) { - setInitialized(); - throw new XMPPException("No RTP Bridge service available"); - } - setInitialized(); - - } - - public void cancel() throws XMPPException { - // Nothing to do here - } - - public static String getLocalHost() { - Enumeration ifaces = null; - - try { - ifaces = NetworkInterface.getNetworkInterfaces(); - } - catch (SocketException e) { - e.printStackTrace(); - } - - while (ifaces.hasMoreElements()) { - - NetworkInterface iface = ifaces.nextElement(); - Enumeration iaddresses = iface.getInetAddresses(); - - while (iaddresses.hasMoreElements()) { - InetAddress iaddress = iaddresses.nextElement(); - if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress() && !(iaddress instanceof Inet6Address)) { - return iaddress.getHostAddress(); - } - } - } - - try { - return InetAddress.getLocalHost().getHostAddress(); - } - catch (Exception e) { - e.printStackTrace(); - } - - return "127.0.0.1"; - - } - -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleSession; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.Random; + +/** + * Bridged Resolver use a RTPBridge Service to add a relayed candidate. + * A very reliable solution for NAT Traversal. + *

    + * The resolver verify is the XMPP Server that the client is connected offer this service. + * If the server supports, a candidate is requested from the service. + * The resolver adds this candidate + */ +public class BridgedResolver extends TransportResolver { + + Connection connection; + + Random random = new Random(); + + long sid; + + /** + * Constructor. + * A Bridged Resolver need a Connection to connect to a RTP Bridge. + */ + public BridgedResolver(Connection connection) { + super(); + this.connection = connection; + } + + /** + * Resolve Bridged Candidate. + *

    + * The BridgedResolver takes the IP addresse and ports of a jmf proxy service. + */ + public synchronized void resolve(JingleSession session) throws XMPPException { + + setResolveInit(); + + clearCandidates(); + + sid = Math.abs(random.nextLong()); + + RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid)); + + String localIp = getLocalHost(); + + TransportCandidate localCandidate = new TransportCandidate.Fixed( + rtpBridge.getIp(), rtpBridge.getPortA()); + localCandidate.setLocalIp(localIp); + + TransportCandidate remoteCandidate = new TransportCandidate.Fixed( + rtpBridge.getIp(), rtpBridge.getPortB()); + remoteCandidate.setLocalIp(localIp); + + localCandidate.setSymmetric(remoteCandidate); + remoteCandidate.setSymmetric(localCandidate); + + localCandidate.setPassword(rtpBridge.getPass()); + remoteCandidate.setPassword(rtpBridge.getPass()); + + localCandidate.setSessionId(rtpBridge.getSid()); + remoteCandidate.setSessionId(rtpBridge.getSid()); + + localCandidate.setConnection(this.connection); + remoteCandidate.setConnection(this.connection); + + addCandidate(localCandidate); + + setResolveEnd(); + } + + public void initialize() throws XMPPException { + + clearCandidates(); + + if (!RTPBridge.serviceAvailable(connection)) { + setInitialized(); + throw new XMPPException("No RTP Bridge service available"); + } + setInitialized(); + + } + + public void cancel() throws XMPPException { + // Nothing to do here + } + + public static String getLocalHost() { + Enumeration ifaces = null; + + try { + ifaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException e) { + e.printStackTrace(); + } + + while (ifaces.hasMoreElements()) { + + NetworkInterface iface = ifaces.nextElement(); + Enumeration iaddresses = iface.getInetAddresses(); + + while (iaddresses.hasMoreElements()) { + InetAddress iaddress = iaddresses.nextElement(); + if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress() && !(iaddress instanceof Inet6Address)) { + return iaddress.getHostAddress(); + } + } + } + + try { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (Exception e) { + e.printStackTrace(); + } + + return "127.0.0.1"; + + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedTransportManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedTransportManager.java index 9b894c874..ffa5a6327 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedTransportManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/BridgedTransportManager.java @@ -1,80 +1,80 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; -import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; -import org.jivesoftware.smackx.jingle.media.PayloadType; - -/** - * A Jingle Transport Manager implementation to be used for NAT Networks. - * This kind of transport needs that the connected XMPP Server provide a Bridge Service. (http://www.jivesoftware.com/protocol/rtpbridge) - * To relay the jmf outside the NAT. - * - * @author Thiago Camargo - */ -public class BridgedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { - - Connection xmppConnection; - - public BridgedTransportManager(Connection xmppConnection) { - super(); - this.xmppConnection = xmppConnection; - } - - /** - * Return the correspondent resolver - * - * @param session correspondent Jingle Session - * @return resolver - */ - protected TransportResolver createResolver(JingleSession session) { - BridgedResolver bridgedResolver = new BridgedResolver(this.xmppConnection); - return bridgedResolver; - } - - // Implement a Session Listener to relay candidates after establishment - - public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { - RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc); - } - - public void sessionDeclined(String reason, JingleSession jingleSession) { - } - - public void sessionRedirected(String redirection, JingleSession jingleSession) { - } - - public void sessionClosed(String reason, JingleSession jingleSession) { - } - - public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { - } - - public void sessionMediaReceived(JingleSession jingleSession, String participant) { - // Do Nothing - } - - // Session Created - - public void sessionCreated(JingleSession jingleSession) { - jingleSession.addListener(this); - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.media.PayloadType; + +/** + * A Jingle Transport Manager implementation to be used for NAT Networks. + * This kind of transport needs that the connected XMPP Server provide a Bridge Service. (http://www.jivesoftware.com/protocol/rtpbridge) + * To relay the jmf outside the NAT. + * + * @author Thiago Camargo + */ +public class BridgedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { + + Connection xmppConnection; + + public BridgedTransportManager(Connection xmppConnection) { + super(); + this.xmppConnection = xmppConnection; + } + + /** + * Return the correspondent resolver + * + * @param session correspondent Jingle Session + * @return resolver + */ + protected TransportResolver createResolver(JingleSession session) { + BridgedResolver bridgedResolver = new BridgedResolver(this.xmppConnection); + return bridgedResolver; + } + + // Implement a Session Listener to relay candidates after establishment + + public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { + RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc); + } + + public void sessionDeclined(String reason, JingleSession jingleSession) { + } + + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } + + public void sessionClosed(String reason, JingleSession jingleSession) { + } + + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + } + + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + // Do Nothing + } + + // Session Created + + public void sessionCreated(JingleSession jingleSession) { + jingleSession.addListener(this); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/DatagramListener.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/DatagramListener.java index 7565e3c04..0908b84b5 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/DatagramListener.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/DatagramListener.java @@ -34,4 +34,4 @@ public interface DatagramListener { */ public boolean datagramReceived(DatagramPacket datagramPacket); -} \ No newline at end of file +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java index c4230aa2f..64fa19360 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java @@ -1,65 +1,65 @@ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.ContentNegotiator; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; -import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; -import org.jivesoftware.smackx.jingle.media.PayloadType; - -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * A Fixed Jingle Transport Manager implementation. - * - */ -public class FixedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { - - FixedResolver resolver; - - public FixedTransportManager(FixedResolver inResolver) { - resolver = inResolver; - } - - protected TransportResolver createResolver(JingleSession session) { - return resolver; - } - - public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { - } - - public void sessionDeclined(String reason, JingleSession jingleSession) { - } - - public void sessionRedirected(String redirection, JingleSession jingleSession) { - } - - public void sessionClosed(String reason, JingleSession jingleSession) { - } - - public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { - } - - public void sessionMediaReceived(JingleSession jingleSession, String participant) { - // Do Nothing - } - - public void sessionCreated(JingleSession jingleSession) { - jingleSession.addListener(this); - } -} +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.ContentNegotiator; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.media.PayloadType; + +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A Fixed Jingle Transport Manager implementation. + * + */ +public class FixedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { + + FixedResolver resolver; + + public FixedTransportManager(FixedResolver inResolver) { + resolver = inResolver; + } + + protected TransportResolver createResolver(JingleSession session) { + return resolver; + } + + public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { + } + + public void sessionDeclined(String reason, JingleSession jingleSession) { + } + + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } + + public void sessionClosed(String reason, JingleSession jingleSession) { + } + + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + } + + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + // Do Nothing + } + + public void sessionCreated(JingleSession jingleSession) { + jingleSession.addListener(this); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/HttpServer.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/HttpServer.java index 80dd68400..1ab013822 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/HttpServer.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/HttpServer.java @@ -142,4 +142,4 @@ public class HttpServer { } } } -} \ No newline at end of file +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICEResolver.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICEResolver.java index 9d4df8933..c7798a268 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICEResolver.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICEResolver.java @@ -1,278 +1,278 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.nat; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.SmackLogger; - -import de.javawi.jstun.test.demo.ice.Candidate; -import de.javawi.jstun.test.demo.ice.ICENegociator; -import de.javawi.jstun.util.UtilityException; - -/** - * ICE Resolver for Jingle transport method that results in sending data between two entities using the Interactive Connectivity Establishment (ICE) methodology. (XEP-0176) - * The goal of this resolver is to make possible to establish and manage out-of-band connections between two XMPP entities, even if they are behind Network Address Translators (NATs) or firewalls. - * To use this resolver you must have a STUN Server and be in a non STUN blocked network. Or use a XMPP server with public IP detection Service. - * - * @author Thiago Camargo - */ -public class ICEResolver extends TransportResolver { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(ICEResolver.class); - - Connection connection; - Random random = new Random(); - long sid; - String server; - int port; - static Map negociatorsMap = new HashMap(); - //ICENegociator iceNegociator = null; - - public ICEResolver(Connection connection, String server, int port) { - super(); - this.connection = connection; - this.server = server; - this.port = port; - this.setType(Type.ice); - } - - public void initialize() throws XMPPException { - if (!isResolving() && !isResolved()) { - LOGGER.debug("Initialized"); - - // Negotiation with a STUN server for a set of interfaces is quite slow, but the results - // never change over then instance of a JVM. To increase connection performance considerably - // we now cache established/initialized negotiators for each STUN server, so that subsequent uses - // of the STUN server are much, much faster. - if (negociatorsMap.get(server) == null) { - ICENegociator iceNegociator = new ICENegociator(server, port, (short) 1); - negociatorsMap.put(server, iceNegociator); - - // gather candidates - iceNegociator.gatherCandidateAddresses(); - // priorize candidates - iceNegociator.prioritizeCandidates(); - } - - } - this.setInitialized(); - } - - public void cancel() throws XMPPException { - - } - - /** - * Resolve the IP and obtain a valid transport method. - */ - public synchronized void resolve(JingleSession session) throws XMPPException { - this.setResolveInit(); - - for (TransportCandidate candidate : this.getCandidatesList()) { - if (candidate instanceof ICECandidate) { - ICECandidate iceCandidate = (ICECandidate) candidate; - iceCandidate.removeCandidateEcho(); - } - } - - this.clear(); - - // Create a transport candidate for each ICE negotiator candidate we have. - ICENegociator iceNegociator = negociatorsMap.get(server); - for (Candidate candidate : iceNegociator.getSortedCandidates()) - try { - Candidate.CandidateType type = candidate.getCandidateType(); - ICECandidate.Type iceType = ICECandidate.Type.local; - if (type.equals(Candidate.CandidateType.ServerReflexive)) - iceType = ICECandidate.Type.srflx; - else if (type.equals(Candidate.CandidateType.PeerReflexive)) - iceType = ICECandidate.Type.prflx; - else if (type.equals(Candidate.CandidateType.Relayed)) - iceType = ICECandidate.Type.relay; - else - iceType = ICECandidate.Type.host; - - // JBW/GW - 17JUL08: Figure out the zero-based NIC number for this candidate. - short nicNum = 0; - try { - Enumeration nics = NetworkInterface.getNetworkInterfaces(); - short i = 0; - NetworkInterface nic = NetworkInterface.getByInetAddress(candidate.getAddress().getInetAddress()); - while(nics.hasMoreElements()) { - NetworkInterface checkNIC = nics.nextElement(); - if (checkNIC.equals(nic)) { - nicNum = i; - break; - } - i++; - } - } catch (SocketException e1) { - e1.printStackTrace(); - } - - TransportCandidate transportCandidate = new ICECandidate(candidate.getAddress().getInetAddress().getHostAddress(), 1, nicNum, String.valueOf(Math.abs(random.nextLong())), candidate.getPort(), "1", candidate.getPriority(), iceType); - transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress()); - transportCandidate.setPort(getFreePort()); - try { - transportCandidate.addCandidateEcho(session); - } - catch (SocketException e) { - e.printStackTrace(); - } - this.addCandidate(transportCandidate); - - LOGGER.debug("Candidate addr: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " Priority:" + candidate.getPriority()); - - } - catch (UtilityException e) { - e.printStackTrace(); - } - catch (UnknownHostException e) { - e.printStackTrace(); - } - - // Get a Relay Candidate from XMPP Server - - if (RTPBridge.serviceAvailable(connection)) { -// try { - - String localIp; - int network; - - - // JBW/GW - 17JUL08: ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now the API doesn't exist in JSTUN 1.7.1 -// if (iceNegociator.getPublicCandidate() != null) { -// localIp = iceNegociator.getPublicCandidate().getBase().getAddress().getInetAddress().getHostAddress(); -// network = iceNegociator.getPublicCandidate().getNetwork(); -// } -// else { - { - localIp = BridgedResolver.getLocalHost(); - network = 0; - } - - sid = Math.abs(random.nextLong()); - - RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid)); - - TransportCandidate localCandidate = new ICECandidate( - rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortA(), "1", 0, ICECandidate.Type.relay); - localCandidate.setLocalIp(localIp); - - TransportCandidate remoteCandidate = new ICECandidate( - rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortB(), "1", 0, ICECandidate.Type.relay); - remoteCandidate.setLocalIp(localIp); - - localCandidate.setSymmetric(remoteCandidate); - remoteCandidate.setSymmetric(localCandidate); - - localCandidate.setPassword(rtpBridge.getPass()); - remoteCandidate.setPassword(rtpBridge.getPass()); - - localCandidate.setSessionId(rtpBridge.getSid()); - remoteCandidate.setSessionId(rtpBridge.getSid()); - - localCandidate.setConnection(this.connection); - remoteCandidate.setConnection(this.connection); - - addCandidate(localCandidate); - -// } -// catch (UtilityException e) { -// e.printStackTrace(); -// } -// catch (UnknownHostException e) { -// e.printStackTrace(); -// } - - // Get Public Candidate From XMPP Server - - // JBW/GW - 17JUL08 - ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now it doesn't exist in JSTUN 1.7.1 - // if (iceNegociator.getPublicCandidate() == null) { - if (true) { - - String publicIp = RTPBridge.getPublicIP(connection); - - if (publicIp != null && !publicIp.equals("")) { - - Enumeration ifaces = null; - - try { - ifaces = NetworkInterface.getNetworkInterfaces(); - } - catch (SocketException e) { - e.printStackTrace(); - } - - // If detect this address in local machine, don't use it. - - boolean found = false; - - while (ifaces.hasMoreElements() && !false) { - - NetworkInterface iface = ifaces.nextElement(); - Enumeration iaddresses = iface.getInetAddresses(); - - while (iaddresses.hasMoreElements()) { - InetAddress iaddress = iaddresses.nextElement(); - if (iaddress.getHostAddress().indexOf(publicIp) > -1) { - found = true; - break; - } - } - } - - if (!found) { - try { - TransportCandidate publicCandidate = new ICECandidate( - publicIp, 1, 0, String.valueOf(Math.abs(random.nextLong())), getFreePort(), "1", 0, ICECandidate.Type.srflx); - publicCandidate.setLocalIp(InetAddress.getLocalHost().getHostAddress()); - - try { - publicCandidate.addCandidateEcho(session); - } - catch (SocketException e) { - e.printStackTrace(); - } - - addCandidate(publicCandidate); - } - catch (UnknownHostException e) { - e.printStackTrace(); - } - } - } - } - - } - - this.setResolveEnd(); - } - -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.nat; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.SmackLogger; + +import de.javawi.jstun.test.demo.ice.Candidate; +import de.javawi.jstun.test.demo.ice.ICENegociator; +import de.javawi.jstun.util.UtilityException; + +/** + * ICE Resolver for Jingle transport method that results in sending data between two entities using the Interactive Connectivity Establishment (ICE) methodology. (XEP-0176) + * The goal of this resolver is to make possible to establish and manage out-of-band connections between two XMPP entities, even if they are behind Network Address Translators (NATs) or firewalls. + * To use this resolver you must have a STUN Server and be in a non STUN blocked network. Or use a XMPP server with public IP detection Service. + * + * @author Thiago Camargo + */ +public class ICEResolver extends TransportResolver { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(ICEResolver.class); + + Connection connection; + Random random = new Random(); + long sid; + String server; + int port; + static Map negociatorsMap = new HashMap(); + //ICENegociator iceNegociator = null; + + public ICEResolver(Connection connection, String server, int port) { + super(); + this.connection = connection; + this.server = server; + this.port = port; + this.setType(Type.ice); + } + + public void initialize() throws XMPPException { + if (!isResolving() && !isResolved()) { + LOGGER.debug("Initialized"); + + // Negotiation with a STUN server for a set of interfaces is quite slow, but the results + // never change over then instance of a JVM. To increase connection performance considerably + // we now cache established/initialized negotiators for each STUN server, so that subsequent uses + // of the STUN server are much, much faster. + if (negociatorsMap.get(server) == null) { + ICENegociator iceNegociator = new ICENegociator(server, port, (short) 1); + negociatorsMap.put(server, iceNegociator); + + // gather candidates + iceNegociator.gatherCandidateAddresses(); + // priorize candidates + iceNegociator.prioritizeCandidates(); + } + + } + this.setInitialized(); + } + + public void cancel() throws XMPPException { + + } + + /** + * Resolve the IP and obtain a valid transport method. + */ + public synchronized void resolve(JingleSession session) throws XMPPException { + this.setResolveInit(); + + for (TransportCandidate candidate : this.getCandidatesList()) { + if (candidate instanceof ICECandidate) { + ICECandidate iceCandidate = (ICECandidate) candidate; + iceCandidate.removeCandidateEcho(); + } + } + + this.clear(); + + // Create a transport candidate for each ICE negotiator candidate we have. + ICENegociator iceNegociator = negociatorsMap.get(server); + for (Candidate candidate : iceNegociator.getSortedCandidates()) + try { + Candidate.CandidateType type = candidate.getCandidateType(); + ICECandidate.Type iceType = ICECandidate.Type.local; + if (type.equals(Candidate.CandidateType.ServerReflexive)) + iceType = ICECandidate.Type.srflx; + else if (type.equals(Candidate.CandidateType.PeerReflexive)) + iceType = ICECandidate.Type.prflx; + else if (type.equals(Candidate.CandidateType.Relayed)) + iceType = ICECandidate.Type.relay; + else + iceType = ICECandidate.Type.host; + + // JBW/GW - 17JUL08: Figure out the zero-based NIC number for this candidate. + short nicNum = 0; + try { + Enumeration nics = NetworkInterface.getNetworkInterfaces(); + short i = 0; + NetworkInterface nic = NetworkInterface.getByInetAddress(candidate.getAddress().getInetAddress()); + while(nics.hasMoreElements()) { + NetworkInterface checkNIC = nics.nextElement(); + if (checkNIC.equals(nic)) { + nicNum = i; + break; + } + i++; + } + } catch (SocketException e1) { + e1.printStackTrace(); + } + + TransportCandidate transportCandidate = new ICECandidate(candidate.getAddress().getInetAddress().getHostAddress(), 1, nicNum, String.valueOf(Math.abs(random.nextLong())), candidate.getPort(), "1", candidate.getPriority(), iceType); + transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress()); + transportCandidate.setPort(getFreePort()); + try { + transportCandidate.addCandidateEcho(session); + } + catch (SocketException e) { + e.printStackTrace(); + } + this.addCandidate(transportCandidate); + + LOGGER.debug("Candidate addr: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " Priority:" + candidate.getPriority()); + + } + catch (UtilityException e) { + e.printStackTrace(); + } + catch (UnknownHostException e) { + e.printStackTrace(); + } + + // Get a Relay Candidate from XMPP Server + + if (RTPBridge.serviceAvailable(connection)) { +// try { + + String localIp; + int network; + + + // JBW/GW - 17JUL08: ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now the API doesn't exist in JSTUN 1.7.1 +// if (iceNegociator.getPublicCandidate() != null) { +// localIp = iceNegociator.getPublicCandidate().getBase().getAddress().getInetAddress().getHostAddress(); +// network = iceNegociator.getPublicCandidate().getNetwork(); +// } +// else { + { + localIp = BridgedResolver.getLocalHost(); + network = 0; + } + + sid = Math.abs(random.nextLong()); + + RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid)); + + TransportCandidate localCandidate = new ICECandidate( + rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortA(), "1", 0, ICECandidate.Type.relay); + localCandidate.setLocalIp(localIp); + + TransportCandidate remoteCandidate = new ICECandidate( + rtpBridge.getIp(), 1, network, String.valueOf(Math.abs(random.nextLong())), rtpBridge.getPortB(), "1", 0, ICECandidate.Type.relay); + remoteCandidate.setLocalIp(localIp); + + localCandidate.setSymmetric(remoteCandidate); + remoteCandidate.setSymmetric(localCandidate); + + localCandidate.setPassword(rtpBridge.getPass()); + remoteCandidate.setPassword(rtpBridge.getPass()); + + localCandidate.setSessionId(rtpBridge.getSid()); + remoteCandidate.setSessionId(rtpBridge.getSid()); + + localCandidate.setConnection(this.connection); + remoteCandidate.setConnection(this.connection); + + addCandidate(localCandidate); + +// } +// catch (UtilityException e) { +// e.printStackTrace(); +// } +// catch (UnknownHostException e) { +// e.printStackTrace(); +// } + + // Get Public Candidate From XMPP Server + + // JBW/GW - 17JUL08 - ICENegotiator.getPublicCandidate() always returned null in JSTUN 1.7.0, and now it doesn't exist in JSTUN 1.7.1 + // if (iceNegociator.getPublicCandidate() == null) { + if (true) { + + String publicIp = RTPBridge.getPublicIP(connection); + + if (publicIp != null && !publicIp.equals("")) { + + Enumeration ifaces = null; + + try { + ifaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException e) { + e.printStackTrace(); + } + + // If detect this address in local machine, don't use it. + + boolean found = false; + + while (ifaces.hasMoreElements() && !false) { + + NetworkInterface iface = ifaces.nextElement(); + Enumeration iaddresses = iface.getInetAddresses(); + + while (iaddresses.hasMoreElements()) { + InetAddress iaddress = iaddresses.nextElement(); + if (iaddress.getHostAddress().indexOf(publicIp) > -1) { + found = true; + break; + } + } + } + + if (!found) { + try { + TransportCandidate publicCandidate = new ICECandidate( + publicIp, 1, 0, String.valueOf(Math.abs(random.nextLong())), getFreePort(), "1", 0, ICECandidate.Type.srflx); + publicCandidate.setLocalIp(InetAddress.getLocalHost().getHostAddress()); + + try { + publicCandidate.addCandidateEcho(session); + } + catch (SocketException e) { + e.printStackTrace(); + } + + addCandidate(publicCandidate); + } + catch (UnknownHostException e) { + e.printStackTrace(); + } + } + } + } + + } + + this.setResolveEnd(); + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICETransportManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICETransportManager.java index 2d3a2317d..d732bb44b 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICETransportManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ICETransportManager.java @@ -1,81 +1,81 @@ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; -import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; -import org.jivesoftware.smackx.jingle.media.PayloadType; - -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public class ICETransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { - - ICEResolver iceResolver = null; - - public ICETransportManager(Connection xmppConnection, String server, int port) { - iceResolver = new ICEResolver(xmppConnection, server, port); - try { - iceResolver.initializeAndWait(); - } - catch (XMPPException e) { - e.printStackTrace(); - } - } - - protected TransportResolver createResolver(JingleSession session) { - try { - iceResolver.resolve(session); - } - catch (XMPPException e) { - e.printStackTrace(); - } - return iceResolver; - } - - // Implement a Session Listener to relay candidates after establishment - - public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { - if (lc instanceof ICECandidate) { - if (((ICECandidate) lc).getType().equals("relay")) { - RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc); - } - } - } - - public void sessionDeclined(String reason, JingleSession jingleSession) { - } - - public void sessionRedirected(String redirection, JingleSession jingleSession) { - } - - public void sessionClosed(String reason, JingleSession jingleSession) { - } - - public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { - } - - public void sessionMediaReceived(JingleSession jingleSession, String participant) { - // Do Nothing - } - - // Session Created - - public void sessionCreated(JingleSession jingleSession) { - jingleSession.addListener(this); - } -} +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.media.PayloadType; + +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class ICETransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { + + ICEResolver iceResolver = null; + + public ICETransportManager(Connection xmppConnection, String server, int port) { + iceResolver = new ICEResolver(xmppConnection, server, port); + try { + iceResolver.initializeAndWait(); + } + catch (XMPPException e) { + e.printStackTrace(); + } + } + + protected TransportResolver createResolver(JingleSession session) { + try { + iceResolver.resolve(session); + } + catch (XMPPException e) { + e.printStackTrace(); + } + return iceResolver; + } + + // Implement a Session Listener to relay candidates after establishment + + public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { + if (lc instanceof ICECandidate) { + if (((ICECandidate) lc).getType().equals("relay")) { + RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc); + } + } + } + + public void sessionDeclined(String reason, JingleSession jingleSession) { + } + + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } + + public void sessionClosed(String reason, JingleSession jingleSession) { + } + + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + } + + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + // Do Nothing + } + + // Session Created + + public void sessionCreated(JingleSession jingleSession) { + jingleSession.addListener(this); + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/RTPBridge.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/RTPBridge.java index 746454156..6b0003312 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/RTPBridge.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/RTPBridge.java @@ -1,552 +1,552 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.nat; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; -import java.util.Iterator; - -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.smack.provider.IQProvider; -import org.jivesoftware.smack.provider.ProviderManager; -import org.jivesoftware.smackx.ServiceDiscoveryManager; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.packet.DiscoverInfo; -import org.xmlpull.v1.XmlPullParser; - -/** - * RTPBridge IQ Packet used to request and retrieve a RTPBridge Candidates that can be used for a Jingle Media Transmission between two parties that are behind NAT. - * This Jingle Bridge has all the needed information to establish a full UDP Channel (Send and Receive) between two parties. - * This transport method should be used only if other transport methods are not allowed. Or if you want a more reliable transport. - *

    - * High Level Usage Example: - *

    - * RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, sessionID); - * - * @author Thiago Camargo - */ -public class RTPBridge extends IQ { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(RTPBridge.class); - - private String sid; - private String pass; - private String ip; - private String name; - private int portA = -1; - private int portB = -1; - private String hostA; - private String hostB; - private BridgeAction bridgeAction = BridgeAction.create; - - private enum BridgeAction { - - create, change, publicip - } - - /** - * Element name of the packet extension. - */ - public static final String NAME = "rtpbridge"; - - /** - * Element name of the packet extension. - */ - public static final String ELEMENT_NAME = "rtpbridge"; - - /** - * Namespace of the packet extension. - */ - public static final String NAMESPACE = "http://www.jivesoftware.com/protocol/rtpbridge"; - - static { - ProviderManager.getInstance().addIQProvider(NAME, NAMESPACE, new Provider()); - } - - /** - * Creates a RTPBridge Instance with defined Session ID - * - * @param sid - */ - public RTPBridge(String sid) { - this.sid = sid; - } - - /** - * Creates a RTPBridge Instance with defined Session ID - * - * @param action - */ - public RTPBridge(BridgeAction action) { - this.bridgeAction = action; - } - - /** - * Creates a RTPBridge Instance with defined Session ID - * - * @param sid - * @param bridgeAction - */ - public RTPBridge(String sid, BridgeAction bridgeAction) { - this.sid = sid; - this.bridgeAction = bridgeAction; - } - - /** - * Creates a RTPBridge Packet without Session ID - */ - public RTPBridge() { - } - - /** - * Get the attributes string - */ - public String getAttributes() { - StringBuilder str = new StringBuilder(); - - if (getSid() != null) - str.append(" sid='").append(getSid()).append("'"); - - if (getPass() != null) - str.append(" pass='").append(getPass()).append("'"); - - if (getPortA() != -1) - str.append(" porta='").append(getPortA()).append("'"); - - if (getPortB() != -1) - str.append(" portb='").append(getPortB()).append("'"); - - if (getHostA() != null) - str.append(" hosta='").append(getHostA()).append("'"); - - if (getHostB() != null) - str.append(" hostb='").append(getHostB()).append("'"); - - return str.toString(); - } - - /** - * Get the Session ID of the Packet (usually same as Jingle Session ID) - * - * @return - */ - public String getSid() { - return sid; - } - - /** - * Set the Session ID of the Packet (usually same as Jingle Session ID) - * - * @param sid - */ - public void setSid(String sid) { - this.sid = sid; - } - - /** - * Get the Host A IP Address - * - * @return - */ - public String getHostA() { - return hostA; - } - - /** - * Set the Host A IP Address - * - * @param hostA - */ - public void setHostA(String hostA) { - this.hostA = hostA; - } - - /** - * Get the Host B IP Address - * - * @return - */ - public String getHostB() { - return hostB; - } - - /** - * Set the Host B IP Address - * - * @param hostB - */ - public void setHostB(String hostB) { - this.hostB = hostB; - } - - /** - * Get Side A receive port - * - * @return - */ - public int getPortA() { - return portA; - } - - /** - * Set Side A receive port - * - * @param portA - */ - public void setPortA(int portA) { - this.portA = portA; - } - - /** - * Get Side B receive port - * - * @return - */ - public int getPortB() { - return portB; - } - - /** - * Set Side B receive port - * - * @param portB - */ - public void setPortB(int portB) { - this.portB = portB; - } - - /** - * Get the RTP Bridge IP - * - * @return - */ - public String getIp() { - return ip; - } - - /** - * Set the RTP Bridge IP - * - * @param ip - */ - public void setIp(String ip) { - this.ip = ip; - } - - /** - * Get the RTP Agent Pass - * - * @return - */ - public String getPass() { - return pass; - } - - /** - * Set the RTP Agent Pass - * - * @param pass - */ - public void setPass(String pass) { - this.pass = pass; - } - - /** - * Get the name of the Candidate - * - * @return - */ - public String getName() { - return name; - } - - /** - * Set the name of the Candidate - * - * @param name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Get the Child Element XML of the Packet - * - * @return - */ - public String getChildElementXML() { - StringBuilder str = new StringBuilder(); - str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "' sid='").append(sid).append("'>"); - - if (bridgeAction.equals(BridgeAction.create)) - str.append(""); - else if (bridgeAction.equals(BridgeAction.change)) - str.append(""); - else - str.append(""); - - str.append(""); - return str.toString(); - } - - /** - * IQProvider for RTP Bridge packets. - * Parse receive RTPBridge packet to a RTPBridge instance - * - * @author Thiago Rocha - */ - public static class Provider implements IQProvider { - - public Provider() { - super(); - } - - public IQ parseIQ(XmlPullParser parser) throws Exception { - - boolean done = false; - - int eventType; - String elementName; - String namespace; - - if (!parser.getNamespace().equals(RTPBridge.NAMESPACE)) - throw new Exception("Not a RTP Bridge packet"); - - RTPBridge iq = new RTPBridge(); - - for (int i = 0; i < parser.getAttributeCount(); i++) { - if (parser.getAttributeName(i).equals("sid")) - iq.setSid(parser.getAttributeValue(i)); - } - - // Start processing sub-elements - while (!done) { - eventType = parser.next(); - elementName = parser.getName(); - namespace = parser.getNamespace(); - - if (eventType == XmlPullParser.START_TAG) { - if (elementName.equals("candidate")) { - for (int i = 0; i < parser.getAttributeCount(); i++) { - if (parser.getAttributeName(i).equals("ip")) - iq.setIp(parser.getAttributeValue(i)); - else if (parser.getAttributeName(i).equals("pass")) - iq.setPass(parser.getAttributeValue(i)); - else if (parser.getAttributeName(i).equals("name")) - iq.setName(parser.getAttributeValue(i)); - else if (parser.getAttributeName(i).equals("porta")) - iq.setPortA(Integer.parseInt(parser.getAttributeValue(i))); - else if (parser.getAttributeName(i).equals("portb")) - iq.setPortB(Integer.parseInt(parser.getAttributeValue(i))); - } - } - else if (elementName.equals("publicip")) { - //String p = parser.getAttributeName(0); - int x = parser.getAttributeCount(); - for (int i = 0; i < parser.getAttributeCount(); i++) { - if (parser.getAttributeName(i).equals("ip")) - iq.setIp(parser.getAttributeValue(i)); - } - } - } - else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals(RTPBridge.ELEMENT_NAME)) { - done = true; - } - } - } - return iq; - } - } - - /** - * Get a new RTPBridge Candidate from the server. - * If a error occurs or the server don't support RTPBridge Service, null is returned. - * - * @param connection - * @param sessionID - * @return - */ - public static RTPBridge getRTPBridge(Connection connection, String sessionID) { - - if (!connection.isConnected()) { - return null; - } - - RTPBridge rtpPacket = new RTPBridge(sessionID); - rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); - - PacketCollector collector = connection - .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); - - connection.sendPacket(rtpPacket); - - RTPBridge response = (RTPBridge) collector - .nextResult(SmackConfiguration.getPacketReplyTimeout()); - - // Cancel the collector. - collector.cancel(); - - return response; - } - - /** - * Check if the server support RTPBridge Service. - * - * @param connection - * @return - */ - public static boolean serviceAvailable(Connection connection) { - - if (!connection.isConnected()) { - return false; - } - - LOGGER.debug("Service listing"); - - ServiceDiscoveryManager disco = ServiceDiscoveryManager - .getInstanceFor(connection); - try { -// DiscoverItems items = disco.discoverItems(connection.getServiceName()); -// Iterator iter = items.getItems(); -// while (iter.hasNext()) { -// DiscoverItems.Item item = (DiscoverItems.Item) iter.next(); -// if (item.getEntityID().startsWith("rtpbridge.")) { -// return true; -// } -// } - - DiscoverInfo discoInfo = disco.discoverInfo(connection.getServiceName()); - Iterator iter = discoInfo.getIdentities(); - while (iter.hasNext()) { - DiscoverInfo.Identity identity = iter.next(); - if ((identity.getName() != null) && (identity.getName().startsWith("rtpbridge"))) { - return true; - } - } - } - catch (XMPPException e) { - e.printStackTrace(); - } - - return false; - } - - /** - * Check if the server support RTPBridge Service. - * - * @param connection - * @return - */ - public static RTPBridge relaySession(Connection connection, String sessionID, String pass, TransportCandidate proxyCandidate, TransportCandidate localCandidate) { - - if (!connection.isConnected()) { - return null; - } - - RTPBridge rtpPacket = new RTPBridge(sessionID, RTPBridge.BridgeAction.change); - rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); - rtpPacket.setType(Type.SET); - - rtpPacket.setPass(pass); - rtpPacket.setPortA(localCandidate.getPort()); - rtpPacket.setPortB(proxyCandidate.getPort()); - rtpPacket.setHostA(localCandidate.getIp()); - rtpPacket.setHostB(proxyCandidate.getIp()); - - // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); - - PacketCollector collector = connection - .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); - - connection.sendPacket(rtpPacket); - - RTPBridge response = (RTPBridge) collector - .nextResult(SmackConfiguration.getPacketReplyTimeout()); - - // Cancel the collector. - collector.cancel(); - - return response; - } - - /** - * Get Public Address from the Server. - * - * @param xmppConnection - * @return public IP String or null if not found - */ - public static String getPublicIP(Connection xmppConnection) { - - if (!xmppConnection.isConnected()) { - return null; - } - - RTPBridge rtpPacket = new RTPBridge(RTPBridge.BridgeAction.publicip); - rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getServiceName()); - rtpPacket.setType(Type.SET); - - // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); - - PacketCollector collector = xmppConnection - .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); - - xmppConnection.sendPacket(rtpPacket); - - RTPBridge response = (RTPBridge) collector - .nextResult(SmackConfiguration.getPacketReplyTimeout()); - - // Cancel the collector. - collector.cancel(); - - if(response == null) return null; - - if (response.getIp() == null || response.getIp().equals("")) return null; - - Enumeration ifaces = null; - try { - ifaces = NetworkInterface.getNetworkInterfaces(); - } - catch (SocketException e) { - e.printStackTrace(); - } - while (ifaces!=null&&ifaces.hasMoreElements()) { - - NetworkInterface iface = ifaces.nextElement(); - Enumeration iaddresses = iface.getInetAddresses(); - - while (iaddresses.hasMoreElements()) { - InetAddress iaddress = iaddresses.nextElement(); - if (!iaddress.isLoopbackAddress()) - if (iaddress.getHostAddress().indexOf(response.getIp()) >= 0) - return null; - - } - } - - return response.getIp(); - } - -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.nat; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.Iterator; + +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.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.xmlpull.v1.XmlPullParser; + +/** + * RTPBridge IQ Packet used to request and retrieve a RTPBridge Candidates that can be used for a Jingle Media Transmission between two parties that are behind NAT. + * This Jingle Bridge has all the needed information to establish a full UDP Channel (Send and Receive) between two parties. + * This transport method should be used only if other transport methods are not allowed. Or if you want a more reliable transport. + *

    + * High Level Usage Example: + *

    + * RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, sessionID); + * + * @author Thiago Camargo + */ +public class RTPBridge extends IQ { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(RTPBridge.class); + + private String sid; + private String pass; + private String ip; + private String name; + private int portA = -1; + private int portB = -1; + private String hostA; + private String hostB; + private BridgeAction bridgeAction = BridgeAction.create; + + private enum BridgeAction { + + create, change, publicip + } + + /** + * Element name of the packet extension. + */ + public static final String NAME = "rtpbridge"; + + /** + * Element name of the packet extension. + */ + public static final String ELEMENT_NAME = "rtpbridge"; + + /** + * Namespace of the packet extension. + */ + public static final String NAMESPACE = "http://www.jivesoftware.com/protocol/rtpbridge"; + + static { + ProviderManager.getInstance().addIQProvider(NAME, NAMESPACE, new Provider()); + } + + /** + * Creates a RTPBridge Instance with defined Session ID + * + * @param sid + */ + public RTPBridge(String sid) { + this.sid = sid; + } + + /** + * Creates a RTPBridge Instance with defined Session ID + * + * @param action + */ + public RTPBridge(BridgeAction action) { + this.bridgeAction = action; + } + + /** + * Creates a RTPBridge Instance with defined Session ID + * + * @param sid + * @param bridgeAction + */ + public RTPBridge(String sid, BridgeAction bridgeAction) { + this.sid = sid; + this.bridgeAction = bridgeAction; + } + + /** + * Creates a RTPBridge Packet without Session ID + */ + public RTPBridge() { + } + + /** + * Get the attributes string + */ + public String getAttributes() { + StringBuilder str = new StringBuilder(); + + if (getSid() != null) + str.append(" sid='").append(getSid()).append("'"); + + if (getPass() != null) + str.append(" pass='").append(getPass()).append("'"); + + if (getPortA() != -1) + str.append(" porta='").append(getPortA()).append("'"); + + if (getPortB() != -1) + str.append(" portb='").append(getPortB()).append("'"); + + if (getHostA() != null) + str.append(" hosta='").append(getHostA()).append("'"); + + if (getHostB() != null) + str.append(" hostb='").append(getHostB()).append("'"); + + return str.toString(); + } + + /** + * Get the Session ID of the Packet (usually same as Jingle Session ID) + * + * @return + */ + public String getSid() { + return sid; + } + + /** + * Set the Session ID of the Packet (usually same as Jingle Session ID) + * + * @param sid + */ + public void setSid(String sid) { + this.sid = sid; + } + + /** + * Get the Host A IP Address + * + * @return + */ + public String getHostA() { + return hostA; + } + + /** + * Set the Host A IP Address + * + * @param hostA + */ + public void setHostA(String hostA) { + this.hostA = hostA; + } + + /** + * Get the Host B IP Address + * + * @return + */ + public String getHostB() { + return hostB; + } + + /** + * Set the Host B IP Address + * + * @param hostB + */ + public void setHostB(String hostB) { + this.hostB = hostB; + } + + /** + * Get Side A receive port + * + * @return + */ + public int getPortA() { + return portA; + } + + /** + * Set Side A receive port + * + * @param portA + */ + public void setPortA(int portA) { + this.portA = portA; + } + + /** + * Get Side B receive port + * + * @return + */ + public int getPortB() { + return portB; + } + + /** + * Set Side B receive port + * + * @param portB + */ + public void setPortB(int portB) { + this.portB = portB; + } + + /** + * Get the RTP Bridge IP + * + * @return + */ + public String getIp() { + return ip; + } + + /** + * Set the RTP Bridge IP + * + * @param ip + */ + public void setIp(String ip) { + this.ip = ip; + } + + /** + * Get the RTP Agent Pass + * + * @return + */ + public String getPass() { + return pass; + } + + /** + * Set the RTP Agent Pass + * + * @param pass + */ + public void setPass(String pass) { + this.pass = pass; + } + + /** + * Get the name of the Candidate + * + * @return + */ + public String getName() { + return name; + } + + /** + * Set the name of the Candidate + * + * @param name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get the Child Element XML of the Packet + * + * @return + */ + public String getChildElementXML() { + StringBuilder str = new StringBuilder(); + str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "' sid='").append(sid).append("'>"); + + if (bridgeAction.equals(BridgeAction.create)) + str.append(""); + else if (bridgeAction.equals(BridgeAction.change)) + str.append(""); + else + str.append(""); + + str.append(""); + return str.toString(); + } + + /** + * IQProvider for RTP Bridge packets. + * Parse receive RTPBridge packet to a RTPBridge instance + * + * @author Thiago Rocha + */ + public static class Provider implements IQProvider { + + public Provider() { + super(); + } + + public IQ parseIQ(XmlPullParser parser) throws Exception { + + boolean done = false; + + int eventType; + String elementName; + String namespace; + + if (!parser.getNamespace().equals(RTPBridge.NAMESPACE)) + throw new Exception("Not a RTP Bridge packet"); + + RTPBridge iq = new RTPBridge(); + + for (int i = 0; i < parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("sid")) + iq.setSid(parser.getAttributeValue(i)); + } + + // Start processing sub-elements + while (!done) { + eventType = parser.next(); + elementName = parser.getName(); + namespace = parser.getNamespace(); + + if (eventType == XmlPullParser.START_TAG) { + if (elementName.equals("candidate")) { + for (int i = 0; i < parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("ip")) + iq.setIp(parser.getAttributeValue(i)); + else if (parser.getAttributeName(i).equals("pass")) + iq.setPass(parser.getAttributeValue(i)); + else if (parser.getAttributeName(i).equals("name")) + iq.setName(parser.getAttributeValue(i)); + else if (parser.getAttributeName(i).equals("porta")) + iq.setPortA(Integer.parseInt(parser.getAttributeValue(i))); + else if (parser.getAttributeName(i).equals("portb")) + iq.setPortB(Integer.parseInt(parser.getAttributeValue(i))); + } + } + else if (elementName.equals("publicip")) { + //String p = parser.getAttributeName(0); + int x = parser.getAttributeCount(); + for (int i = 0; i < parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("ip")) + iq.setIp(parser.getAttributeValue(i)); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(RTPBridge.ELEMENT_NAME)) { + done = true; + } + } + } + return iq; + } + } + + /** + * Get a new RTPBridge Candidate from the server. + * If a error occurs or the server don't support RTPBridge Service, null is returned. + * + * @param connection + * @param sessionID + * @return + */ + public static RTPBridge getRTPBridge(Connection connection, String sessionID) { + + if (!connection.isConnected()) { + return null; + } + + RTPBridge rtpPacket = new RTPBridge(sessionID); + rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); + + PacketCollector collector = connection + .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); + + connection.sendPacket(rtpPacket); + + RTPBridge response = (RTPBridge) collector + .nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + + return response; + } + + /** + * Check if the server support RTPBridge Service. + * + * @param connection + * @return + */ + public static boolean serviceAvailable(Connection connection) { + + if (!connection.isConnected()) { + return false; + } + + LOGGER.debug("Service listing"); + + ServiceDiscoveryManager disco = ServiceDiscoveryManager + .getInstanceFor(connection); + try { +// DiscoverItems items = disco.discoverItems(connection.getServiceName()); +// Iterator iter = items.getItems(); +// while (iter.hasNext()) { +// DiscoverItems.Item item = (DiscoverItems.Item) iter.next(); +// if (item.getEntityID().startsWith("rtpbridge.")) { +// return true; +// } +// } + + DiscoverInfo discoInfo = disco.discoverInfo(connection.getServiceName()); + Iterator iter = discoInfo.getIdentities(); + while (iter.hasNext()) { + DiscoverInfo.Identity identity = iter.next(); + if ((identity.getName() != null) && (identity.getName().startsWith("rtpbridge"))) { + return true; + } + } + } + catch (XMPPException e) { + e.printStackTrace(); + } + + return false; + } + + /** + * Check if the server support RTPBridge Service. + * + * @param connection + * @return + */ + public static RTPBridge relaySession(Connection connection, String sessionID, String pass, TransportCandidate proxyCandidate, TransportCandidate localCandidate) { + + if (!connection.isConnected()) { + return null; + } + + RTPBridge rtpPacket = new RTPBridge(sessionID, RTPBridge.BridgeAction.change); + rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); + rtpPacket.setType(Type.SET); + + rtpPacket.setPass(pass); + rtpPacket.setPortA(localCandidate.getPort()); + rtpPacket.setPortB(proxyCandidate.getPort()); + rtpPacket.setHostA(localCandidate.getIp()); + rtpPacket.setHostB(proxyCandidate.getIp()); + + // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); + + PacketCollector collector = connection + .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); + + connection.sendPacket(rtpPacket); + + RTPBridge response = (RTPBridge) collector + .nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + + return response; + } + + /** + * Get Public Address from the Server. + * + * @param xmppConnection + * @return public IP String or null if not found + */ + public static String getPublicIP(Connection xmppConnection) { + + if (!xmppConnection.isConnected()) { + return null; + } + + RTPBridge rtpPacket = new RTPBridge(RTPBridge.BridgeAction.publicip); + rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getServiceName()); + rtpPacket.setType(Type.SET); + + // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); + + PacketCollector collector = xmppConnection + .createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID())); + + xmppConnection.sendPacket(rtpPacket); + + RTPBridge response = (RTPBridge) collector + .nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + + if(response == null) return null; + + if (response.getIp() == null || response.getIp().equals("")) return null; + + Enumeration ifaces = null; + try { + ifaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException e) { + e.printStackTrace(); + } + while (ifaces!=null&&ifaces.hasMoreElements()) { + + NetworkInterface iface = ifaces.nextElement(); + Enumeration iaddresses = iface.getInetAddresses(); + + while (iaddresses.hasMoreElements()) { + InetAddress iaddress = iaddresses.nextElement(); + if (!iaddress.isLoopbackAddress()) + if (iaddress.getHostAddress().indexOf(response.getIp()) >= 0) + return null; + + } + } + + return response.getIp(); + } + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ResultListener.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ResultListener.java index a10982835..cae363eae 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ResultListener.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/ResultListener.java @@ -1,29 +1,29 @@ -/** - * - * Copyright 2003-2005 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.jingle.nat; - -/** - * Listener for ECHO Test Results - * - * @author Thiago Camargo - */ -public interface ResultListener { - - public void testFinished(TestResult result, TransportCandidate candidate); - -} +/** + * + * Copyright 2003-2005 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.nat; + +/** + * Listener for ECHO Test Results + * + * @author Thiago Camargo + */ +public interface ResultListener { + + public void testFinished(TestResult result, TransportCandidate candidate); + +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUN.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUN.java index abcab464d..806818532 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUN.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUN.java @@ -1,284 +1,284 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.nat; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -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.smack.provider.IQProvider; -import org.jivesoftware.smack.provider.ProviderManager; -import org.jivesoftware.smackx.ServiceDiscoveryManager; -import org.jivesoftware.smackx.jingle.SmackLogger; -import org.jivesoftware.smackx.packet.DiscoverInfo; -import org.jivesoftware.smackx.packet.DiscoverItems; -import org.xmlpull.v1.XmlPullParser; - -/** - * STUN IQ Packet used to request and retrieve a STUN server and port to make p2p connections easier. STUN is usually used by Jingle Media Transmission between two parties that are behind NAT. - *

    - * High Level Usage Example: - *

    - * STUN stun = STUN.getSTUNServer(connection); - * - * @author Thiago Camargo - */ -public class STUN extends IQ { - - private static final SmackLogger LOGGER = SmackLogger.getLogger(STUN.class); - - private List servers = new ArrayList(); - - private String publicIp = null; - - /** - * Element name of the packet extension. - */ - public static final String DOMAIN = "stun"; - - /** - * Element name of the packet extension. - */ - public static final String ELEMENT_NAME = "query"; - - /** - * Namespace of the packet extension. - */ - public static final String NAMESPACE = "google:jingleinfo"; - - static { - ProviderManager.getInstance().addIQProvider(ELEMENT_NAME, NAMESPACE, new STUN.Provider()); - } - - /** - * Creates a STUN IQ - */ - public STUN() { - } - - /** - * Get a list of STUN Servers recommended by the Server - * - * @return - */ - public List getServers() { - return servers; - } - - /** - * Get Public Ip returned from the XMPP server - * - * @return - */ - public String getPublicIp() { - return publicIp; - } - - /** - * Set Public Ip returned from the XMPP server - * - * @param publicIp - */ - private void setPublicIp(String publicIp) { - this.publicIp = publicIp; - } - - /** - * Get the Child Element XML of the Packet - * - * @return - */ - public String getChildElementXML() { - StringBuilder str = new StringBuilder(); - str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "'/>"); - return str.toString(); - } - - /** - * IQProvider for RTP Bridge packets. - * Parse receive RTPBridge packet to a RTPBridge instance - * - * @author Thiago Rocha - */ - public static class Provider implements IQProvider { - - public Provider() { - super(); - } - - public IQ parseIQ(XmlPullParser parser) throws Exception { - - boolean done = false; - - int eventType; - String elementName; - String namespace; - - if (!parser.getNamespace().equals(NAMESPACE)) - throw new Exception("Not a STUN packet"); - - STUN iq = new STUN(); - - // Start processing sub-elements - while (!done) { - eventType = parser.next(); - elementName = parser.getName(); - namespace = parser.getNamespace(); - - if (eventType == XmlPullParser.START_TAG) { - if (elementName.equals("server")) { - String host = null; - String port = null; - for (int i = 0; i < parser.getAttributeCount(); i++) { - if (parser.getAttributeName(i).equals("host")) - host = parser.getAttributeValue(i); - else if (parser.getAttributeName(i).equals("udp")) - port = parser.getAttributeValue(i); - } - if (host != null && port != null) - iq.servers.add(new StunServerAddress(host, port)); - } - else if (elementName.equals("publicip")) { - String host = null; - for (int i = 0; i < parser.getAttributeCount(); i++) { - if (parser.getAttributeName(i).equals("ip")) - host = parser.getAttributeValue(i); - } - if (host != null && !host.equals("")) - iq.setPublicIp(host); - } - } - else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals(ELEMENT_NAME)) { - done = true; - } - } - } - return iq; - } - } - - /** - * Get a new STUN Server Address and port from the server. - * If a error occurs or the server don't support STUN Service, null is returned. - * - * @param connection - * @return - */ - public static STUN getSTUNServer(Connection connection) { - - if (!connection.isConnected()) { - return null; - } - - STUN stunPacket = new STUN(); - stunPacket.setTo(DOMAIN + "." + connection.getServiceName()); - - PacketCollector collector = connection - .createPacketCollector(new PacketIDFilter(stunPacket.getPacketID())); - - connection.sendPacket(stunPacket); - - STUN response = (STUN) collector - .nextResult(SmackConfiguration.getPacketReplyTimeout()); - - // Cancel the collector. - collector.cancel(); - - return response; - } - - /** - * Check if the server support STUN Service. - * - * @param xmppConnection - * @return - */ - public static boolean serviceAvailable(Connection connection) { - - if (!connection.isConnected()) { - return false; - } - - LOGGER.debug("Service listing"); - - ServiceDiscoveryManager disco = ServiceDiscoveryManager - .getInstanceFor(connection); - try { - DiscoverItems items = disco.discoverItems(connection.getServiceName()); - - Iterator iter = items.getItems(); - while (iter.hasNext()) { - DiscoverItems.Item item = iter.next(); - DiscoverInfo info = disco.discoverInfo(item.getEntityID()); - - Iterator iter2 = info.getIdentities(); - while (iter2.hasNext()) { - DiscoverInfo.Identity identity = iter2.next(); - if (identity.getCategory().equals("proxy") && identity.getType().equals("stun")) - if (info.containsFeature(NAMESPACE)) - return true; - } - - LOGGER.debug(item.getName()+"-"+info.getType()); - - } - } - catch (XMPPException e) { - e.printStackTrace(); - } - return false; - } - - /** - * Provides easy abstract to store STUN Server Addresses and Ports - */ - public static class StunServerAddress { - - private String server; - private String port; - - public StunServerAddress(String server, String port) { - this.server = server; - this.port = port; - } - - /** - * Get the Host Address - * - * @return - */ - public String getServer() { - return server; - } - - /** - * Get the Server Port - * - * @return - */ - public String getPort() { - return port; - } - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.nat; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.jingle.SmackLogger; +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.jivesoftware.smackx.packet.DiscoverItems; +import org.xmlpull.v1.XmlPullParser; + +/** + * STUN IQ Packet used to request and retrieve a STUN server and port to make p2p connections easier. STUN is usually used by Jingle Media Transmission between two parties that are behind NAT. + *

    + * High Level Usage Example: + *

    + * STUN stun = STUN.getSTUNServer(connection); + * + * @author Thiago Camargo + */ +public class STUN extends IQ { + + private static final SmackLogger LOGGER = SmackLogger.getLogger(STUN.class); + + private List servers = new ArrayList(); + + private String publicIp = null; + + /** + * Element name of the packet extension. + */ + public static final String DOMAIN = "stun"; + + /** + * Element name of the packet extension. + */ + public static final String ELEMENT_NAME = "query"; + + /** + * Namespace of the packet extension. + */ + public static final String NAMESPACE = "google:jingleinfo"; + + static { + ProviderManager.getInstance().addIQProvider(ELEMENT_NAME, NAMESPACE, new STUN.Provider()); + } + + /** + * Creates a STUN IQ + */ + public STUN() { + } + + /** + * Get a list of STUN Servers recommended by the Server + * + * @return + */ + public List getServers() { + return servers; + } + + /** + * Get Public Ip returned from the XMPP server + * + * @return + */ + public String getPublicIp() { + return publicIp; + } + + /** + * Set Public Ip returned from the XMPP server + * + * @param publicIp + */ + private void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + /** + * Get the Child Element XML of the Packet + * + * @return + */ + public String getChildElementXML() { + StringBuilder str = new StringBuilder(); + str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "'/>"); + return str.toString(); + } + + /** + * IQProvider for RTP Bridge packets. + * Parse receive RTPBridge packet to a RTPBridge instance + * + * @author Thiago Rocha + */ + public static class Provider implements IQProvider { + + public Provider() { + super(); + } + + public IQ parseIQ(XmlPullParser parser) throws Exception { + + boolean done = false; + + int eventType; + String elementName; + String namespace; + + if (!parser.getNamespace().equals(NAMESPACE)) + throw new Exception("Not a STUN packet"); + + STUN iq = new STUN(); + + // Start processing sub-elements + while (!done) { + eventType = parser.next(); + elementName = parser.getName(); + namespace = parser.getNamespace(); + + if (eventType == XmlPullParser.START_TAG) { + if (elementName.equals("server")) { + String host = null; + String port = null; + for (int i = 0; i < parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("host")) + host = parser.getAttributeValue(i); + else if (parser.getAttributeName(i).equals("udp")) + port = parser.getAttributeValue(i); + } + if (host != null && port != null) + iq.servers.add(new StunServerAddress(host, port)); + } + else if (elementName.equals("publicip")) { + String host = null; + for (int i = 0; i < parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("ip")) + host = parser.getAttributeValue(i); + } + if (host != null && !host.equals("")) + iq.setPublicIp(host); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(ELEMENT_NAME)) { + done = true; + } + } + } + return iq; + } + } + + /** + * Get a new STUN Server Address and port from the server. + * If a error occurs or the server don't support STUN Service, null is returned. + * + * @param connection + * @return + */ + public static STUN getSTUNServer(Connection connection) { + + if (!connection.isConnected()) { + return null; + } + + STUN stunPacket = new STUN(); + stunPacket.setTo(DOMAIN + "." + connection.getServiceName()); + + PacketCollector collector = connection + .createPacketCollector(new PacketIDFilter(stunPacket.getPacketID())); + + connection.sendPacket(stunPacket); + + STUN response = (STUN) collector + .nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + + return response; + } + + /** + * Check if the server support STUN Service. + * + * @param xmppConnection + * @return + */ + public static boolean serviceAvailable(Connection connection) { + + if (!connection.isConnected()) { + return false; + } + + LOGGER.debug("Service listing"); + + ServiceDiscoveryManager disco = ServiceDiscoveryManager + .getInstanceFor(connection); + try { + DiscoverItems items = disco.discoverItems(connection.getServiceName()); + + Iterator iter = items.getItems(); + while (iter.hasNext()) { + DiscoverItems.Item item = iter.next(); + DiscoverInfo info = disco.discoverInfo(item.getEntityID()); + + Iterator iter2 = info.getIdentities(); + while (iter2.hasNext()) { + DiscoverInfo.Identity identity = iter2.next(); + if (identity.getCategory().equals("proxy") && identity.getType().equals("stun")) + if (info.containsFeature(NAMESPACE)) + return true; + } + + LOGGER.debug(item.getName()+"-"+info.getType()); + + } + } + catch (XMPPException e) { + e.printStackTrace(); + } + return false; + } + + /** + * Provides easy abstract to store STUN Server Addresses and Ports + */ + public static class StunServerAddress { + + private String server; + private String port; + + public StunServerAddress(String server, String port) { + this.server = server; + this.port = port; + } + + /** + * Get the Host Address + * + * @return + */ + public String getServer() { + return server; + } + + /** + * Get the Server Port + * + * @return + */ + public String getPort() { + return port; + } + } +} diff --git a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUNTransportManager.java b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUNTransportManager.java index f5eeab1e1..8b9b39053 100644 --- a/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUNTransportManager.java +++ b/jingle/src/main/java/org/jivesoftware/smackx/jingle/nat/STUNTransportManager.java @@ -1,48 +1,48 @@ -/** - * - * Copyright 2003-2006 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smackx.jingle.JingleSession; - -/** - * A Jingle Transport Manager implementation to be used on NAT networks with STUN Service NOT Blocked. - * - * @author Thiago Camargo - */ -public class STUNTransportManager extends JingleTransportManager { - STUNResolver stunResolver = null; - - public STUNTransportManager() { - stunResolver = new STUNResolver() { - }; - try { - stunResolver.initializeAndWait(); - } catch (XMPPException e) { - e.printStackTrace(); - } - } - - protected TransportResolver createResolver(JingleSession session) { - try { - stunResolver.resolve(session); - } catch (XMPPException e) { - e.printStackTrace(); - } - return stunResolver; - } -} +/** + * + * Copyright 2003-2006 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleSession; + +/** + * A Jingle Transport Manager implementation to be used on NAT networks with STUN Service NOT Blocked. + * + * @author Thiago Camargo + */ +public class STUNTransportManager extends JingleTransportManager { + STUNResolver stunResolver = null; + + public STUNTransportManager() { + stunResolver = new STUNResolver() { + }; + try { + stunResolver.initializeAndWait(); + } catch (XMPPException e) { + e.printStackTrace(); + } + } + + protected TransportResolver createResolver(JingleSession session) { + try { + stunResolver.resolve(session); + } catch (XMPPException e) { + e.printStackTrace(); + } + return stunResolver; + } +} diff --git a/jingle/src/test/java/org/jivesoftware/jingle/JingleMediaTest.java b/jingle/src/test/java/org/jivesoftware/jingle/JingleMediaTest.java index 203fbe2f4..e0bb72a53 100644 --- a/jingle/src/test/java/org/jivesoftware/jingle/JingleMediaTest.java +++ b/jingle/src/test/java/org/jivesoftware/jingle/JingleMediaTest.java @@ -1,538 +1,538 @@ -package org.jivesoftware.smackx.jingle; - -/** - *

    - * Copyright 2003-2006 Jive Software. - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.test.SmackTestCase; -import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; -import org.jivesoftware.smackx.jingle.media.JingleMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.jmf.AudioChannel; -import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.multi.MultiMediaManager; -import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager; -import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager; -import org.jivesoftware.smackx.jingle.nat.ICETransportManager; -import org.jivesoftware.smackx.jingle.nat.STUNTransportManager; -import org.jivesoftware.smackx.packet.JingleError; - -import javax.media.MediaLocator; -import javax.media.format.AudioFormat; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -/** - * Test the Jingle Media using the high level API - *

    - * - * @author Thiago Camargo - */ -public class JingleMediaTest extends SmackTestCase { - - public JingleMediaTest(final String name) { - super(name); - } - - public void testCompleteJmf() { - - XMPPConnection x0 = getConnection(0); - XMPPConnection x1 = getConnection(1); - - for (int i = 0; i < 1; i++) - try { - - ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478); - ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478); - - JingleMediaManager jingleMediaManager0 = new JmfMediaManager(icetm0); - JingleMediaManager jingleMediaManager1 = new JmfMediaManager(icetm1); - - List jml0 = new ArrayList(); - List jml1 = new ArrayList(); - - jml0.add(jingleMediaManager0); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm0.addCreationListener(icetm0); - jm1.addCreationListener(icetm1); - - JingleSessionRequestListener jingleSessionRequestListener = new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - try { - JingleSession session = request.accept(); - session.startIncoming(); - - // session.addStateListener(new JingleSessionStateListener() { - // public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne) - // throws JingleNegotiator.JingleException { - // if (newOne instanceof IncomingJingleSession.Active) { - // throw new JingleNegotiator.JingleException(); - // } - // } - // - // public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) { - // - // } - // }); - - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }; - - jm1.addJingleSessionRequestListener(jingleSessionRequestListener); - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(20000); - - JingleSession incomingJingleSession = jm1.getSession(js0.getConnection().getUser()); - //JingleSession.removeAllStateListeners(); - - Thread.sleep(15000); - - js0.terminate(); - - jm1.removeJingleSessionRequestListener(jingleSessionRequestListener); - - Thread.sleep(60000); - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public void testCompleteMulti() { - - try { - - XMPPConnection x0 = getConnection(0); - XMPPConnection x1 = getConnection(1); - - ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478); - ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478); - - MultiMediaManager jingleMediaManager0 = new MultiMediaManager(icetm0); - jingleMediaManager0.addMediaManager(new JmfMediaManager(icetm0)); - jingleMediaManager0.addMediaManager(new SpeexMediaManager(icetm0)); - jingleMediaManager0.setPreferredPayloadType(jingleMediaManager0.getPayloads().get(1)); - List jml0 = new ArrayList(); - jml0.add(jingleMediaManager0); - - MultiMediaManager jingleMediaManager1 = new MultiMediaManager(icetm1); - jingleMediaManager1.addMediaManager(new JmfMediaManager(icetm1)); - jingleMediaManager1.addMediaManager(new SpeexMediaManager(icetm1)); - jingleMediaManager1.setPreferredPayloadType(jingleMediaManager1.getPayloads().get(2)); - List jml1 = new ArrayList(); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm0.addCreationListener(icetm0); - jm1.addCreationListener(icetm1); - - jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - - try { - JingleSession session = request.accept(); - try { - Thread.sleep(12000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - session.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - - for (int i = 0; i < 10; i++) { - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - // js0.addStateListener(new JingleSessionStateListener() { - // - // public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne) - // throws JingleNegotiator.JingleException { - // } - // - // public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) { - // if (newOne != null) { - // if ((newOne instanceof OutgoingJingleSession.Active)) - // System.err.println("|||" + newOne.getClass().getCanonicalName() + "|||"); - // } - // } - // }); - - js0.startOutgoing(); - - Thread.sleep(45000); - js0.terminate(); - - Thread.sleep(1500); - - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public void testCompleteSpeex() { - - try { - - //XMPPConnection.DEBUG_ENABLED = true; - - XMPPConnection x0 = getConnection(0); - XMPPConnection x1 = getConnection(1); - - JingleMediaManager jingleMediaManager0 = new SpeexMediaManager(new STUNTransportManager()); - JingleMediaManager jingleMediaManager1 = new SpeexMediaManager(new STUNTransportManager()); - - List jml0 = new ArrayList(); - List jml1 = new ArrayList(); - - jml0.add(jingleMediaManager0); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - - try { - - JingleSession session = request.accept(); - - session.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(150000); - js0.terminate(); - - Thread.sleep(6000); - - x0.disconnect(); - x1.disconnect(); - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public void testCompleteScreenShare() { - - try { - - XMPPConnection x0 = getConnection(0); - XMPPConnection x1 = getConnection(1); - - ICETransportManager icetm0 = new ICETransportManager(x0, "stun.xten.net", 3478); - ICETransportManager icetm1 = new ICETransportManager(x1, "stun.xten.net", 3478); - - JingleMediaManager jingleMediaManager0 = new ScreenShareMediaManager(icetm0); - JingleMediaManager jingleMediaManager1 = new ScreenShareMediaManager(icetm1); - - List jml0 = new ArrayList(); - List jml1 = new ArrayList(); - - jml0.add(jingleMediaManager0); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - - try { - - JingleSession session = request.accept(); - - session.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(150000); - js0.terminate(); - - Thread.sleep(6000); - - x0.disconnect(); - x1.disconnect(); - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public void testCompleteWithBridge() { - - for (int i = 0; i < 1; i += 2) { - final int n = i; - Thread t = new Thread(new Runnable() { - public void run() { - try { - - XMPPConnection x0 = getConnection(n); - XMPPConnection x1 = getConnection(n + 1); - - BridgedTransportManager btm0 = new BridgedTransportManager(x0); - BridgedTransportManager btm1 = new BridgedTransportManager(x1); - - - JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0); - JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1); - - List jml0 = new ArrayList(); - List jml1 = new ArrayList(); - - jml0.add(jingleMediaManager0); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm0.addCreationListener(btm0); - jm1.addCreationListener(btm1); - - jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - - try { - JingleSession session = request.accept(); - - session.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(20000); - - //js0.sendFormattedError(JingleError.UNSUPPORTED_TRANSPORTS); - js0.sendPacket(js0.createJingleError(null, JingleError.UNSUPPORTED_TRANSPORTS)); - - - Thread.sleep(20000); - - js0.terminate(); - - Thread.sleep(3000); - - x0.disconnect(); - x1.disconnect(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - - t.start(); - } - - try { - Thread.sleep(250000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public void testCompleteWithBridgeB() { - try { - - //XMPPConnection.DEBUG_ENABLED = true; - - XMPPConnection x0 = getConnection(0); - XMPPConnection x1 = getConnection(1); - - BridgedTransportManager btm0 = new BridgedTransportManager(x0); - BridgedTransportManager btm1 = new BridgedTransportManager(x1); - - - JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0); - JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1); - - List jml0 = new ArrayList(); - List jml1 = new ArrayList(); - - jml0.add(jingleMediaManager0); - jml1.add(jingleMediaManager1); - - final JingleManager jm0 = new JingleManager(x0, jml0); - final JingleManager jm1 = new JingleManager(x1, jml1); - - jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { - public void sessionRequested(final JingleSessionRequest request) { - - try { - - JingleSession session = request.accept(); - - session.startIncoming(); - } catch (XMPPException e) { - e.printStackTrace(); - } - - } - }); - - JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(20000); - - js0.terminate(); - - Thread.sleep(3000); - - js0 = jm0.createOutgoingJingleSession(x1.getUser()); - - js0.startOutgoing(); - - Thread.sleep(20000); - - js0.terminate(); - - Thread.sleep(3000); - - x0.disconnect(); - x1.disconnect(); - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public void testAudioChannelOpenClose() { - for (int i = 0; i < 5; i++) { - try { - AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() - .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, new AudioFormat( - AudioFormat.GSM_RTP), null); - AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() - .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, new AudioFormat( - AudioFormat.GSM_RTP), null); - - audioChannel0.start(); - audioChannel1.start(); - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - audioChannel0.stop(); - audioChannel1.stop(); - - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public void testAudioChannelStartStop() { - - try { - AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() - .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, - new AudioFormat(AudioFormat.GSM_RTP), null); - AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() - .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, - new AudioFormat(AudioFormat.GSM_RTP), null); - - for (int i = 0; i < 5; i++) { - - audioChannel0.start(); - audioChannel1.start(); - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - audioChannel0.stop(); - audioChannel1.stop(); - - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - protected int getMaxConnections() { - return 2; - } -} \ No newline at end of file +package org.jivesoftware.smackx.jingle; + +/** + *

    + * Copyright 2003-2006 Jive Software. + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.jmf.AudioChannel; +import org.jivesoftware.smackx.jingle.mediaimpl.jmf.JmfMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.jspeex.SpeexMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.multi.MultiMediaManager; +import org.jivesoftware.smackx.jingle.mediaimpl.sshare.ScreenShareMediaManager; +import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager; +import org.jivesoftware.smackx.jingle.nat.ICETransportManager; +import org.jivesoftware.smackx.jingle.nat.STUNTransportManager; +import org.jivesoftware.smackx.packet.JingleError; + +import javax.media.MediaLocator; +import javax.media.format.AudioFormat; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +/** + * Test the Jingle Media using the high level API + *

    + * + * @author Thiago Camargo + */ +public class JingleMediaTest extends SmackTestCase { + + public JingleMediaTest(final String name) { + super(name); + } + + public void testCompleteJmf() { + + XMPPConnection x0 = getConnection(0); + XMPPConnection x1 = getConnection(1); + + for (int i = 0; i < 1; i++) + try { + + ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478); + ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478); + + JingleMediaManager jingleMediaManager0 = new JmfMediaManager(icetm0); + JingleMediaManager jingleMediaManager1 = new JmfMediaManager(icetm1); + + List jml0 = new ArrayList(); + List jml1 = new ArrayList(); + + jml0.add(jingleMediaManager0); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm0.addCreationListener(icetm0); + jm1.addCreationListener(icetm1); + + JingleSessionRequestListener jingleSessionRequestListener = new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + try { + JingleSession session = request.accept(); + session.startIncoming(); + + // session.addStateListener(new JingleSessionStateListener() { + // public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne) + // throws JingleNegotiator.JingleException { + // if (newOne instanceof IncomingJingleSession.Active) { + // throw new JingleNegotiator.JingleException(); + // } + // } + // + // public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) { + // + // } + // }); + + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }; + + jm1.addJingleSessionRequestListener(jingleSessionRequestListener); + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(20000); + + JingleSession incomingJingleSession = jm1.getSession(js0.getConnection().getUser()); + //JingleSession.removeAllStateListeners(); + + Thread.sleep(15000); + + js0.terminate(); + + jm1.removeJingleSessionRequestListener(jingleSessionRequestListener); + + Thread.sleep(60000); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void testCompleteMulti() { + + try { + + XMPPConnection x0 = getConnection(0); + XMPPConnection x1 = getConnection(1); + + ICETransportManager icetm0 = new ICETransportManager(x0, "jivesoftware.com", 3478); + ICETransportManager icetm1 = new ICETransportManager(x1, "jivesoftware.com", 3478); + + MultiMediaManager jingleMediaManager0 = new MultiMediaManager(icetm0); + jingleMediaManager0.addMediaManager(new JmfMediaManager(icetm0)); + jingleMediaManager0.addMediaManager(new SpeexMediaManager(icetm0)); + jingleMediaManager0.setPreferredPayloadType(jingleMediaManager0.getPayloads().get(1)); + List jml0 = new ArrayList(); + jml0.add(jingleMediaManager0); + + MultiMediaManager jingleMediaManager1 = new MultiMediaManager(icetm1); + jingleMediaManager1.addMediaManager(new JmfMediaManager(icetm1)); + jingleMediaManager1.addMediaManager(new SpeexMediaManager(icetm1)); + jingleMediaManager1.setPreferredPayloadType(jingleMediaManager1.getPayloads().get(2)); + List jml1 = new ArrayList(); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm0.addCreationListener(icetm0); + jm1.addCreationListener(icetm1); + + jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + + try { + JingleSession session = request.accept(); + try { + Thread.sleep(12000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + session.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + + for (int i = 0; i < 10; i++) { + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + // js0.addStateListener(new JingleSessionStateListener() { + // + // public void beforeChange(JingleNegotiator.State old, JingleNegotiator.State newOne) + // throws JingleNegotiator.JingleException { + // } + // + // public void afterChanged(JingleNegotiator.State old, JingleNegotiator.State newOne) { + // if (newOne != null) { + // if ((newOne instanceof OutgoingJingleSession.Active)) + // System.err.println("|||" + newOne.getClass().getCanonicalName() + "|||"); + // } + // } + // }); + + js0.startOutgoing(); + + Thread.sleep(45000); + js0.terminate(); + + Thread.sleep(1500); + + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void testCompleteSpeex() { + + try { + + //XMPPConnection.DEBUG_ENABLED = true; + + XMPPConnection x0 = getConnection(0); + XMPPConnection x1 = getConnection(1); + + JingleMediaManager jingleMediaManager0 = new SpeexMediaManager(new STUNTransportManager()); + JingleMediaManager jingleMediaManager1 = new SpeexMediaManager(new STUNTransportManager()); + + List jml0 = new ArrayList(); + List jml1 = new ArrayList(); + + jml0.add(jingleMediaManager0); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + + try { + + JingleSession session = request.accept(); + + session.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(150000); + js0.terminate(); + + Thread.sleep(6000); + + x0.disconnect(); + x1.disconnect(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void testCompleteScreenShare() { + + try { + + XMPPConnection x0 = getConnection(0); + XMPPConnection x1 = getConnection(1); + + ICETransportManager icetm0 = new ICETransportManager(x0, "stun.xten.net", 3478); + ICETransportManager icetm1 = new ICETransportManager(x1, "stun.xten.net", 3478); + + JingleMediaManager jingleMediaManager0 = new ScreenShareMediaManager(icetm0); + JingleMediaManager jingleMediaManager1 = new ScreenShareMediaManager(icetm1); + + List jml0 = new ArrayList(); + List jml1 = new ArrayList(); + + jml0.add(jingleMediaManager0); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + + try { + + JingleSession session = request.accept(); + + session.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(150000); + js0.terminate(); + + Thread.sleep(6000); + + x0.disconnect(); + x1.disconnect(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void testCompleteWithBridge() { + + for (int i = 0; i < 1; i += 2) { + final int n = i; + Thread t = new Thread(new Runnable() { + public void run() { + try { + + XMPPConnection x0 = getConnection(n); + XMPPConnection x1 = getConnection(n + 1); + + BridgedTransportManager btm0 = new BridgedTransportManager(x0); + BridgedTransportManager btm1 = new BridgedTransportManager(x1); + + + JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0); + JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1); + + List jml0 = new ArrayList(); + List jml1 = new ArrayList(); + + jml0.add(jingleMediaManager0); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm0.addCreationListener(btm0); + jm1.addCreationListener(btm1); + + jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + + try { + JingleSession session = request.accept(); + + session.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(20000); + + //js0.sendFormattedError(JingleError.UNSUPPORTED_TRANSPORTS); + js0.sendPacket(js0.createJingleError(null, JingleError.UNSUPPORTED_TRANSPORTS)); + + + Thread.sleep(20000); + + js0.terminate(); + + Thread.sleep(3000); + + x0.disconnect(); + x1.disconnect(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + t.start(); + } + + try { + Thread.sleep(250000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void testCompleteWithBridgeB() { + try { + + //XMPPConnection.DEBUG_ENABLED = true; + + XMPPConnection x0 = getConnection(0); + XMPPConnection x1 = getConnection(1); + + BridgedTransportManager btm0 = new BridgedTransportManager(x0); + BridgedTransportManager btm1 = new BridgedTransportManager(x1); + + + JingleMediaManager jingleMediaManager0 = new JmfMediaManager(btm0); + JingleMediaManager jingleMediaManager1 = new JmfMediaManager(btm1); + + List jml0 = new ArrayList(); + List jml1 = new ArrayList(); + + jml0.add(jingleMediaManager0); + jml1.add(jingleMediaManager1); + + final JingleManager jm0 = new JingleManager(x0, jml0); + final JingleManager jm1 = new JingleManager(x1, jml1); + + jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() { + public void sessionRequested(final JingleSessionRequest request) { + + try { + + JingleSession session = request.accept(); + + session.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + + } + }); + + JingleSession js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(20000); + + js0.terminate(); + + Thread.sleep(3000); + + js0 = jm0.createOutgoingJingleSession(x1.getUser()); + + js0.startOutgoing(); + + Thread.sleep(20000); + + js0.terminate(); + + Thread.sleep(3000); + + x0.disconnect(); + x1.disconnect(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public void testAudioChannelOpenClose() { + for (int i = 0; i < 5; i++) { + try { + AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() + .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, new AudioFormat( + AudioFormat.GSM_RTP), null); + AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() + .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, new AudioFormat( + AudioFormat.GSM_RTP), null); + + audioChannel0.start(); + audioChannel1.start(); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + audioChannel0.stop(); + audioChannel1.stop(); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public void testAudioChannelStartStop() { + + try { + AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() + .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7002, 7020, + new AudioFormat(AudioFormat.GSM_RTP), null); + AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://"), InetAddress.getLocalHost() + .getHostAddress(), InetAddress.getLocalHost().getHostAddress(), 7020, 7002, + new AudioFormat(AudioFormat.GSM_RTP), null); + + for (int i = 0; i < 5; i++) { + + audioChannel0.start(); + audioChannel1.start(); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + audioChannel0.stop(); + audioChannel1.stop(); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected int getMaxConnections() { + return 2; + } +} diff --git a/jingle/src/test/java/org/jivesoftware/jingle/nat/BridgedResolverTest.java b/jingle/src/test/java/org/jivesoftware/jingle/nat/BridgedResolverTest.java index 7f2b81abf..9f9ff32e4 100644 --- a/jingle/src/test/java/org/jivesoftware/jingle/nat/BridgedResolverTest.java +++ b/jingle/src/test/java/org/jivesoftware/jingle/nat/BridgedResolverTest.java @@ -12,108 +12,108 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smackx.jingle.nat; - -import org.jivesoftware.smack.test.SmackTestCase; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -public class BridgedResolverTest extends SmackTestCase { - - private int counter; - - private final Object mutex = new Object(); - - public BridgedResolverTest(String arg) { - super(arg); - } - - // Counter management - - private void resetCounter() { - synchronized (mutex) { - counter = 0; - } - } - - private void incCounter() { - synchronized (mutex) { - counter++; - } - } - - private int valCounter() { - int val; - synchronized (mutex) { - val = counter; - } - return val; - } - - public void testCheckService() { - assertTrue(RTPBridge.serviceAvailable(getConnection(0))); - } - - public void testGetBridge() { - - resetCounter(); - - RTPBridge rtpBridge = RTPBridge.getRTPBridge(getConnection(0), "001"); - - System.out.println(rtpBridge.getIp() + " portA:" + rtpBridge.getPortA() + " portB:" + rtpBridge.getPortB()); - - if (rtpBridge != null) { - if (rtpBridge.getIp() != null) incCounter(); - if (rtpBridge.getPortA() != -1) incCounter(); - if (rtpBridge.getPortB() != -1) incCounter(); - } - - assertTrue(valCounter() == 3); - } - - public void testGetPublicIp() { - - resetCounter(); - - String publicIp = RTPBridge.getPublicIP(getConnection(0)); - - System.out.println(publicIp); - - if (publicIp != null) { - incCounter(); - } - - try { - InetAddress localaddr = InetAddress.getLocalHost(); - System.out.println("main Local IP Address : " + localaddr.getHostAddress()); - System.out.println("main Local hostname : " + localaddr.getHostName()); - - InetAddress[] localaddrs = InetAddress.getAllByName("localhost"); - for (int i = 0; i < localaddrs.length; i++) { - if (!localaddrs[i].equals(localaddr)) { - System.out.println("alt Local IP Address : " + localaddrs[i].getHostAddress()); - System.out.println("alt Local hostname : " + localaddrs[i].getHostName()); - System.out.println(); - } - } - } - catch (UnknownHostException e) { - System.err.println("Can't detect localhost : " + e); - } - - try { - Thread.sleep(1000); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - assertTrue(valCounter() == 1); - } - - protected int getMaxConnections() { - return 1; - } - -} +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.test.SmackTestCase; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class BridgedResolverTest extends SmackTestCase { + + private int counter; + + private final Object mutex = new Object(); + + public BridgedResolverTest(String arg) { + super(arg); + } + + // Counter management + + private void resetCounter() { + synchronized (mutex) { + counter = 0; + } + } + + private void incCounter() { + synchronized (mutex) { + counter++; + } + } + + private int valCounter() { + int val; + synchronized (mutex) { + val = counter; + } + return val; + } + + public void testCheckService() { + assertTrue(RTPBridge.serviceAvailable(getConnection(0))); + } + + public void testGetBridge() { + + resetCounter(); + + RTPBridge rtpBridge = RTPBridge.getRTPBridge(getConnection(0), "001"); + + System.out.println(rtpBridge.getIp() + " portA:" + rtpBridge.getPortA() + " portB:" + rtpBridge.getPortB()); + + if (rtpBridge != null) { + if (rtpBridge.getIp() != null) incCounter(); + if (rtpBridge.getPortA() != -1) incCounter(); + if (rtpBridge.getPortB() != -1) incCounter(); + } + + assertTrue(valCounter() == 3); + } + + public void testGetPublicIp() { + + resetCounter(); + + String publicIp = RTPBridge.getPublicIP(getConnection(0)); + + System.out.println(publicIp); + + if (publicIp != null) { + incCounter(); + } + + try { + InetAddress localaddr = InetAddress.getLocalHost(); + System.out.println("main Local IP Address : " + localaddr.getHostAddress()); + System.out.println("main Local hostname : " + localaddr.getHostName()); + + InetAddress[] localaddrs = InetAddress.getAllByName("localhost"); + for (int i = 0; i < localaddrs.length; i++) { + if (!localaddrs[i].equals(localaddr)) { + System.out.println("alt Local IP Address : " + localaddrs[i].getHostAddress()); + System.out.println("alt Local hostname : " + localaddrs[i].getHostName()); + System.out.println(); + } + } + } + catch (UnknownHostException e) { + System.err.println("Can't detect localhost : " + e); + } + + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + assertTrue(valCounter() == 1); + } + + protected int getMaxConnections() { + return 1; + } + +} diff --git a/jingle/src/test/resources/test-case.xml b/jingle/src/test/resources/test-case.xml index d477c269e..d86db2f0f 100644 --- a/jingle/src/test/resources/test-case.xml +++ b/jingle/src/test/resources/test-case.xml @@ -1,13 +1,13 @@ - - - - - - localhost - 5222 - - - chat - conference - + + + + + + localhost + 5222 + + + chat + conference + \ No newline at end of file diff --git a/legacy/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java b/legacy/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java index 692b4d470..3765129a2 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/WorkgroupProviderInitializer.java @@ -1,27 +1,27 @@ -/** - * - * Copyright the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; - -public class WorkgroupProviderInitializer extends UrlProviderFileInitializer { - - @Override - protected String getFilePath() { - return "classpath:org.jivesoftware.smackx/workgroup.providers"; - } -} +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.initializer.UrlProviderFileInitializer; + +public class WorkgroupProviderInitializer extends UrlProviderFileInitializer { + + @Override + protected String getFilePath() { + return "classpath:org.jivesoftware.smackx/workgroup.providers"; + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/MetaData.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/MetaData.java index b4026e665..37d953220 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/MetaData.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/MetaData.java @@ -1,66 +1,66 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData; - - public MetaData(Map> metaData) { - this.metaData = metaData; - } - - /** - * @return the Map of metadata contained by this instance - */ - public Map> 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData; + + public MetaData(Map> metaData) { + this.metaData = metaData; + } + + /** + * @return the Map of metadata contained by this instance + */ + public Map> getMetaData() { + return metaData; + } + + public String getElementName() { + return ELEMENT_NAME; + } + + public String getNamespace() { + return NAMESPACE; + } + + public String toXML() { + return MetaDataUtils.serializeMetaData(this.getMetaData()); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/QueueUser.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/QueueUser.java index f9cab70a0..4077ffe7a 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/QueueUser.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/QueueUser.java @@ -1,83 +1,83 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java index 76d04a8be..fa0cb841c 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java @@ -1,132 +1,132 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> 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> 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> getMetaData () { - return this.metaData; - } - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> 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> 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> getMetaData () { + return this.metaData; + } + +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java index 447cc5a1a..687694cb2 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java @@ -1,37 +1,37 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Agent.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Agent.java index f324aa801..70977a926 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Agent.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Agent.java @@ -1,136 +1,136 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Agent 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 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Agent 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 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java index 1b818dbef..53525420a 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java @@ -1,385 +1,385 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -import java.util.logging.Logger; - -/** - * Manges information about the agents in a workgroup and their presence. - * - * @author Matt Tucker - * @see AgentSession#getAgentRoster() - */ -public class AgentRoster { - private static Logger log = Logger.getLogger(AgentRoster.class.getName()); - 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 entries; - private List listeners; - private Map> 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(); - listeners = new ArrayList(); - presenceMap = new HashMap>(); - // 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 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 userPresences = presenceMap.get(jid); - if (userPresences != null) { - Iterator 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 getAgents() { - Set agents = new HashSet(); - synchronized (entries) { - for (Iterator 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 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 null if the agent - * is unavailable (offline) or if no presence information is available.

    - * - * @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 null if the agent is unavailable - * or if no presence information is available.. - */ - public Presence getPresence(String user) { - String key = getPresenceMapKey(user); - Map 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 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? - log.warning("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 userPresences; - // Get the user presence map - if (presenceMap.get(key) == null) { - userPresences = new HashMap(); - 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 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 userPresences = presenceMap.get(key); - synchronized (userPresences) { - userPresences.remove(StringUtils.parseResource(from)); - } - if (userPresences.isEmpty()) { - presenceMap.remove(key); - } - } - // Fire an event. - synchronized (entries) { - for (Iterator 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 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; +import java.util.logging.Logger; + +/** + * Manges information about the agents in a workgroup and their presence. + * + * @author Matt Tucker + * @see AgentSession#getAgentRoster() + */ +public class AgentRoster { + private static Logger log = Logger.getLogger(AgentRoster.class.getName()); + 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 entries; + private List listeners; + private Map> 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(); + listeners = new ArrayList(); + presenceMap = new HashMap>(); + // 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 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 userPresences = presenceMap.get(jid); + if (userPresences != null) { + Iterator 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 getAgents() { + Set agents = new HashSet(); + synchronized (entries) { + for (Iterator 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 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 null if the agent + * is unavailable (offline) or if no presence information is available.

    + * + * @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 null if the agent is unavailable + * or if no presence information is available.. + */ + public Presence getPresence(String user) { + String key = getPresenceMapKey(user); + Map 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 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? + log.warning("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 userPresences; + // Get the user presence map + if (presenceMap.get(key) == null) { + userPresences = new HashMap(); + 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 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 userPresences = presenceMap.get(key); + synchronized (userPresences) { + userPresences.remove(StringUtils.parseResource(from)); + } + if (userPresences.isEmpty()) { + presenceMap.remove(key); + } + } + // Fire an event. + synchronized (entries) { + for (Iterator 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 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; + } + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java index f12765065..d9efa7912 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java @@ -1,33 +1,33 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java index c15688ea2..c7dd7ba12 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java @@ -1,1185 +1,1185 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.muc.packet.MUCUser; -import org.jivesoftware.smackx.search.ReportedData; -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.smackx.xdata.Form; -import org.jivesoftware.smack.*; -import org.jivesoftware.smack.filter.*; -import org.jivesoftware.smack.packet.*; -import org.jivesoftware.smack.util.StringUtils; - -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * 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 static Logger log = Logger.getLogger(AgentSession.class.getName()); - - private Connection connection; - - private String workgroupJID; - - private boolean online = false; - private Presence.Mode presenceMode; - private int maxChats; - private final Map> metaData; - - private Map queues; - - private final List offerListeners; - private final List invitationListeners; - private final List 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 true 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>(); - - this.queues = new HashMap(); - - offerListeners = new ArrayList(); - invitationListeners = new ArrayList(); - queueUsersListeners = new ArrayList(); - - // 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) { - log.log(Level.SEVERE, "Error processing packet", e); - } - } - }; - 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 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 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 null if the meta-data - * doesn't exist.. - */ - public List 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:

      - *

      - *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats - * (equivalent to Presence.Mode.CHAT). - *
    • 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. - *
    • Presence.Mode.AWAY -- the agent is not available and should not - * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    - *

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

      - *

      - *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats - * (equivalent to Presence.Mode.CHAT). - *
    • 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. - *
    • Presence.Mode.AWAY -- the agent is not available and should not - * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    - *

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

      - *

      - *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats - * (equivalent to Presence.Mode.CHAT). - *
    • 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. - *
    • Presence.Mode.AWAY -- the agent is not available and should not - * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    - * - * @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 - *

    - * 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 getQueues() { - return Collections.unmodifiableMap((new HashMap(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> 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 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> 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 ChatNote 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. - * @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> 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.

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

    - * - * The agent or user that accepted the offer MUST 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.

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

    - * - * Once the invitee joins the support room the workgroup service will kick the inviter from the room.

    - * - * 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.muc.packet.MUCUser; +import org.jivesoftware.smackx.search.ReportedData; +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.smackx.xdata.Form; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 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 static Logger log = Logger.getLogger(AgentSession.class.getName()); + + private Connection connection; + + private String workgroupJID; + + private boolean online = false; + private Presence.Mode presenceMode; + private int maxChats; + private final Map> metaData; + + private Map queues; + + private final List offerListeners; + private final List invitationListeners; + private final List 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 true 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>(); + + this.queues = new HashMap(); + + offerListeners = new ArrayList(); + invitationListeners = new ArrayList(); + queueUsersListeners = new ArrayList(); + + // 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) { + log.log(Level.SEVERE, "Error processing packet", e); + } + } + }; + 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 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 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 null if the meta-data + * doesn't exist.. + */ + public List 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:

      + *

      + *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats + * (equivalent to Presence.Mode.CHAT). + *
    • 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. + *
    • Presence.Mode.AWAY -- the agent is not available and should not + * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    + *

    + * 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:

      + *

      + *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats + * (equivalent to Presence.Mode.CHAT). + *
    • 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. + *
    • Presence.Mode.AWAY -- the agent is not available and should not + * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    + *

    + * 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:

      + *

      + *

    • Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats + * (equivalent to Presence.Mode.CHAT). + *
    • 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. + *
    • Presence.Mode.AWAY -- the agent is not available and should not + * have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).
    + * + * @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 + *

    + * 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 getQueues() { + return Collections.unmodifiableMap((new HashMap(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> 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 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> 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 ChatNote 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. + * @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> 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.

    + * + * 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.

    + * + * The agent or user that accepted the offer MUST 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.

    + * + * 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.

    + * + * Once the invitee joins the support room the workgroup service will kick the inviter from the room.

    + * + * 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()); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java index 410e9cbf0..d88c0feb7 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java @@ -1,60 +1,60 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java index 80e077f6a..422b79ab6 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java @@ -1,220 +1,220 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    - * - * @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> 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 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> 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> 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 ""; - } - } - - /** - * 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 ""; - } - } - -} \ No newline at end of file +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    + * + * @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> 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 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> 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> 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 ""; + } + } + + /** + * 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 ""; + } + } + +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java index cfc7b94a0..54d379731 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java @@ -1,112 +1,112 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - buf.append(""); - 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 ""; - } - } - - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + buf.append(""); + 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 ""; + } + } + + +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java index d8bef5626..b5a7a8a08 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java @@ -1,30 +1,30 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 Offer. 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); -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 Offer. 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferContent.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferContent.java index 4c9b15365..7531d66ab 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferContent.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferContent.java @@ -1,53 +1,53 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(); -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java index 48b9c16ce..20c36dce3 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java @@ -1,47 +1,47 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    - * - * @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); - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    + * + * @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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java index 4699fff7f..2bac37e95 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java @@ -1,58 +1,58 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 users); -} \ No newline at end of file +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 users); +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java index 69b4f9b60..b10a01f8a 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java @@ -1,96 +1,96 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    - * - * @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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    + * + * @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; + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java index 66b2b995c..1a3169c4a 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java @@ -1,98 +1,98 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java index 13f6b01f5..89bced4ae 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java @@ -1,109 +1,109 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.search.ReportedData; -import org.jivesoftware.smackx.workgroup.packet.TranscriptSearch; -import org.jivesoftware.smackx.xdata.Form; -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; - -/** - * 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); - } -} - - +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.search.ReportedData; +import org.jivesoftware.smackx.workgroup.packet.TranscriptSearch; +import org.jivesoftware.smackx.xdata.Form; +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; + +/** + * 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java index 5b9c68499..0f491d04c 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java @@ -1,60 +1,60 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/UserRequest.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/UserRequest.java index f7cf4fb95..d5a098577 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/UserRequest.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/UserRequest.java @@ -1,45 +1,45 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java index b89848f59..e3ef7d6e2 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java @@ -1,222 +1,222 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 getUsers() { - if (users == null) { - return new HashSet().iterator(); - } - return Collections.unmodifiableSet(users).iterator(); - } - - void setUsers(Set 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 null. - * - * @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: - * - *

      - *
    • WorkgroupQueue.Status.OPEN -- the queue is active and accepting new chat requests. - *
    • WorkgroupQueue.Status.ACTIVE -- the queue is active but NOT accepting new chat - * requests. - *
    • WorkgroupQueue.Status.CLOSED -- the queue is NOT active and NOT accepting new - * chat requests. - *
    - */ - 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 getUsers() { + if (users == null) { + return new HashSet().iterator(); + } + return Collections.unmodifiableSet(users).iterator(); + } + + void setUsers(Set 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 null. + * + * @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: + * + *
      + *
    • WorkgroupQueue.Status.OPEN -- the queue is active and accepting new chat requests. + *
    • WorkgroupQueue.Status.ACTIVE -- the queue is active but NOT accepting new chat + * requests. + *
    • WorkgroupQueue.Status.CLOSED -- the queue is NOT active and NOT accepting new + * chat requests. + *
    + */ + 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; + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java index d5bda184a..8d3cbde57 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java @@ -1,80 +1,80 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(" "); - - 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; - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(" "); + + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java index fe968b793..83767bdea 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java @@ -1,153 +1,153 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 agentChatSessions = new ArrayList(); - - 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 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("> "); - 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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 agentChatSessions = new ArrayList(); + + 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 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("> "); + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java index 18581fd70..752ccf806 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java @@ -1,91 +1,91 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java index 864d03bb0..10f0e4b04 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java @@ -1,114 +1,114 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> map = new HashMap>(); - - public void setMetadata(Map> metadata){ - this.map = metadata; - } - - public Map> getMetadata(){ - return map; - } - - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - - buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">"); - buf.append("").append(getSessionID()).append(""); - buf.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> map = MetaDataUtils.parseMetaData(parser); - chatM.setMetadata(map); - } - } - else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals(ELEMENT_NAME)) { - done = true; - } - } - } - - return chatM; - } - } -} - - - - +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> map = new HashMap>(); + + public void setMetadata(Map> metadata){ + this.map = metadata; + } + + public Map> getMetadata(){ + return map; + } + + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + + buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">"); + buf.append("").append(getSessionID()).append(""); + buf.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> 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java index 5898731c0..95ffa9186 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java @@ -1,66 +1,66 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java index 15d3d3de6..f46e45003 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java @@ -1,141 +1,141 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 macros; - private List macroGroups; - - - // Define MacroGroup - private String title; - - public MacroGroup() { - macros = new ArrayList(); - macroGroups = new ArrayList(); - } - - public void addMacro(Macro macro) { - macros.add(macro); - } - - public void removeMacro(Macro macro) { - macros.remove(macro); - } - - public Macro getMacroByTitle(String title) { - Collection col = Collections.unmodifiableList(macros); - Iterator 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 col = Collections.unmodifiableList(macroGroups); - Iterator 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 getMacros() { - return macros; - } - - public void setMacros(List macros) { - this.macros = macros; - } - - public List getMacroGroups() { - return macroGroups; - } - - public void setMacroGroups(List 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(""); - buf.append("" + getTitle() + ""); - buf.append(""); - for (Macro macro : getMacros()) - { - buf.append(""); - buf.append("" + macro.getTitle() + ""); - buf.append("" + macro.getType() + ""); - buf.append("" + macro.getDescription() + ""); - buf.append("" + macro.getResponse() + ""); - buf.append(""); - } - buf.append(""); - - if (getMacroGroups().size() > 0) { - buf.append(""); - for (MacroGroup groups : getMacroGroups()) { - buf.append(groups.toXML()); - } - buf.append(""); - } - buf.append(""); - return buf.toString(); - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 macros; + private List macroGroups; + + + // Define MacroGroup + private String title; + + public MacroGroup() { + macros = new ArrayList(); + macroGroups = new ArrayList(); + } + + public void addMacro(Macro macro) { + macros.add(macro); + } + + public void removeMacro(Macro macro) { + macros.remove(macro); + } + + public Macro getMacroByTitle(String title) { + Collection col = Collections.unmodifiableList(macros); + Iterator 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 col = Collections.unmodifiableList(macroGroups); + Iterator 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 getMacros() { + return macros; + } + + public void setMacros(List macros) { + this.macros = macros; + } + + public List getMacroGroups() { + return macroGroups; + } + + public void setMacroGroups(List 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(""); + buf.append("" + getTitle() + ""); + buf.append(""); + for (Macro macro : getMacros()) + { + buf.append(""); + buf.append("" + macro.getTitle() + ""); + buf.append("" + macro.getType() + ""); + buf.append("" + macro.getDescription() + ""); + buf.append("" + macro.getResponse() + ""); + buf.append(""); + } + buf.append(""); + + if (getMacroGroups().size() > 0) { + buf.append(""); + for (MacroGroup groups : getMacroGroups()) { + buf.append(groups.toXML()); + } + buf.append(""); + } + buf.append(""); + return buf.toString(); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java index 19af23446..a3171b8bf 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java @@ -1,196 +1,196 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("true"); - } - if (getPersonalMacroGroup() != null) { - buf.append(""); - buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML())); - buf.append(""); - } - buf.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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("true"); + } + if (getPersonalMacroGroup() != null) { + buf.append(""); + buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML())); + buf.append(""); + } + buf.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; + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java index 92f16dbeb..e1063cd74 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java @@ -1,153 +1,153 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(getSessionID()).append(""); - - if (getNotes() != null) { - buf.append("").append(getNotes()).append(""); - } - buf.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; - } -} - - - +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(getSessionID()).append(""); + + if (getNotes() != null) { + buf.append("").append(getNotes()).append(""); + } + buf.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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java index bb068214a..4c4f36c5b 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java @@ -1,130 +1,130 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(getJid()).append(""); - } - if (name != null) { - buf.append("").append(getName()).append(""); - } - buf.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; - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(getJid()).append(""); + } + if (name != null) { + buf.append("").append(getName()).append(""); + } + buf.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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java index f9b3e5357..d1af09623 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java @@ -1,264 +1,264 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 currentChats = new ArrayList(); - 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 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("").append(maxChats).append(""); - } - if (!currentChats.isEmpty()) { - buf.append(""); - for (Iterator it = currentChats.iterator(); it.hasNext();) { - buf.append(((ChatInfo)it.next()).toXML()); - } - buf.append(""); - } - buf.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(""); - - 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 currentChats = new ArrayList(); + 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 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("").append(maxChats).append(""); + } + if (!currentChats.isEmpty()) { + buf.append(""); + for (Iterator it = currentChats.iterator(); it.hasNext();) { + buf.append(((ChatInfo)it.next()).toXML()); + } + buf.append(""); + } + buf.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(""); + + 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); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java index ca35f19f0..0da55cbfb 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java @@ -1,161 +1,161 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 agents; - - public AgentStatusRequest() { - agents = new HashSet(); - } - - public int getAgentCount() { - return agents.size(); - } - - public Set 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 i=agents.iterator(); i.hasNext(); ) { - Item item = (Item) i.next(); - buf.append(""); - if (item.getName() != null) { - buf.append(""); - buf.append(item.getName()); - buf.append(""); - } - buf.append(""); - } - } - buf.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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 agents; + + public AgentStatusRequest() { + agents = new HashSet(); + } + + public int getAgentCount() { + return agents.size(); + } + + public Set 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 i=agents.iterator(); i.hasNext(); ) { + Item item = (Item) i.next(); + buf.append(""); + if (item.getName() != null) { + buf.append(""); + buf.append(item.getName()); + buf.append(""); + } + buf.append(""); + } + } + buf.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); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java index 9836ee9df..ff99fbb2b 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java @@ -1,127 +1,127 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); - } - - /** - * 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 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 getWorkgroups() { - return Collections.unmodifiableList(workgroups); - } - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - - buf.append(""); - - for (Iterator it=workgroups.iterator(); it.hasNext();) { - String workgroupJID = it.next(); - buf.append(""); - } - - buf.append(""); - - 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 workgroups = new ArrayList(); - - 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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); + } + + /** + * 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 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 getWorkgroups() { + return Collections.unmodifiableList(workgroups); + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + + buf.append(""); + + for (Iterator it=workgroups.iterator(); it.hasNext();) { + String workgroupJID = it.next(); + buf.append(""); + } + + buf.append(""); + + 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 workgroups = new ArrayList(); + + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java index 4ec5d4486..e22c1a462 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java @@ -1,73 +1,73 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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:
      - *
    • The user wants to leave the queue. In this case, an instance of this class - * should be created without passing in a user address. - *
    • 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.
    - * - * @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("").append(this.user).append(""); - } - else { - buf.append("/>"); - } - - return buf.toString(); - } -} \ No newline at end of file +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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:
      + *
    • The user wants to leave the queue. In this case, an instance of this class + * should be created without passing in a user address. + *
    • 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.
    + * + * @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("").append(this.user).append(""); + } + else { + buf.append("/>"); + } + + return buf.toString(); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java index a0c651533..2cb58b697 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java @@ -1,47 +1,47 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData = MetaDataUtils.parseMetaData(parser); - - return new MetaData(metaData); - } -} \ No newline at end of file +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData = MetaDataUtils.parseMetaData(parser); + + return new MetaData(metaData); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java index c96a322af..e75475eeb 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java @@ -1,171 +1,171 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 occupants; - - public OccupantsInfo(String roomID) { - this.roomID = roomID; - this.occupants = new HashSet(); - } - - public String getRoomID() { - return roomID; - } - - public int getOccupantsCount() { - return occupants.size(); - } - - public Set 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(""); - // Add the occupant jid - buf.append(""); - buf.append(occupant.getJID()); - buf.append(""); - // Add the occupant nickname - buf.append(""); - buf.append(occupant.getNickname()); - buf.append(""); - // Add the date when the occupant joined the room - buf.append(""); - buf.append(UTC_FORMAT.format(occupant.getJoined())); - buf.append(""); - buf.append(""); - } - } - buf.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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 occupants; + + public OccupantsInfo(String roomID) { + this.roomID = roomID; + this.occupants = new HashSet(); + } + + public String getRoomID() { + return roomID; + } + + public int getOccupantsCount() { + return occupants.size(); + } + + public Set 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(""); + // Add the occupant jid + buf.append(""); + buf.append(occupant.getJID()); + buf.append(""); + // Add the occupant nickname + buf.append(""); + buf.append(occupant.getNickname()); + buf.append(""); + // Add the date when the occupant joined the room + buf.append(""); + buf.append(UTC_FORMAT.format(occupant.getJoined())); + buf.append(""); + buf.append(""); + } + } + buf.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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java index 6607370d3..223a007f7 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java @@ -1,209 +1,209 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData = new HashMap>(); - - 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> metaData; - private String sessionID; - private OfferContent content; - - public OfferRequestPacket(String userJID, String userID, int timeout, Map> 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, null 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> getMetaData() { - return this.metaData; - } - - public String getChildElementXML () { - StringBuilder buf = new StringBuilder(); - - buf.append(""); - buf.append("").append(timeout).append(""); - - 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(""); - - return buf.toString(); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> metaData = new HashMap>(); + + 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> metaData; + private String sessionID; + private OfferContent content; + + public OfferRequestPacket(String userJID, String userID, int timeout, Map> 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, null 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> getMetaData() { + return this.metaData; + } + + public String getChildElementXML () { + StringBuilder buf = new StringBuilder(); + + buf.append(""); + buf.append("").append(timeout).append(""); + + 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(""); + + return buf.toString(); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java index 2097303ab..21672c6ac 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java @@ -1,110 +1,110 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    - * - * @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(""); - if (reason != null) { - buf.append("").append(reason).append(""); - } - if (sessionID != null) { - buf.append(new SessionID(sessionID).toXML()); - } - if (userID != null) { - buf.append(new UserID(userID).toXML()); - } - buf.append(""); - return buf.toString(); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.
    + * + * @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(""); + if (reason != null) { + buf.append("").append(reason).append(""); + } + if (sessionID != null) { + buf.append(new SessionID(sessionID).toXML()); + } + if (userID != null) { + buf.append(new UserID(userID).toXML()); + } + buf.append(""); + return buf.toString(); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java index aa3dfc8c6..2df277832 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java @@ -1,199 +1,199 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -import java.util.logging.Logger; - -/** - * Queue details packet extension, which contains details about the users - * currently in a queue. - */ -public class QueueDetails implements PacketExtension { - private static Logger log = Logger.getLogger(QueueDetails.class.getName()); - - /** - * 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 users; - - /** - * Creates a new QueueDetails packet - */ - private QueueDetails() { - users = new HashSet(); - } - - /** - * 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 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 i=users.iterator(); i.hasNext(); ) { - QueueUser user = (QueueUser)i.next(); - int position = user.getQueuePosition(); - int timeRemaining = user.getEstimatedRemainingTime(); - Date timestamp = user.getQueueJoinTimestamp(); - - buf.append(""); - - if (position != -1) { - buf.append("").append(position).append(""); - } - - if (timeRemaining != -1) { - buf.append(""); - } - - if (timestamp != null) { - buf.append(""); - buf.append(dateFormat.format(timestamp)); - buf.append(""); - } - - buf.append(""); - } - } - buf.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()); - log.fine(wait.toString()); - } - - 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; +import java.util.logging.Logger; + +/** + * Queue details packet extension, which contains details about the users + * currently in a queue. + */ +public class QueueDetails implements PacketExtension { + private static Logger log = Logger.getLogger(QueueDetails.class.getName()); + + /** + * 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 users; + + /** + * Creates a new QueueDetails packet + */ + private QueueDetails() { + users = new HashSet(); + } + + /** + * 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 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 i=users.iterator(); i.hasNext(); ) { + QueueUser user = (QueueUser)i.next(); + int position = user.getQueuePosition(); + int timeRemaining = user.getEstimatedRemainingTime(); + Date timestamp = user.getQueueJoinTimestamp(); + + buf.append(""); + + if (position != -1) { + buf.append("").append(position).append(""); + } + + if (timeRemaining != -1) { + buf.append(""); + } + + if (timestamp != null) { + buf.append(""); + buf.append(dateFormat.format(timestamp)); + buf.append(""); + } + + buf.append(""); + } + } + buf.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()); + log.fine(wait.toString()); + } + + eventType = parser.next(); + + if (eventType != XmlPullParser.END_TAG) { + // throw exception + } + } + + queueDetails.addUser(new QueueUser(uid, position, time, joinTime)); + + eventType = parser.next(); + } + } + return queueDetails; + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java index 211305f36..7728f540d 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java @@ -1,158 +1,158 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(userCount).append(""); - } - if (oldestEntry != null) { - buf.append("").append(dateFormat.format(oldestEntry)).append(""); - } - if (averageWaitTime != -1) { - buf.append(""); - } - if (status != null) { - buf.append("").append(status).append(""); - } - buf.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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("").append(userCount).append(""); + } + if (oldestEntry != null) { + buf.append("").append(dateFormat.format(oldestEntry)).append(""); + } + if (averageWaitTime != -1) { + buf.append(""); + } + if (status != null) { + buf.append("").append(status).append(""); + } + buf.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; + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java index 18705033a..a8f8004cc 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java @@ -1,120 +1,120 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - if (position != -1) { - buf.append("").append(position).append(""); - } - if (remainingTime != -1) { - buf.append(""); - } - buf.append(""); - 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + if (position != -1) { + buf.append("").append(position).append(""); + } + if (remainingTime != -1) { + buf.append(""); + } + buf.append(""); + 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); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java index ffe6d7b78..b0717d321 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java @@ -1,175 +1,175 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - if (invitee != null) { - buf.append("").append(invitee).append(""); - } - if (inviter != null) { - buf.append("").append(inviter).append(""); - } - if (reason != null) { - buf.append("").append(reason).append(""); - } - // Add packet extensions, if any are defined. - buf.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; - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + if (invitee != null) { + buf.append("").append(invitee).append(""); + } + if (inviter != null) { + buf.append("").append(inviter).append(""); + } + if (reason != null) { + buf.append("").append(reason).append(""); + } + // Add packet extensions, if any are defined. + buf.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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java index cf6163a31..5bb643c05 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java @@ -1,175 +1,175 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); - if (invitee != null) { - buf.append("").append(invitee).append(""); - } - if (inviter != null) { - buf.append("").append(inviter).append(""); - } - if (reason != null) { - buf.append("").append(reason).append(""); - } - // Add packet extensions, if any are defined. - buf.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; - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(""); + if (invitee != null) { + buf.append("").append(invitee).append(""); + } + if (inviter != null) { + buf.append("").append(inviter).append(""); + } + if (reason != null) { + buf.append("").append(reason).append(""); + } + // Add packet extensions, if any are defined. + buf.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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/SessionID.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/SessionID.java index 783a8d6ed..a4be3c48c 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/SessionID.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/SessionID.java @@ -1,75 +1,75 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcript.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcript.java index fff0a3ad8..745afef66 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcript.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcript.java @@ -1,96 +1,96 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); - } - - /** - * 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 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 getPackets() { - return Collections.unmodifiableList(packets); - } - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - - buf.append(""); - - for (Iterator it=packets.iterator(); it.hasNext();) { - Packet packet = it.next(); - buf.append(packet.toXML()); - } - - buf.append(""); - - return buf.toString(); - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); + } + + /** + * 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 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 getPackets() { + return Collections.unmodifiableList(packets); + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + + buf.append(""); + + for (Iterator it=packets.iterator(); it.hasNext();) { + Packet packet = it.next(); + buf.append(packet.toXML()); + } + + buf.append(""); + + return buf.toString(); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java index fbefd7f3a..b328e2bbf 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java @@ -1,64 +1,64 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 packets = new ArrayList(); - - 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); - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 packets = new ArrayList(); + + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java index eec777932..15022f2ee 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java @@ -1,85 +1,85 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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(" "); - - 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; - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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(" "); + + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcripts.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcripts.java index 3cadb86c2..9160686db 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcripts.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/Transcripts.java @@ -1,245 +1,245 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); - } - - /** - * 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 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 getSummaries() { - return Collections.unmodifiableList(summaries); - } - - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - - buf.append(""); - - for (TranscriptSummary transcriptSummary : summaries) { - buf.append(transcriptSummary.toXML()); - } - - buf.append(""); - - 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 agentDetails; - - public TranscriptSummary(String sessionID, Date joinTime, Date leftTime, List 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 getAgentDetails() { - return agentDetails; - } - - public String toXML() { - StringBuilder buf = new StringBuilder(); - - buf.append(""); - - if (joinTime != null) { - buf.append("").append(UTC_FORMAT.format(joinTime)).append(""); - } - if (leftTime != null) { - buf.append("").append(UTC_FORMAT.format(leftTime)).append(""); - } - buf.append(""); - for (AgentDetail agentDetail : agentDetails) { - buf.append(agentDetail.toXML()); - } - buf.append(""); - - 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(""); - - if (agentJID != null) { - buf.append("").append(agentJID).append(""); - } - if (joinTime != null) { - buf.append("").append(UTC_FORMAT.format(joinTime)).append(""); - } - if (leftTime != null) { - buf.append("").append(UTC_FORMAT.format(leftTime)).append(""); - } - buf.append(""); - - return buf.toString(); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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(); + } + + /** + * 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 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 getSummaries() { + return Collections.unmodifiableList(summaries); + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + + buf.append(""); + + for (TranscriptSummary transcriptSummary : summaries) { + buf.append(transcriptSummary.toXML()); + } + + buf.append(""); + + 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 agentDetails; + + public TranscriptSummary(String sessionID, Date joinTime, Date leftTime, List 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 getAgentDetails() { + return agentDetails; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + + buf.append(""); + + if (joinTime != null) { + buf.append("").append(UTC_FORMAT.format(joinTime)).append(""); + } + if (leftTime != null) { + buf.append("").append(UTC_FORMAT.format(leftTime)).append(""); + } + buf.append(""); + for (AgentDetail agentDetail : agentDetails) { + buf.append(agentDetail.toXML()); + } + buf.append(""); + + 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(""); + + if (agentJID != null) { + buf.append("").append(agentJID).append(""); + } + if (joinTime != null) { + buf.append("").append(UTC_FORMAT.format(joinTime)).append(""); + } + if (leftTime != null) { + buf.append("").append(UTC_FORMAT.format(leftTime)).append(""); + } + buf.append(""); + + return buf.toString(); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java index f5d9ed67b..1619cc91f 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java @@ -1,146 +1,146 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 summaries = new ArrayList(); - - 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 agents = new ArrayList(); - - 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 parseAgents(XmlPullParser parser) throws IOException, XmlPullParserException { - List agents = new ArrayList(); - 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 summaries = new ArrayList(); + + 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 agents = new ArrayList(); + + 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 parseAgents(XmlPullParser parser) throws IOException, XmlPullParserException { + List agents = new ArrayList(); + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/UserID.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/UserID.java index 511832050..d2e44d7ed 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/UserID.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/UserID.java @@ -1,75 +1,75 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - } - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java index c56af9db7..b490eccfb 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java @@ -1,84 +1,84 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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); + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java index ca21490d0..8b6236cf2 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java @@ -1,54 +1,54 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java index ea4ee1053..df7960d32 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java @@ -1,177 +1,177 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 settings; - private String key; - private int type = -1; - - public ChatSettings() { - settings = new ArrayList(); - } - - 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 getSettings() { - return settings; - } - - public ChatSetting getChatSetting(String key) { - Collection col = getSettings(); - if (col != null) { - Iterator 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("> "); - 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); - } - } -} - +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 settings; + private String key; + private int type = -1; + + public ChatSettings() { + settings = new ArrayList(); + } + + 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 getSettings() { + return settings; + } + + public ChatSetting getChatSetting(String key) { + Collection col = getSettings(); + if (col != null) { + Iterator 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("> "); + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java index 5f0a5c331..2a46d149b 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java @@ -1,153 +1,153 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("> "); - 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; - } - } -} - +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("> "); + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java index 24834fd4d..22a21599b 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java @@ -1,113 +1,113 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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("> "); - 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. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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("> "); + 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/QueueListener.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/QueueListener.java index 097b07df1..3b3877609 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/QueueListener.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/QueueListener.java @@ -1,53 +1,53 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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); - -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java index 81f477b5c..d3c74830e 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java @@ -1,852 +1,852 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.smackx.xdata.Form; -import org.jivesoftware.smackx.xdata.FormField; -import org.jivesoftware.smackx.xdata.packet.DataForm; -import org.jivesoftware.smack.*; -import org.jivesoftware.smack.filter.*; -import org.jivesoftware.smack.packet.*; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.muc.MultiUserChat; -import org.jivesoftware.smackx.muc.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.

    - *

    - * 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 invitationListeners; - private List 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(); - queueListeners = new ArrayList(); - - // 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:

      - *

      - *

    • The user is routed to an agent, which triggers a GroupChat invitation. - *
    • The user asks to leave the queue by calling the {@link #departQueue} method. - *
    • A server error occurs, or an administrator explicitly removes the user - * from the queue. - *
    - *

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

    - *

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

    - *

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

      - *

      - *

    • The user is routed to an agent, which triggers a GroupChat invitation. - *
    • The user asks to leave the queue by calling the {@link #departQueue} method. - *
    • A server error occurs, or an administrator explicitly removes the user - * from the queue. - *
    - *

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

    - *

    - * Some servers may be configured to require certain meta-data in order to - * join the queue.

    - *

    - * 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); - } - - /** - *

    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:

      - *

      - *

    • The user is routed to an agent, which triggers a GroupChat invitation. - *
    • The user asks to leave the queue by calling the {@link #departQueue} method. - *
    • A server error occurs, or an administrator explicitly removes the user - * from the queue. - *
    - *

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

    - *

    - * Some servers may be configured to require certain meta-data in order to - * join the queue.

    - *

    - * 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 null 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(); - } - - /** - *

    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:

      - *

      - *

    • The user is routed to an agent, which triggers a GroupChat invitation. - *
    • The user asks to leave the queue by calling the {@link #departQueue} method. - *
    • A server error occurs, or an administrator explicitly removes the user - * from the queue. - *
    - *

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

    - *

    - * Some servers may be configured to require certain meta-data in order to - * join the queue.

    - *

    - * 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 null 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 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 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.

    - *

    - * 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 i = invitationListeners.iterator(); i.hasNext();) { - WorkgroupInvitationListener listener = i.next(); - listener.invitationReceived(invitation); - } - } - } - - private void fireQueueJoinedEvent() { - synchronized (queueListeners) { - for (Iterator i = queueListeners.iterator(); i.hasNext();) { - QueueListener listener = i.next(); - listener.joinedQueue(); - } - } - } - - private void fireQueueDepartedEvent() { - synchronized (queueListeners) { - for (Iterator i = queueListeners.iterator(); i.hasNext();) { - QueueListener listener = i.next(); - listener.departedQueue(); - } - } - } - - private void fireQueuePositionEvent(int currentPosition) { - synchronized (queueListeners) { - for (Iterator i = queueListeners.iterator(); i.hasNext();) { - QueueListener listener = i.next(); - listener.queuePositionUpdated(currentPosition); - } - } - } - - private void fireQueueTimeEvent(int secondsRemaining) { - synchronized (queueListeners) { - for (Iterator 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> 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(""); - buf.append(""); - // 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(""); - - 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); - } -} \ No newline at end of file +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.smackx.xdata.Form; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.muc.MultiUserChat; +import org.jivesoftware.smackx.muc.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.

    + *

    + * 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 invitationListeners; + private List 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(); + queueListeners = new ArrayList(); + + // 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:

      + *

      + *

    • The user is routed to an agent, which triggers a GroupChat invitation. + *
    • The user asks to leave the queue by calling the {@link #departQueue} method. + *
    • A server error occurs, or an administrator explicitly removes the user + * from the queue. + *
    + *

    + * 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.

    + *

    + * 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.

    + *

    + * 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:

      + *

      + *

    • The user is routed to an agent, which triggers a GroupChat invitation. + *
    • The user asks to leave the queue by calling the {@link #departQueue} method. + *
    • A server error occurs, or an administrator explicitly removes the user + * from the queue. + *
    + *

    + * 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.

    + *

    + * Some servers may be configured to require certain meta-data in order to + * join the queue.

    + *

    + * 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); + } + + /** + *

    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:

      + *

      + *

    • The user is routed to an agent, which triggers a GroupChat invitation. + *
    • The user asks to leave the queue by calling the {@link #departQueue} method. + *
    • A server error occurs, or an administrator explicitly removes the user + * from the queue. + *
    + *

    + * 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.

    + *

    + * Some servers may be configured to require certain meta-data in order to + * join the queue.

    + *

    + * 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 null 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(); + } + + /** + *

    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:

      + *

      + *

    • The user is routed to an agent, which triggers a GroupChat invitation. + *
    • The user asks to leave the queue by calling the {@link #departQueue} method. + *
    • A server error occurs, or an administrator explicitly removes the user + * from the queue. + *
    + *

    + * 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.

    + *

    + * Some servers may be configured to require certain meta-data in order to + * join the queue.

    + *

    + * 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 null 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 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 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.

    + *

    + * 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 i = invitationListeners.iterator(); i.hasNext();) { + WorkgroupInvitationListener listener = i.next(); + listener.invitationReceived(invitation); + } + } + } + + private void fireQueueJoinedEvent() { + synchronized (queueListeners) { + for (Iterator i = queueListeners.iterator(); i.hasNext();) { + QueueListener listener = i.next(); + listener.joinedQueue(); + } + } + } + + private void fireQueueDepartedEvent() { + synchronized (queueListeners) { + for (Iterator i = queueListeners.iterator(); i.hasNext();) { + QueueListener listener = i.next(); + listener.departedQueue(); + } + } + } + + private void fireQueuePositionEvent(int currentPosition) { + synchronized (queueListeners) { + for (Iterator i = queueListeners.iterator(); i.hasNext();) { + QueueListener listener = i.next(); + listener.queuePositionUpdated(currentPosition); + } + } + } + + private void fireQueueTimeEvent(int secondsRemaining) { + synchronized (queueListeners) { + for (Iterator 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> 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(""); + buf.append(""); + // 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(""); + + 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); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java index 87ba0c246..ff71bf124 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java @@ -1,132 +1,132 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * 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.

    - * - * 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 { - private static Logger log = Logger.getLogger(ListenerEventDispatcher.class.getName()); - - protected transient ArrayList triplets; - - protected transient boolean hasFinishedDispatching; - protected transient boolean isRunning; - - public ListenerEventDispatcher () { - super(); - - this.triplets = new ArrayList(); - - 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.
    - * - * 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.
    - * - * 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.
    - * - * @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 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) { - log.log(Level.SEVERE, "Exception dispatching an event", e); - } - } - - 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 +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 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.

    + * + * 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 { + private static Logger log = Logger.getLogger(ListenerEventDispatcher.class.getName()); + + protected transient ArrayList triplets; + + protected transient boolean hasFinishedDispatching; + protected transient boolean isRunning; + + public ListenerEventDispatcher () { + super(); + + this.triplets = new ArrayList(); + + 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.
    + * + * 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.
    + * + * 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.
    + * + * @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 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) { + log.log(Level.SEVERE, "Exception dispatching an event", e); + } + } + + 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; + } + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java index 4c09421be..09fd44f50 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java @@ -1,103 +1,103 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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> 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> metaData = new Hashtable>(); - - 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 values = metaData.get(name); - values.add(value); - } - else { - List values = new ArrayList(); - 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<String,List<String>> - * @return the meta-data values in XML form. - */ - public static String serializeMetaData(Map> metaData) { - StringBuilder buf = new StringBuilder(); - if (metaData != null && metaData.size() > 0) { - buf.append(""); - for (Iterator i = metaData.keySet().iterator(); i.hasNext();) { - String key = i.next(); - List value = metaData.get(key); - for (Iterator it = value.iterator(); it.hasNext();) { - String v = it.next(); - buf.append(""); - buf.append(StringUtils.escapeForXML(v)); - buf.append(""); - } - } - buf.append(""); - } - return buf.toString(); - } -} +/** + * + * Copyright 2003-2007 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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> 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> metaData = new Hashtable>(); + + 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 values = metaData.get(name); + values.add(value); + } + else { + List values = new ArrayList(); + 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<String,List<String>> + * @return the meta-data values in XML form. + */ + public static String serializeMetaData(Map> metaData) { + StringBuilder buf = new StringBuilder(); + if (metaData != null && metaData.size() > 0) { + buf.append(""); + for (Iterator i = metaData.keySet().iterator(); i.hasNext();) { + String key = i.next(); + List value = metaData.get(key); + for (Iterator it = value.iterator(); it.hasNext();) { + String v = it.next(); + buf.append(""); + buf.append(StringUtils.escapeForXML(v)); + buf.append(""); + } + } + buf.append(""); + } + return buf.toString(); + } +} diff --git a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ModelUtil.java b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ModelUtil.java index b8a7ecc84..59378c660 100644 --- a/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ModelUtil.java +++ b/legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/ModelUtil.java @@ -1,321 +1,321 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 null The result of - * this method is determined as follows: - *

      - *
    1. If o1 and o2 are the same object - * according to the == operator, return - * true. - *
    2. Otherwise, if either o1 or o2 is - * null, return false. - *
    3. Otherwise, return o1.equals(o2). - *
    - *

    - * This method produces the exact logically inverted result as the - * {@link #areDifferent(Object, Object)} method.

    - *

    - * For array types, one of the equals 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 equals 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 null The result of - * this method is determined as follows: - *

      - *
    1. If b1 and b2 are both TRUE or - * neither b1 nor b2 is TRUE, - * return true. - *
    2. Otherwise, return false. - *
    - *

    - */ - 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 null. The result - * returned by this method is determined as follows: - *

      - *
    1. If o1 and o2 are the same object - * according to the == operator, return - * false. - *
    2. Otherwise, if either o1 or o2 is - * null, return true. - *
    3. Otherwise, return !o1.equals(o2). - *
    - *

    - * This method produces the exact logically inverted result as the - * {@link #areEqual(Object, Object)} method.

    - *

    - * For array types, one of the equals 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 equals 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 null The result of - * this method is determined as follows: - *

      - *
    1. If b1 and b2 are both TRUE or - * neither b1 nor b2 is TRUE, - * return false. - *
    2. Otherwise, return true. - *
    - *

    - * This method produces the exact logically inverted result as the - * {@link #areBooleansEqual(Boolean, Boolean)} method.

    - */ - public static final boolean areBooleansDifferent(Boolean b1, Boolean b2) { - return !areBooleansEqual(b1, b2); - } - - - /** - * Returns true if the specified array is not null - * and contains a non-null element. Returns false - * 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 true if the specified {@link String} is not - * null 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 null if the specified string is empty or - * null. Otherwise the string itself is returned. - */ - public static final String nullifyIfEmpty(String s) { - return ModelUtil.hasLength(s) ? s : null; - } - - /** - * Returns null if the specified object is null - * or if its toString() representation is empty. - * Otherwise, the toString() 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 List iteratorAsList(Iterator i) { - ArrayList list = new ArrayList(10); - while (i.hasNext()) { - list.add(i.next()); - } - return list; - } - - /** - * Creates an Iterator that is the reverse of a ListIterator. - */ - public static Iterator reverseListIterator(ListIterator i) { - return new ReverseListIterator(i); - } -} - -/** - * An Iterator that is the reverse of a ListIterator. - */ -class ReverseListIterator implements Iterator { - private ListIterator _i; - - ReverseListIterator(ListIterator 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. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 null The result of + * this method is determined as follows: + *

      + *
    1. If o1 and o2 are the same object + * according to the == operator, return + * true. + *
    2. Otherwise, if either o1 or o2 is + * null, return false. + *
    3. Otherwise, return o1.equals(o2). + *
    + *

    + * This method produces the exact logically inverted result as the + * {@link #areDifferent(Object, Object)} method.

    + *

    + * For array types, one of the equals 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 equals 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 null The result of + * this method is determined as follows: + *

      + *
    1. If b1 and b2 are both TRUE or + * neither b1 nor b2 is TRUE, + * return true. + *
    2. Otherwise, return false. + *
    + *

    + */ + 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 null. The result + * returned by this method is determined as follows: + *

      + *
    1. If o1 and o2 are the same object + * according to the == operator, return + * false. + *
    2. Otherwise, if either o1 or o2 is + * null, return true. + *
    3. Otherwise, return !o1.equals(o2). + *
    + *

    + * This method produces the exact logically inverted result as the + * {@link #areEqual(Object, Object)} method.

    + *

    + * For array types, one of the equals 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 equals 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 null The result of + * this method is determined as follows: + *

      + *
    1. If b1 and b2 are both TRUE or + * neither b1 nor b2 is TRUE, + * return false. + *
    2. Otherwise, return true. + *
    + *

    + * This method produces the exact logically inverted result as the + * {@link #areBooleansEqual(Boolean, Boolean)} method.

    + */ + public static final boolean areBooleansDifferent(Boolean b1, Boolean b2) { + return !areBooleansEqual(b1, b2); + } + + + /** + * Returns true if the specified array is not null + * and contains a non-null element. Returns false + * 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 true if the specified {@link String} is not + * null 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 null if the specified string is empty or + * null. Otherwise the string itself is returned. + */ + public static final String nullifyIfEmpty(String s) { + return ModelUtil.hasLength(s) ? s : null; + } + + /** + * Returns null if the specified object is null + * or if its toString() representation is empty. + * Otherwise, the toString() 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 List iteratorAsList(Iterator i) { + ArrayList list = new ArrayList(10); + while (i.hasNext()) { + list.add(i.next()); + } + return list; + } + + /** + * Creates an Iterator that is the reverse of a ListIterator. + */ + public static Iterator reverseListIterator(ListIterator i) { + return new ReverseListIterator(i); + } +} + +/** + * An Iterator that is the reverse of a ListIterator. + */ +class ReverseListIterator implements Iterator { + private ListIterator _i; + + ReverseListIterator(ListIterator 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(); + } + +} + + + + + + + + + + + diff --git a/legacy/src/main/resources/org.jivesoftware.smackx/workgroup.providers b/legacy/src/main/resources/org.jivesoftware.smackx/workgroup.providers index cc7085085..d86cc2484 100644 --- a/legacy/src/main/resources/org.jivesoftware.smackx/workgroup.providers +++ b/legacy/src/main/resources/org.jivesoftware.smackx/workgroup.providers @@ -1,214 +1,214 @@ - - - - - - offer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider - - - - offer-revoke - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider - - - - agent-status-request - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider - - - - transcripts - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider - - - - transcript - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptProvider - - - - workgroups - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider - - - - agent-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider - - - - transcript-search - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider - - - - occupants-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider - - - - chat-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider - - - - chat-notes - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider - - - - chat-sessions - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider - - - - offline-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider - - - - sound-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider - - - - workgroup-properties - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider - - - - - search-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider - - - - workgroup-form - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider - - - - macros - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider - - - - chat-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider - - - - generic-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider - - - - monitor - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider - - - - - queue-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider - - - - workgroup - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider - - - - metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MetaDataProvider - - - - session - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.SessionID$Provider - - - - user - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.UserID$Provider - - - - agent-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider - - - - notify-queue-details - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider - - - - notify-queue - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider - - - - invite - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider - - - - transfer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider - - - + + + + + + offer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider + + + + offer-revoke + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider + + + + agent-status-request + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider + + + + transcripts + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider + + + + transcript + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptProvider + + + + workgroups + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider + + + + agent-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider + + + + transcript-search + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider + + + + occupants-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider + + + + chat-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider + + + + chat-notes + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider + + + + chat-sessions + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider + + + + offline-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider + + + + sound-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider + + + + workgroup-properties + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider + + + + + search-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider + + + + workgroup-form + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider + + + + macros + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider + + + + chat-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider + + + + generic-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider + + + + monitor + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider + + + + + queue-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider + + + + workgroup + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider + + + + metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MetaDataProvider + + + + session + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.SessionID$Provider + + + + user + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.UserID$Provider + + + + agent-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider + + + + notify-queue-details + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider + + + + notify-queue + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider + + + + invite + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider + + + + transfer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider + + + diff --git a/resources/sample.providers b/resources/sample.providers index b13ddbdaa..21474ce4e 100644 --- a/resources/sample.providers +++ b/resources/sample.providers @@ -1,17 +1,17 @@ - - - - - - element - ns - com.myco.MyIQProvider - - - - elem - http://jabber.org/protocol/whoknows - com.myco.MyExtProvider - - - + + + + + + element + ns + com.myco.MyIQProvider + + + + elem + http://jabber.org/protocol/whoknows + com.myco.MyExtProvider + + +