• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision$
4  * $Date$
5  *
6  * Copyright 2009 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package org.jivesoftware.smack;
22 
23 import java.io.StringReader;
24 
25 import org.jivesoftware.smack.util.PacketParserUtils;
26 import org.jivesoftware.smack.sasl.SASLMechanism.Challenge;
27 import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
28 import org.jivesoftware.smack.sasl.SASLMechanism.Success;
29 import org.xmlpull.v1.XmlPullParserFactory;
30 import org.xmlpull.v1.XmlPullParser;
31 
32 import com.kenai.jbosh.AbstractBody;
33 import com.kenai.jbosh.BOSHClientResponseListener;
34 import com.kenai.jbosh.BOSHMessageEvent;
35 import com.kenai.jbosh.BodyQName;
36 import com.kenai.jbosh.ComposableBody;
37 
38 /**
39  * Listens for XML traffic from the BOSH connection manager and parses it into
40  * packet objects.
41  *
42  * @author Guenther Niess
43  */
44 public class BOSHPacketReader implements BOSHClientResponseListener {
45 
46     private BOSHConnection connection;
47 
48     /**
49      * Create a packet reader which listen on a BOSHConnection for received
50      * HTTP responses, parse the packets and notifies the connection.
51      *
52      * @param connection the corresponding connection for the received packets.
53      */
BOSHPacketReader(BOSHConnection connection)54     public BOSHPacketReader(BOSHConnection connection) {
55         this.connection = connection;
56     }
57 
58     /**
59      * Parse the received packets and notify the corresponding connection.
60      *
61      * @param event the BOSH client response which includes the received packet.
62      */
responseReceived(BOSHMessageEvent event)63     public void responseReceived(BOSHMessageEvent event) {
64         AbstractBody body = event.getBody();
65         if (body != null) {
66             try {
67                 if (connection.sessionID == null) {
68                     connection.sessionID = body.getAttribute(BodyQName.create(BOSHConnection.BOSH_URI, "sid"));
69                 }
70                 if (connection.authID == null) {
71                     connection.authID = body.getAttribute(BodyQName.create(BOSHConnection.BOSH_URI, "authid"));
72                 }
73                 final XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
74                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,
75                         true);
76                 parser.setInput(new StringReader(body.toXML()));
77                 int eventType = parser.getEventType();
78                 do {
79                     eventType = parser.next();
80                     if (eventType == XmlPullParser.START_TAG) {
81                         if (parser.getName().equals("body")) {
82                             // ignore the container root element
83                         } else if (parser.getName().equals("message")) {
84                             connection.processPacket(PacketParserUtils.parseMessage(parser));
85                         } else if (parser.getName().equals("iq")) {
86                             connection.processPacket(PacketParserUtils.parseIQ(parser, connection));
87                         } else if (parser.getName().equals("presence")) {
88                             connection.processPacket(PacketParserUtils.parsePresence(parser));
89                         } else if (parser.getName().equals("challenge")) {
90                             // The server is challenging the SASL authentication
91                             // made by the client
92                             final String challengeData = parser.nextText();
93                             connection.getSASLAuthentication()
94                                     .challengeReceived(challengeData);
95                             connection.processPacket(new Challenge(
96                                     challengeData));
97                         } else if (parser.getName().equals("success")) {
98                             connection.send(ComposableBody.builder()
99                                     .setNamespaceDefinition("xmpp", BOSHConnection.XMPP_BOSH_NS)
100                                     .setAttribute(
101                                             BodyQName.createWithPrefix(BOSHConnection.XMPP_BOSH_NS, "restart", "xmpp"),
102                                             "true")
103                                     .setAttribute(
104                                             BodyQName.create(BOSHConnection.BOSH_URI, "to"),
105                                             connection.getServiceName())
106                                     .build());
107                             connection.getSASLAuthentication().authenticated();
108                             connection.processPacket(new Success(parser.nextText()));
109                         } else if (parser.getName().equals("features")) {
110                             parseFeatures(parser);
111                         } else if (parser.getName().equals("failure")) {
112                             if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) {
113                                 final Failure failure = PacketParserUtils.parseSASLFailure(parser);
114                                 connection.getSASLAuthentication().authenticationFailed();
115                                 connection.processPacket(failure);
116                             }
117                         } else if (parser.getName().equals("error")) {
118                             throw new XMPPException(PacketParserUtils.parseStreamError(parser));
119                         }
120                     }
121                 } while (eventType != XmlPullParser.END_DOCUMENT);
122             }
123             catch (Exception e) {
124                 if (connection.isConnected()) {
125                     connection.notifyConnectionError(e);
126                 }
127             }
128         }
129     }
130 
131     /**
132      * Parse and setup the XML stream features.
133      *
134      * @param parser the XML parser, positioned at the start of a message packet.
135      * @throws Exception if an exception occurs while parsing the packet.
136      */
parseFeatures(XmlPullParser parser)137     private void parseFeatures(XmlPullParser parser) throws Exception {
138         boolean done = false;
139         while (!done) {
140             int eventType = parser.next();
141 
142             if (eventType == XmlPullParser.START_TAG) {
143                 if (parser.getName().equals("mechanisms")) {
144                     // The server is reporting available SASL mechanisms. Store
145                     // this information
146                     // which will be used later while logging (i.e.
147                     // authenticating) into
148                     // the server
149                     connection.getSASLAuthentication().setAvailableSASLMethods(
150                             PacketParserUtils.parseMechanisms(parser));
151                 } else if (parser.getName().equals("bind")) {
152                     // The server requires the client to bind a resource to the
153                     // stream
154                     connection.getSASLAuthentication().bindingRequired();
155                 } else if (parser.getName().equals("session")) {
156                     // The server supports sessions
157                     connection.getSASLAuthentication().sessionsSupported();
158                 } else if (parser.getName().equals("register")) {
159                     connection.getAccountManager().setSupportsAccountCreation(
160                             true);
161                 }
162             } else if (eventType == XmlPullParser.END_TAG) {
163                 if (parser.getName().equals("features")) {
164                     done = true;
165                 }
166             }
167         }
168     }
169 }
170