001/** 002 * 003 * Copyright 2009 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack.bosh; 019 020import java.io.StringReader; 021 022import org.jivesoftware.smack.sasl.SASLMechanism.Challenge; 023import org.jivesoftware.smack.sasl.SASLMechanism.SASLFailure; 024import org.jivesoftware.smack.sasl.SASLMechanism.Success; 025import org.jivesoftware.smack.util.PacketParserUtils; 026import org.jivesoftware.smack.XMPPException.StreamErrorException; 027import org.xmlpull.v1.XmlPullParserFactory; 028import org.xmlpull.v1.XmlPullParser; 029import org.igniterealtime.jbosh.AbstractBody; 030import org.igniterealtime.jbosh.BOSHClientResponseListener; 031import org.igniterealtime.jbosh.BOSHMessageEvent; 032import org.igniterealtime.jbosh.BodyQName; 033import org.igniterealtime.jbosh.ComposableBody; 034 035/** 036 * Listens for XML traffic from the BOSH connection manager and parses it into 037 * packet objects. 038 * 039 * @author Guenther Niess 040 */ 041public class BOSHPacketReader implements BOSHClientResponseListener { 042 043 private XMPPBOSHConnection connection; 044 045 /** 046 * Create a packet reader which listen on a BOSHConnection for received 047 * HTTP responses, parse the packets and notifies the connection. 048 * 049 * @param connection the corresponding connection for the received packets. 050 */ 051 public BOSHPacketReader(XMPPBOSHConnection connection) { 052 this.connection = connection; 053 } 054 055 /** 056 * Parse the received packets and notify the corresponding connection. 057 * 058 * @param event the BOSH client response which includes the received packet. 059 */ 060 public void responseReceived(BOSHMessageEvent event) { 061 AbstractBody body = event.getBody(); 062 if (body != null) { 063 try { 064 if (connection.sessionID == null) { 065 connection.sessionID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "sid")); 066 } 067 if (connection.authID == null) { 068 connection.authID = body.getAttribute(BodyQName.create(XMPPBOSHConnection.BOSH_URI, "authid")); 069 } 070 final XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 071 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, 072 true); 073 parser.setInput(new StringReader(body.toXML())); 074 int eventType = parser.getEventType(); 075 do { 076 eventType = parser.next(); 077 if (eventType == XmlPullParser.START_TAG) { 078 if (parser.getName().equals("body")) { 079 // ignore the container root element 080 } else if (parser.getName().equals("message")) { 081 connection.processPacket(PacketParserUtils.parseMessage(parser)); 082 } else if (parser.getName().equals("iq")) { 083 connection.processPacket(PacketParserUtils.parseIQ(parser, connection)); 084 } else if (parser.getName().equals("presence")) { 085 connection.processPacket(PacketParserUtils.parsePresence(parser)); 086 } else if (parser.getName().equals("challenge")) { 087 // The server is challenging the SASL authentication 088 // made by the client 089 final String challengeData = parser.nextText(); 090 connection.getSASLAuthentication() 091 .challengeReceived(challengeData); 092 connection.processPacket(new Challenge( 093 challengeData)); 094 } else if (parser.getName().equals("success")) { 095 connection.send(ComposableBody.builder() 096 .setNamespaceDefinition("xmpp", XMPPBOSHConnection.XMPP_BOSH_NS) 097 .setAttribute( 098 BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart", "xmpp"), 099 "true") 100 .setAttribute( 101 BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), 102 connection.getServiceName()) 103 .build()); 104 connection.getSASLAuthentication().authenticated(); 105 connection.processPacket(new Success(parser.nextText())); 106 } else if (parser.getName().equals("features")) { 107 parseFeatures(parser); 108 } else if (parser.getName().equals("failure")) { 109 if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) { 110 final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser); 111 connection.getSASLAuthentication().authenticationFailed(failure); 112 connection.processPacket(failure); 113 } 114 } else if (parser.getName().equals("error")) { 115 throw new StreamErrorException(PacketParserUtils.parseStreamError(parser)); 116 } 117 } 118 } while (eventType != XmlPullParser.END_DOCUMENT); 119 } 120 catch (Exception e) { 121 if (connection.isConnected()) { 122 connection.notifyConnectionError(e); 123 } 124 } 125 } 126 } 127 128 /** 129 * Parse and setup the XML stream features. 130 * 131 * @param parser the XML parser, positioned at the start of a message packet. 132 * @throws Exception if an exception occurs while parsing the packet. 133 */ 134 private void parseFeatures(XmlPullParser parser) throws Exception { 135 boolean done = false; 136 while (!done) { 137 int eventType = parser.next(); 138 139 if (eventType == XmlPullParser.START_TAG) { 140 if (parser.getName().equals("mechanisms")) { 141 // The server is reporting available SASL mechanisms. Store 142 // this information 143 // which will be used later while logging (i.e. 144 // authenticating) into 145 // the server 146 connection.getSASLAuthentication().setAvailableSASLMethods( 147 PacketParserUtils.parseMechanisms(parser)); 148 } else if (parser.getName().equals("bind")) { 149 // The server requires the client to bind a resource to the 150 // stream 151 connection.serverRequiresBinding(); 152 } else if (parser.getName().equals("session")) { 153 // The server supports sessions 154 connection.serverSupportsSession(); 155 } else if (parser.getName().equals("register")) { 156 connection.serverSupportsAccountCreation(); 157 } 158 } else if (eventType == XmlPullParser.END_TAG) { 159 if (parser.getName().equals("features")) { 160 done = true; 161 } 162 } 163 } 164 } 165}