diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHPacketReader.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHPacketReader.java deleted file mode 100644 index 7a9ef8914..000000000 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHPacketReader.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * - * 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. - */ - -package org.jivesoftware.smack.bosh; - -import java.io.StringReader; - -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; -import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; -import org.jivesoftware.smack.util.PacketParserUtils; -import org.jivesoftware.smack.XMPPException.StreamErrorException; -import org.xmlpull.v1.XmlPullParserFactory; -import org.xmlpull.v1.XmlPullParser; -import org.igniterealtime.jbosh.AbstractBody; -import org.igniterealtime.jbosh.BOSHClientResponseListener; -import org.igniterealtime.jbosh.BOSHMessageEvent; -import org.igniterealtime.jbosh.BodyQName; -import org.igniterealtime.jbosh.ComposableBody; - -/** - * Listens for XML traffic from the BOSH connection manager and parses it into - * packet objects. - * - * @author Guenther Niess - */ -public class BOSHPacketReader implements BOSHClientResponseListener { - - private XMPPBOSHConnection connection; - - /** - * Create a packet reader which listen on a BOSHConnection for received - * HTTP responses, parse the packets and notifies the connection. - * - * @param connection the corresponding connection for the received packets. - */ - public BOSHPacketReader(XMPPBOSHConnection connection) { - this.connection = connection; - } - - /** - * 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 (connection.sessionID == null) { - connection.sessionID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "sid")); - } - if (connection.authID == null) { - connection.authID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid")); - } - 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(); - if (eventType == XmlPullParser.START_TAG) { - Packet packet = PacketParserUtils.parseStanza(parser, connection); - if (packet != null) { - connection.processPacket(packet); - // TODO call connection.reportStanzaReceived here - } else if (parser.getName().equals("challenge")) { - // The server is challenging the SASL authentication - // made by the client - final String challengeData = parser.nextText(); - connection.getSASLAuthentication() - .challengeReceived(challengeData); - } else if (parser.getName().equals("success")) { - connection.send(ComposableBody.builder() - .setNamespaceDefinition("xmpp", XMPPBOSHConnection.XMPP_BOSH_NS) - .setAttribute( - BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart", "xmpp"), - "true") - .setAttribute( - BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), - connection.getServiceName()) - .build()); - Success success = new Success(parser.nextText()); - connection.getSASLAuthentication().authenticated(success); - } else if (parser.getName().equals("features")) { - parseFeatures(parser); - } else if (parser.getName().equals("failure")) { - if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) { - final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser); - connection.getSASLAuthentication().authenticationFailed(failure); - } - } else if (parser.getName().equals("error")) { - throw new StreamErrorException(PacketParserUtils.parseStreamError(parser)); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - } - catch (Exception e) { - if (connection.isConnected()) { - connection.notifyConnectionError(e); - } - } - } - } - - private void parseFeatures(XmlPullParser parser) throws Exception { - connection.parseFeatures0(parser); - } -} diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index d59c46643..dc8d16945 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -20,6 +20,7 @@ package org.jivesoftware.smack.bosh; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; +import java.io.StringReader; import java.io.Writer; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,18 +31,25 @@ import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.ConnectionException; -import org.jivesoftware.smack.SASLAuthentication; +import org.jivesoftware.smack.XMPPException.StreamErrorException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Element; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence.Type; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; +import org.jivesoftware.smack.util.PacketParserUtils; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.igniterealtime.jbosh.AbstractBody; import org.igniterealtime.jbosh.BOSHClient; import org.igniterealtime.jbosh.BOSHClientConfig; import org.igniterealtime.jbosh.BOSHClientConnEvent; @@ -154,7 +162,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { client = BOSHClient.create(cfgBuilder.build()); client.addBOSHClientConnListener(new BOSHConnectionListener(this)); - client.addBOSHClientResponseListener(new BOSHPacketReader(this)); + client.addBOSHClientResponseListener(new BOSHPacketReader()); // Initialize the debugger if (config.isDebuggerEnabled()) { @@ -443,20 +451,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { callConnectionClosedOnErrorListener(e); } - @Override - protected void processPacket(Packet packet) { - super.processPacket(packet); - } - - @Override - protected SASLAuthentication getSASLAuthentication() { - return super.getSASLAuthentication(); - } - - void parseFeatures0(XmlPullParser parser) throws Exception { - parseFeatures(parser); - } - /** * A listener class which listen for a successfully established connection * and connection errors and notifies the BOSHConnection. @@ -525,4 +519,82 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { } } } + + /** + * Listens for XML traffic from the BOSH connection manager and parses it into + * packet objects. + * + * @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")); + } + if (authID == null) { + authID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid")); + } + 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: + case IQ.ELEMENT: + 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( + BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getServiceName()).build()); + Success success = new Success(parser.nextText()); + getSASLAuthentication().authenticated(success); + 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); + } + } + } + } + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index b20042157..08e0a89d5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -67,11 +67,14 @@ import org.jivesoftware.smack.packet.RosterVer; import org.jivesoftware.smack.packet.Session; import org.jivesoftware.smack.packet.StartTls; import org.jivesoftware.smack.packet.PlainStreamElement; +import org.jivesoftware.smack.parsing.ParsingExceptionCallback; +import org.jivesoftware.smack.parsing.UnparsablePacket; import org.jivesoftware.smack.provider.PacketExtensionProvider; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.rosterstore.RosterStore; import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.SmackExecutorThreadFactory; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.dns.HostAddress; @@ -211,6 +214,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { protected XMPPInputOutputStream compressionHandler; + private ParsingExceptionCallback parsingExceptionCallback = SmackConfiguration.getDefaultParsingExceptionCallback(); + /** * ExecutorService used to invoke the PacketListeners on newly arrived and parsed stanzas. It is * important that we use a single threaded ExecutorService in order to guarantee that the @@ -870,6 +875,28 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { packetReplyTimeout = timeout; } + protected void parseAndProcessStanza(XmlPullParser parser) throws Exception { + ParserUtils.assertAtStartTag(parser); + int parserDepth = parser.getDepth(); + Packet stanza = null; + try { + stanza = PacketParserUtils.parseStanza(parser, this); + } + catch (Exception e) { + CharSequence content = PacketParserUtils.parseContentDepth(parser, + parserDepth); + UnparsablePacket message = new UnparsablePacket(content, e); + ParsingExceptionCallback callback = getParsingExceptionCallback(); + if (callback != null) { + callback.handleUnparsablePacket(message); + } + } + ParserUtils.assertAtEndTag(parser); + if (stanza != null) { + processPacket(stanza); + } + } + /** * Processes a packet after it's been fully parsed by looping through the installed * packet collectors and listeners and letting them examine the packet to see if @@ -1271,7 +1298,27 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { return lastStanzaReceived; } + /** + * Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a + * stanza + * + * @param callback the callback to install + */ + public void setParsingExceptionCallback(ParsingExceptionCallback callback) { + parsingExceptionCallback = callback; + } + + /** + * Get the current active parsing exception callback. + * + * @return the active exception callback or null if there is none + */ + public ParsingExceptionCallback getParsingExceptionCallback() { + return parsingExceptionCallback; + } + protected final void asyncGo(Runnable runnable) { cachedExecutorService.execute(runnable); } + } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 060cb80b2..488189cfe 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -48,8 +48,6 @@ import org.jivesoftware.smack.packet.StreamOpen; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.StartTls; -import org.jivesoftware.smack.parsing.ParsingExceptionCallback; -import org.jivesoftware.smack.parsing.UnparsablePacket; import org.jivesoftware.smack.sasl.packet.SaslStreamElements; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; @@ -152,8 +150,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private boolean usingTLS = false; - private ParsingExceptionCallback parsingExceptionCallback = SmackConfiguration.getDefaultParsingExceptionCallback(); - /** * Protected access level because of unit test purposes */ @@ -293,25 +289,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { return connectionID; } - /** - * Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a - * stanza - * - * @param callback the callback to install - */ - public void setParsingExceptionCallback(ParsingExceptionCallback callback) { - parsingExceptionCallback = callback; - } - - /** - * Get the current active parsing exception callback. - * - * @return the active exception callback or null if there is none - */ - public ParsingExceptionCallback getParsingExceptionCallback() { - return parsingExceptionCallback; - } - @Override protected void throwNotConnectedExceptionIfAppropriate() throws NotConnectedException { packetWriter.throwNotConnectedExceptionIfDoneAndResumptionNotPossible(); @@ -975,29 +952,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { case Message.ELEMENT: case IQ.ELEMENT: case Presence.ELEMENT: - int parserDepth = parser.getDepth(); - Packet packet; try { - packet = PacketParserUtils.parseStanza(parser, - XMPPTCPConnection.this); - } - catch (Exception e) { - ParsingExceptionCallback callback = getParsingExceptionCallback(); - CharSequence content = PacketParserUtils.parseContentDepth(parser, - parserDepth); - UnparsablePacket message = new UnparsablePacket(content, e); - if (callback != null) { - callback.handleUnparsablePacket(message); - } - // The parser is now at the end tag of the unparsable stanza. We need to advance to the next - // start tag in order to avoid an exception which would again lead to the execution of the - // catch block becoming effectively an endless loop. - eventType = parser.next(); - continue; + parseAndProcessStanza(parser); } finally { clientHandledStanzasCount = SMUtils.incrementHeight(clientHandledStanzasCount); } - processPacket(packet); break; case "stream": // We found an opening stream.