2014-02-19 20:06:30 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Copyright 2009 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.
|
|
|
|
*/
|
|
|
|
|
2014-05-15 15:04:46 +02:00
|
|
|
package org.jivesoftware.smack.bosh;
|
2014-02-19 20:06:30 +01:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.PipedReader;
|
|
|
|
import java.io.PipedWriter;
|
2014-12-28 17:43:39 +01:00
|
|
|
import java.io.StringReader;
|
2014-02-19 20:06:30 +01:00
|
|
|
import java.io.Writer;
|
2014-03-17 19:33:17 +01:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
2014-02-19 20:06:30 +01:00
|
|
|
|
2014-05-25 12:28:08 +02:00
|
|
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
2014-05-15 15:04:46 +02:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2014-03-12 11:50:05 +01:00
|
|
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
|
|
|
import org.jivesoftware.smack.SmackException.ConnectionException;
|
2014-12-28 17:43:39 +01:00
|
|
|
import org.jivesoftware.smack.XMPPException.StreamErrorException;
|
2014-03-10 09:45:50 +01:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2014-02-19 20:06:30 +01:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2014-09-11 09:49:16 +02:00
|
|
|
import org.jivesoftware.smack.packet.Element;
|
2014-12-28 17:43:39 +01:00
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
|
|
|
import org.jivesoftware.smack.packet.Message;
|
2015-02-05 11:17:27 +01:00
|
|
|
import org.jivesoftware.smack.packet.Stanza;
|
2015-07-14 22:41:33 +02:00
|
|
|
import org.jivesoftware.smack.packet.Nonza;
|
2014-02-19 20:06:30 +01:00
|
|
|
import org.jivesoftware.smack.packet.Presence;
|
2014-12-28 17:43:39 +01:00
|
|
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
|
|
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
|
|
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
2015-02-14 17:15:02 +01:00
|
|
|
import org.jxmpp.jid.DomainBareJid;
|
2015-05-04 20:14:45 +02:00
|
|
|
import org.jxmpp.jid.parts.Resourcepart;
|
2014-09-11 09:49:16 +02:00
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
2014-12-28 17:43:39 +01:00
|
|
|
import org.xmlpull.v1.XmlPullParserFactory;
|
|
|
|
import org.igniterealtime.jbosh.AbstractBody;
|
2014-02-19 20:06:30 +01:00
|
|
|
import org.igniterealtime.jbosh.BOSHClient;
|
|
|
|
import org.igniterealtime.jbosh.BOSHClientConfig;
|
|
|
|
import org.igniterealtime.jbosh.BOSHClientConnEvent;
|
|
|
|
import org.igniterealtime.jbosh.BOSHClientConnListener;
|
|
|
|
import org.igniterealtime.jbosh.BOSHClientRequestListener;
|
|
|
|
import org.igniterealtime.jbosh.BOSHClientResponseListener;
|
|
|
|
import org.igniterealtime.jbosh.BOSHException;
|
|
|
|
import org.igniterealtime.jbosh.BOSHMessageEvent;
|
|
|
|
import org.igniterealtime.jbosh.BodyQName;
|
|
|
|
import org.igniterealtime.jbosh.ComposableBody;
|
|
|
|
|
|
|
|
/**
|
2015-02-12 12:13:19 +01:00
|
|
|
* Creates a connection to an XMPP server via HTTP binding.
|
2014-02-19 20:06:30 +01:00
|
|
|
* This is specified in the XEP-0206: XMPP Over BOSH.
|
|
|
|
*
|
2014-03-10 09:45:50 +01:00
|
|
|
* @see XMPPConnection
|
2014-02-19 20:06:30 +01:00
|
|
|
* @author Guenther Niess
|
|
|
|
*/
|
2014-05-25 12:28:08 +02:00
|
|
|
public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
2014-04-09 12:16:44 +02:00
|
|
|
private static final Logger LOGGER = Logger.getLogger(XMPPBOSHConnection.class.getName());
|
2014-02-19 20:06:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The XMPP Over Bosh namespace.
|
|
|
|
*/
|
|
|
|
public static final String XMPP_BOSH_NS = "urn:xmpp:xbosh";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The BOSH namespace from XEP-0124.
|
|
|
|
*/
|
|
|
|
public static final String BOSH_URI = "http://jabber.org/protocol/httpbind";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The used BOSH client from the jbosh library.
|
|
|
|
*/
|
|
|
|
private BOSHClient client;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Holds the initial configuration used while creating the connection.
|
|
|
|
*/
|
|
|
|
private final BOSHConfiguration config;
|
|
|
|
|
|
|
|
// Some flags which provides some info about the current state.
|
|
|
|
private boolean isFirstInitialization = true;
|
|
|
|
private boolean done = false;
|
|
|
|
|
|
|
|
// The readerPipe and consumer thread are used for the debugger.
|
|
|
|
private PipedWriter readerPipe;
|
|
|
|
private Thread readerConsumer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The session ID for the BOSH session with the connection manager.
|
|
|
|
*/
|
|
|
|
protected String sessionID = null;
|
|
|
|
|
|
|
|
/**
|
2015-02-12 12:13:19 +01:00
|
|
|
* Create a HTTP Binding connection to an XMPP server.
|
2014-02-19 20:06:30 +01:00
|
|
|
*
|
2014-11-09 18:30:16 +01:00
|
|
|
* @param username the username to use.
|
|
|
|
* @param password the password to use.
|
2014-02-19 20:06:30 +01:00
|
|
|
* @param https true if you want to use SSL
|
|
|
|
* (e.g. false for http://domain.lt:7070/http-bind).
|
|
|
|
* @param host the hostname or IP address of the connection manager
|
|
|
|
* (e.g. domain.lt for http://domain.lt:7070/http-bind).
|
|
|
|
* @param port the port of the connection manager
|
|
|
|
* (e.g. 7070 for http://domain.lt:7070/http-bind).
|
|
|
|
* @param filePath the file which is described by the URL
|
|
|
|
* (e.g. /http-bind for http://domain.lt:7070/http-bind).
|
2015-05-18 16:48:23 +02:00
|
|
|
* @param xmppServiceDomain the XMPP service name
|
2014-02-19 20:06:30 +01:00
|
|
|
* (e.g. domain.lt for the user alice@domain.lt)
|
|
|
|
*/
|
2015-05-18 16:48:23 +02:00
|
|
|
public XMPPBOSHConnection(String username, String password, boolean https, String host, int port, String filePath, DomainBareJid xmppServiceDomain) {
|
2014-11-09 18:30:16 +01:00
|
|
|
this(BOSHConfiguration.builder().setUseHttps(https).setHost(host)
|
2015-06-08 14:39:05 +02:00
|
|
|
.setPort(port).setFile(filePath).setXmppDomain(xmppServiceDomain)
|
2014-11-09 18:30:16 +01:00
|
|
|
.setUsernameAndPassword(username, password).build());
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-02-12 12:13:19 +01:00
|
|
|
* Create a HTTP Binding connection to an XMPP server.
|
2014-02-19 20:06:30 +01:00
|
|
|
*
|
|
|
|
* @param config The configuration which is used for this connection.
|
|
|
|
*/
|
2014-04-09 12:16:44 +02:00
|
|
|
public XMPPBOSHConnection(BOSHConfiguration config) {
|
2014-02-19 20:06:30 +01:00
|
|
|
super(config);
|
|
|
|
this.config = config;
|
|
|
|
}
|
|
|
|
|
2014-04-27 12:27:12 +02:00
|
|
|
@Override
|
2015-04-12 18:33:43 +02:00
|
|
|
protected void connectInternal() throws SmackException, InterruptedException {
|
2014-02-19 20:06:30 +01:00
|
|
|
done = false;
|
|
|
|
try {
|
|
|
|
// Ensure a clean starting state
|
|
|
|
if (client != null) {
|
|
|
|
client.close();
|
|
|
|
client = null;
|
|
|
|
}
|
|
|
|
sessionID = null;
|
|
|
|
|
|
|
|
// Initialize BOSH client
|
|
|
|
BOSHClientConfig.Builder cfgBuilder = BOSHClientConfig.Builder
|
2015-05-18 16:48:23 +02:00
|
|
|
.create(config.getURI(), config.getXMPPServiceDomain().toString());
|
2014-02-19 20:06:30 +01:00
|
|
|
if (config.isProxyEnabled()) {
|
|
|
|
cfgBuilder.setProxy(config.getProxyAddress(), config.getProxyPort());
|
|
|
|
}
|
|
|
|
client = BOSHClient.create(cfgBuilder.build());
|
|
|
|
|
2014-12-28 17:46:47 +01:00
|
|
|
client.addBOSHClientConnListener(new BOSHConnectionListener());
|
2014-12-28 17:43:39 +01:00
|
|
|
client.addBOSHClientResponseListener(new BOSHPacketReader());
|
2014-02-19 20:06:30 +01:00
|
|
|
|
|
|
|
// Initialize the debugger
|
|
|
|
if (config.isDebuggerEnabled()) {
|
|
|
|
initDebugger();
|
|
|
|
if (isFirstInitialization) {
|
|
|
|
if (debugger.getReaderListener() != null) {
|
2015-03-01 10:28:15 +01:00
|
|
|
addAsyncStanzaListener(debugger.getReaderListener(), null);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
if (debugger.getWriterListener() != null) {
|
|
|
|
addPacketSendingListener(debugger.getWriterListener(), null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the session creation request
|
|
|
|
client.send(ComposableBody.builder()
|
|
|
|
.setNamespaceDefinition("xmpp", XMPP_BOSH_NS)
|
|
|
|
.setAttribute(BodyQName.createWithPrefix(XMPP_BOSH_NS, "version", "xmpp"), "1.0")
|
|
|
|
.build());
|
|
|
|
} catch (Exception e) {
|
2014-03-12 11:50:05 +01:00
|
|
|
throw new ConnectionException(e);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the response from the server
|
|
|
|
synchronized (this) {
|
|
|
|
if (!connected) {
|
|
|
|
try {
|
2014-04-23 09:49:05 +02:00
|
|
|
wait(getPacketReplyTimeout());
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
catch (InterruptedException e) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is no feedback, throw an remote server timeout error
|
|
|
|
if (!connected && !done) {
|
|
|
|
done = true;
|
|
|
|
String errorMessage = "Timeout reached for the connection to "
|
|
|
|
+ getHost() + ":" + getPort() + ".";
|
2014-03-12 11:50:05 +01:00
|
|
|
throw new SmackException(errorMessage);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
2014-09-29 16:41:56 +02:00
|
|
|
|
|
|
|
// Wait with SASL auth until the SASL mechanisms have been received
|
|
|
|
saslFeatureReceived.checkIfSuccessOrWaitOrThrow();
|
|
|
|
|
2014-03-14 01:48:33 +01:00
|
|
|
callConnectionConnectedListener();
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isSecureConnection() {
|
|
|
|
// TODO: Implement SSL usage
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isUsingCompression() {
|
|
|
|
// TODO: Implement compression
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-11-09 18:30:16 +01:00
|
|
|
@Override
|
2015-05-04 20:14:45 +02:00
|
|
|
protected void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
|
2015-05-03 22:00:41 +02:00
|
|
|
SmackException, IOException, InterruptedException {
|
|
|
|
// Authenticate using SASL
|
|
|
|
saslAuthentication.authenticate(username, password);
|
2014-02-19 20:06:30 +01:00
|
|
|
|
2014-09-11 09:49:16 +02:00
|
|
|
bindResourceAndEstablishSession(resource);
|
2014-02-19 20:06:30 +01:00
|
|
|
|
2014-11-09 18:30:16 +01:00
|
|
|
afterSuccessfulLogin(false);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
2014-05-15 15:04:46 +02:00
|
|
|
@Override
|
2015-07-14 22:41:33 +02:00
|
|
|
public void sendNonza(Nonza element) throws NotConnectedException {
|
2014-05-03 10:23:57 +02:00
|
|
|
if (done) {
|
|
|
|
throw new NotConnectedException();
|
|
|
|
}
|
2014-09-11 09:49:16 +02:00
|
|
|
sendElement(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-03-04 21:44:43 +01:00
|
|
|
protected void sendStanzaInternal(Stanza packet) throws NotConnectedException {
|
2014-09-11 09:49:16 +02:00
|
|
|
sendElement(packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendElement(Element element) {
|
2014-05-03 10:23:57 +02:00
|
|
|
try {
|
2014-09-11 09:49:16 +02:00
|
|
|
send(ComposableBody.builder().setPayloadXML(element.toXML().toString()).build());
|
2015-02-05 11:17:27 +01:00
|
|
|
if (element instanceof Stanza) {
|
|
|
|
firePacketSendingListeners((Stanza) element);
|
2014-10-25 11:39:16 +02:00
|
|
|
}
|
2014-05-03 10:23:57 +02:00
|
|
|
}
|
|
|
|
catch (BOSHException e) {
|
2015-03-04 21:44:43 +01:00
|
|
|
LOGGER.log(Level.SEVERE, "BOSHException in sendStanzaInternal", e);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the connection by setting presence to unavailable and closing the
|
|
|
|
* HTTP client. The shutdown logic will be used during a planned disconnection or when
|
|
|
|
* dealing with an unexpected disconnection. Unlike {@link #disconnect()} the connection's
|
2015-03-21 09:36:28 +01:00
|
|
|
* BOSH stanza(/packet) reader will not be removed; thus connection's state is kept.
|
2014-02-19 20:06:30 +01:00
|
|
|
*
|
|
|
|
*/
|
2014-05-04 20:31:25 +02:00
|
|
|
@Override
|
|
|
|
protected void shutdown() {
|
2014-11-09 18:30:16 +01:00
|
|
|
setWasAuthenticated();
|
2014-02-19 20:06:30 +01:00
|
|
|
sessionID = null;
|
|
|
|
done = true;
|
|
|
|
authenticated = false;
|
|
|
|
connected = false;
|
|
|
|
isFirstInitialization = false;
|
|
|
|
|
|
|
|
// Close down the readers and writers.
|
|
|
|
if (readerPipe != null) {
|
|
|
|
try {
|
|
|
|
readerPipe.close();
|
|
|
|
}
|
|
|
|
catch (Throwable ignore) { /* ignore */ }
|
|
|
|
reader = null;
|
|
|
|
}
|
|
|
|
if (reader != null) {
|
|
|
|
try {
|
|
|
|
reader.close();
|
|
|
|
}
|
|
|
|
catch (Throwable ignore) { /* ignore */ }
|
|
|
|
reader = null;
|
|
|
|
}
|
|
|
|
if (writer != null) {
|
|
|
|
try {
|
|
|
|
writer.close();
|
|
|
|
}
|
|
|
|
catch (Throwable ignore) { /* ignore */ }
|
|
|
|
writer = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
readerConsumer = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a HTTP request to the connection manager with the provided body element.
|
|
|
|
*
|
|
|
|
* @param body the body which will be sent.
|
|
|
|
*/
|
|
|
|
protected void send(ComposableBody body) throws BOSHException {
|
|
|
|
if (!connected) {
|
|
|
|
throw new IllegalStateException("Not connected to a server!");
|
|
|
|
}
|
|
|
|
if (body == null) {
|
|
|
|
throw new NullPointerException("Body mustn't be null!");
|
|
|
|
}
|
|
|
|
if (sessionID != null) {
|
|
|
|
body = body.rebuild().setAttribute(
|
|
|
|
BodyQName.create(BOSH_URI, "sid"), sessionID).build();
|
|
|
|
}
|
|
|
|
client.send(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the SmackDebugger which allows to log and debug XML traffic.
|
|
|
|
*/
|
|
|
|
protected void initDebugger() {
|
|
|
|
// TODO: Maybe we want to extend the SmackDebugger for simplification
|
|
|
|
// and a performance boost.
|
|
|
|
|
|
|
|
// Initialize a empty writer which discards all data.
|
|
|
|
writer = new Writer() {
|
|
|
|
public void write(char[] cbuf, int off, int len) { /* ignore */}
|
|
|
|
public void close() { /* ignore */ }
|
|
|
|
public void flush() { /* ignore */ }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize a pipe for received raw data.
|
|
|
|
try {
|
|
|
|
readerPipe = new PipedWriter();
|
|
|
|
reader = new PipedReader(readerPipe);
|
|
|
|
}
|
|
|
|
catch (IOException e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the method from the parent class which initializes the debugger.
|
|
|
|
super.initDebugger();
|
|
|
|
|
|
|
|
// Add listeners for the received and sent raw data.
|
|
|
|
client.addBOSHClientResponseListener(new BOSHClientResponseListener() {
|
|
|
|
public void responseReceived(BOSHMessageEvent event) {
|
|
|
|
if (event.getBody() != null) {
|
|
|
|
try {
|
|
|
|
readerPipe.write(event.getBody().toXML());
|
|
|
|
readerPipe.flush();
|
|
|
|
} catch (Exception e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
client.addBOSHClientRequestListener(new BOSHClientRequestListener() {
|
|
|
|
public void requestSent(BOSHMessageEvent event) {
|
|
|
|
if (event.getBody() != null) {
|
|
|
|
try {
|
|
|
|
writer.write(event.getBody().toXML());
|
|
|
|
} catch (Exception e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create and start a thread which discards all read data.
|
|
|
|
readerConsumer = new Thread() {
|
|
|
|
private Thread thread = this;
|
|
|
|
private int bufferLength = 1024;
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
char[] cbuf = new char[bufferLength];
|
|
|
|
while (readerConsumer == thread && !done) {
|
|
|
|
reader.read(cbuf, 0, bufferLength);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
readerConsumer.setDaemon(true);
|
|
|
|
readerConsumer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends out a notification that there was an error with the connection
|
|
|
|
* and closes the connection.
|
|
|
|
*
|
|
|
|
* @param e the exception that causes the connection close event.
|
|
|
|
*/
|
|
|
|
protected void notifyConnectionError(Exception e) {
|
|
|
|
// Closes the connection temporary. A reconnection is possible
|
2014-05-04 20:31:25 +02:00
|
|
|
shutdown();
|
2014-03-14 01:48:33 +01:00
|
|
|
callConnectionClosedOnErrorListener(e);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A listener class which listen for a successfully established connection
|
|
|
|
* and connection errors and notifies the BOSHConnection.
|
|
|
|
*
|
|
|
|
* @author Guenther Niess
|
|
|
|
*/
|
|
|
|
private class BOSHConnectionListener implements BOSHClientConnListener {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notify the BOSHConnection about connection state changes.
|
|
|
|
* Process the connection listeners and try to login if the
|
|
|
|
* connection was formerly authenticated and is now reconnected.
|
|
|
|
*/
|
|
|
|
public void connectionEvent(BOSHClientConnEvent connEvent) {
|
|
|
|
try {
|
|
|
|
if (connEvent.isConnected()) {
|
|
|
|
connected = true;
|
|
|
|
if (isFirstInitialization) {
|
|
|
|
isFirstInitialization = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (wasAuthenticated) {
|
2015-01-10 01:23:20 +01:00
|
|
|
try {
|
|
|
|
login();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
2015-01-07 20:02:01 +01:00
|
|
|
notifyReconnection();
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (connEvent.isError()) {
|
2014-03-18 11:19:37 +01:00
|
|
|
// TODO Check why jbosh's getCause returns Throwable here. This is very
|
|
|
|
// unusual and should be avoided if possible
|
|
|
|
Throwable cause = connEvent.getCause();
|
|
|
|
Exception e;
|
|
|
|
if (cause instanceof Exception) {
|
|
|
|
e = (Exception) cause;
|
|
|
|
} else {
|
|
|
|
e = new Exception(cause);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
2014-03-18 11:19:37 +01:00
|
|
|
notifyConnectionError(e);
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
connected = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally {
|
2014-12-28 17:46:47 +01:00
|
|
|
synchronized (XMPPBOSHConnection.this) {
|
|
|
|
XMPPBOSHConnection.this.notifyAll();
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-28 17:43:39 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Listens for XML traffic from the BOSH connection manager and parses it into
|
2015-03-21 09:36:28 +01:00
|
|
|
* stanza(/packet) objects.
|
2014-12-28 17:43:39 +01:00
|
|
|
*
|
|
|
|
* @author Guenther Niess
|
|
|
|
*/
|
|
|
|
private class BOSHPacketReader implements BOSHClientResponseListener {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse the received packets and notify the corresponding connection.
|
|
|
|
*
|
|
|
|
* @param event the BOSH client response which includes the received packet.
|
|
|
|
*/
|
|
|
|
public void responseReceived(BOSHMessageEvent event) {
|
|
|
|
AbstractBody body = event.getBody();
|
|
|
|
if (body != null) {
|
|
|
|
try {
|
|
|
|
if (sessionID == null) {
|
|
|
|
sessionID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "sid"));
|
|
|
|
}
|
2015-01-28 17:14:33 +01:00
|
|
|
if (streamId == null) {
|
|
|
|
streamId = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid"));
|
2014-12-28 17:43:39 +01:00
|
|
|
}
|
|
|
|
final XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
|
|
|
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
|
|
|
parser.setInput(new StringReader(body.toXML()));
|
|
|
|
int eventType = parser.getEventType();
|
|
|
|
do {
|
|
|
|
eventType = parser.next();
|
|
|
|
switch (eventType) {
|
|
|
|
case XmlPullParser.START_TAG:
|
|
|
|
String name = parser.getName();
|
|
|
|
switch (name) {
|
|
|
|
case Message.ELEMENT:
|
2015-01-11 21:52:06 +01:00
|
|
|
case IQ.IQ_ELEMENT:
|
2014-12-28 17:43:39 +01:00
|
|
|
case Presence.ELEMENT:
|
|
|
|
parseAndProcessStanza(parser);
|
|
|
|
break;
|
|
|
|
case "challenge":
|
|
|
|
// The server is challenging the SASL authentication
|
|
|
|
// made by the client
|
|
|
|
final String challengeData = parser.nextText();
|
|
|
|
getSASLAuthentication().challengeReceived(challengeData);
|
|
|
|
break;
|
|
|
|
case "success":
|
|
|
|
send(ComposableBody.builder().setNamespaceDefinition("xmpp",
|
|
|
|
XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
|
|
|
|
BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
|
|
|
|
"xmpp"), "true").setAttribute(
|
2015-05-18 16:48:23 +02:00
|
|
|
BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build());
|
2014-12-28 17:43:39 +01:00
|
|
|
Success success = new Success(parser.nextText());
|
|
|
|
getSASLAuthentication().authenticated(success);
|
2015-02-10 16:36:34 +01:00
|
|
|
break;
|
2014-12-28 17:43:39 +01:00
|
|
|
case "features":
|
|
|
|
parseFeatures(parser);
|
|
|
|
break;
|
|
|
|
case "failure":
|
|
|
|
if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) {
|
|
|
|
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
|
|
|
|
getSASLAuthentication().authenticationFailed(failure);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "error":
|
|
|
|
throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (eventType != XmlPullParser.END_DOCUMENT);
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
if (isConnected()) {
|
|
|
|
notifyConnectionError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-19 20:06:30 +01:00
|
|
|
}
|