Home | History | Annotate | Download | only in smack
      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      */
     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 }
    170