blob: c86d756a19482b55d22fed332975e2f1ad0b9056 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
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
21package org.jivesoftware.smack;
22
23import java.io.StringReader;
24
25import org.jivesoftware.smack.util.PacketParserUtils;
26import org.jivesoftware.smack.sasl.SASLMechanism.Challenge;
27import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
28import org.jivesoftware.smack.sasl.SASLMechanism.Success;
29import org.xmlpull.v1.XmlPullParserFactory;
30import org.xmlpull.v1.XmlPullParser;
31
32import com.kenai.jbosh.AbstractBody;
33import com.kenai.jbosh.BOSHClientResponseListener;
34import com.kenai.jbosh.BOSHMessageEvent;
35import com.kenai.jbosh.BodyQName;
36import 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 */
44public 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 */
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 */
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 */
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}