diff options
author | Shuyi Chen <shuyichen@google.com> | 2013-05-22 14:51:55 -0700 |
---|---|---|
committer | Shuyi Chen <shuyichen@google.com> | 2013-05-22 17:19:30 -0700 |
commit | d7955ce24d294fb2014c59d11fca184471056f44 (patch) | |
tree | e260500b0b7639127038495d46a0ad6dcbb6d96c /src/org/jivesoftware/smack/util/PacketParserUtils.java | |
parent | 8f4ce9ea0de51fee918bffe19c434612d6bbb2d7 (diff) | |
download | smack-kitkat-mr1-release.tar.gz |
Add android smack source.HEADandroid-wear-5.0.0_r1android-sdk-4.4.2_r1.0.1android-sdk-4.4.2_r1android-l-preview_r2android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-cts-4.4_r4android-cts-4.4_r1android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1android-4.4w_r1android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.4_r0.7android-4.4.4_r2.0.1android-4.4.4_r2android-4.4.4_r1.0.1android-4.4.4_r1android-4.4.3_r1.1.0.1android-4.4.3_r1.1android-4.4.3_r1.0.1android-4.4.3_r1android-4.4.2_r2.0.1android-4.4.2_r2android-4.4.2_r1.0.1android-4.4.2_r1android-4.4.1_r1.0.1android-4.4.1_r1android-4.3_r3.1android-4.3_r3android-4.3_r2.3android-4.3_r2.2android-4.3_r2.1android-4.3_r2android-4.3_r1.1android-4.3_r1android-4.3_r0.9.1android-4.3_r0.9android-4.3.1_r1tools_r22.2mastermainlollipop-wear-releaselollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-releasel-previewkitkat-wearkitkat-releasekitkat-mr2.2-releasekitkat-mr2.1-releasekitkat-mr2-releasekitkat-mr1.1-releasekitkat-mr1-releasekitkat-devkitkat-cts-releasekitkat-cts-devjb-mr2.0.0-releasejb-mr2.0-releasejb-mr2-releasejb-mr2-devidea133-weekly-releaseidea133
Change-Id: I49ce97136c17173c4ae3965c694af6e7bc49897d
Diffstat (limited to 'src/org/jivesoftware/smack/util/PacketParserUtils.java')
-rw-r--r-- | src/org/jivesoftware/smack/util/PacketParserUtils.java | 925 |
1 files changed, 925 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/util/PacketParserUtils.java b/src/org/jivesoftware/smack/util/PacketParserUtils.java new file mode 100644 index 0000000..aacbad5 --- /dev/null +++ b/src/org/jivesoftware/smack/util/PacketParserUtils.java @@ -0,0 +1,925 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. 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.util; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.packet.Authentication; +import org.jivesoftware.smack.packet.Bind; +import org.jivesoftware.smack.packet.DefaultPacketExtension; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Registration; +import org.jivesoftware.smack.packet.RosterPacket; +import org.jivesoftware.smack.packet.StreamError; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.sasl.SASLMechanism.Failure; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Utility class that helps to parse packets. Any parsing packets method that must be shared + * between many clients must be placed in this utility class. + * + * @author Gaston Dombiak + */ +public class PacketParserUtils { + + /** + * Namespace used to store packet properties. + */ + private static final String PROPERTIES_NAMESPACE = + "http://www.jivesoftware.com/xmlns/xmpp/properties"; + + /** + * Parses a message packet. + * + * @param parser the XML parser, positioned at the start of a message packet. + * @return a Message packet. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static Packet parseMessage(XmlPullParser parser) throws Exception { + Message message = new Message(); + String id = parser.getAttributeValue("", "id"); + message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id); + message.setTo(parser.getAttributeValue("", "to")); + message.setFrom(parser.getAttributeValue("", "from")); + message.setType(Message.Type.fromString(parser.getAttributeValue("", "type"))); + String language = getLanguageAttribute(parser); + + // determine message's default language + String defaultLanguage = null; + if (language != null && !"".equals(language.trim())) { + message.setLanguage(language); + defaultLanguage = language; + } + else { + defaultLanguage = Packet.getDefaultLanguage(); + } + + // Parse sub-elements. We include extra logic to make sure the values + // are only read once. This is because it's possible for the names to appear + // in arbitrary sub-elements. + boolean done = false; + String thread = null; + Map<String, Object> properties = null; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (elementName.equals("subject")) { + String xmlLang = getLanguageAttribute(parser); + if (xmlLang == null) { + xmlLang = defaultLanguage; + } + + String subject = parseContent(parser); + + if (message.getSubject(xmlLang) == null) { + message.addSubject(xmlLang, subject); + } + } + else if (elementName.equals("body")) { + String xmlLang = getLanguageAttribute(parser); + if (xmlLang == null) { + xmlLang = defaultLanguage; + } + + String body = parseContent(parser); + + if (message.getBody(xmlLang) == null) { + message.addBody(xmlLang, body); + } + } + else if (elementName.equals("thread")) { + if (thread == null) { + thread = parser.nextText(); + } + } + else if (elementName.equals("error")) { + message.setError(parseError(parser)); + } + else if (elementName.equals("properties") && + namespace.equals(PROPERTIES_NAMESPACE)) + { + properties = parseProperties(parser); + } + // Otherwise, it must be a packet extension. + else { + message.addExtension( + PacketParserUtils.parsePacketExtension(elementName, namespace, parser)); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("message")) { + done = true; + } + } + } + + message.setThread(thread); + // Set packet properties. + if (properties != null) { + for (String name : properties.keySet()) { + message.setProperty(name, properties.get(name)); + } + } + return message; + } + + /** + * Returns the content of a tag as string regardless of any tags included. + * + * @param parser the XML pull parser + * @return the content of a tag as string + * @throws XmlPullParserException if parser encounters invalid XML + * @throws IOException if an IO error occurs + */ + private static String parseContent(XmlPullParser parser) + throws XmlPullParserException, IOException { + StringBuffer content = new StringBuffer(); + int parserDepth = parser.getDepth(); + while (!(parser.next() == XmlPullParser.END_TAG && parser + .getDepth() == parserDepth)) { + content.append(parser.getText()); + } + return content.toString(); + } + + /** + * Parses a presence packet. + * + * @param parser the XML parser, positioned at the start of a presence packet. + * @return a Presence packet. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static Presence parsePresence(XmlPullParser parser) throws Exception { + Presence.Type type = Presence.Type.available; + String typeString = parser.getAttributeValue("", "type"); + if (typeString != null && !typeString.equals("")) { + try { + type = Presence.Type.valueOf(typeString); + } + catch (IllegalArgumentException iae) { + System.err.println("Found invalid presence type " + typeString); + } + } + Presence presence = new Presence(type); + presence.setTo(parser.getAttributeValue("", "to")); + presence.setFrom(parser.getAttributeValue("", "from")); + String id = parser.getAttributeValue("", "id"); + presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id); + + String language = getLanguageAttribute(parser); + if (language != null && !"".equals(language.trim())) { + presence.setLanguage(language); + } + presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id); + + // Parse sub-elements + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (elementName.equals("status")) { + presence.setStatus(parser.nextText()); + } + else if (elementName.equals("priority")) { + try { + int priority = Integer.parseInt(parser.nextText()); + presence.setPriority(priority); + } + catch (NumberFormatException nfe) { + // Ignore. + } + catch (IllegalArgumentException iae) { + // Presence priority is out of range so assume priority to be zero + presence.setPriority(0); + } + } + else if (elementName.equals("show")) { + String modeText = parser.nextText(); + try { + presence.setMode(Presence.Mode.valueOf(modeText)); + } + catch (IllegalArgumentException iae) { + System.err.println("Found invalid presence mode " + modeText); + } + } + else if (elementName.equals("error")) { + presence.setError(parseError(parser)); + } + else if (elementName.equals("properties") && + namespace.equals(PROPERTIES_NAMESPACE)) + { + Map<String,Object> properties = parseProperties(parser); + // Set packet properties. + for (String name : properties.keySet()) { + presence.setProperty(name, properties.get(name)); + } + } + // Otherwise, it must be a packet extension. + else { + try { + presence.addExtension(PacketParserUtils.parsePacketExtension(elementName, namespace, parser)); + } + catch (Exception e) { + System.err.println("Failed to parse extension packet in Presence packet."); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("presence")) { + done = true; + } + } + } + return presence; + } + + /** + * Parses an IQ packet. + * + * @param parser the XML parser, positioned at the start of an IQ packet. + * @return an IQ object. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static IQ parseIQ(XmlPullParser parser, Connection connection) throws Exception { + IQ iqPacket = null; + + String id = parser.getAttributeValue("", "id"); + String to = parser.getAttributeValue("", "to"); + String from = parser.getAttributeValue("", "from"); + IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type")); + XMPPError error = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (elementName.equals("error")) { + error = PacketParserUtils.parseError(parser); + } + else if (elementName.equals("query") && namespace.equals("jabber:iq:auth")) { + iqPacket = parseAuthentication(parser); + } + else if (elementName.equals("query") && namespace.equals("jabber:iq:roster")) { + iqPacket = parseRoster(parser); + } + else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) { + iqPacket = parseRegistration(parser); + } + else if (elementName.equals("bind") && + namespace.equals("urn:ietf:params:xml:ns:xmpp-bind")) { + iqPacket = parseResourceBinding(parser); + } + // Otherwise, see if there is a registered provider for + // this element name and namespace. + else { + Object provider = ProviderManager.getInstance().getIQProvider(elementName, namespace); + if (provider != null) { + if (provider instanceof IQProvider) { + iqPacket = ((IQProvider)provider).parseIQ(parser); + } + else if (provider instanceof Class) { + iqPacket = (IQ)PacketParserUtils.parseWithIntrospection(elementName, + (Class<?>)provider, parser); + } + } + // Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood + // have to be answered with an IQ error response. See the code a few lines below + else if (IQ.Type.RESULT == type){ + // No Provider found for the IQ stanza, parse it to an UnparsedIQ instance + // so that the content of the IQ can be examined later on + iqPacket = new UnparsedResultIQ(parseContent(parser)); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("iq")) { + done = true; + } + } + } + // Decide what to do when an IQ packet was not understood + if (iqPacket == null) { + if (IQ.Type.GET == type || IQ.Type.SET == type ) { + // If the IQ stanza is of type "get" or "set" containing a child element + // qualified by a namespace it does not understand, then answer an IQ of + // type "error" with code 501 ("feature-not-implemented") + iqPacket = new IQ() { + @Override + public String getChildElementXML() { + return null; + } + }; + iqPacket.setPacketID(id); + iqPacket.setTo(from); + iqPacket.setFrom(to); + iqPacket.setType(IQ.Type.ERROR); + iqPacket.setError(new XMPPError(XMPPError.Condition.feature_not_implemented)); + connection.sendPacket(iqPacket); + return null; + } + else { + // If an IQ packet wasn't created above, create an empty IQ packet. + iqPacket = new IQ() { + @Override + public String getChildElementXML() { + return null; + } + }; + } + } + + // Set basic values on the iq packet. + iqPacket.setPacketID(id); + iqPacket.setTo(to); + iqPacket.setFrom(from); + iqPacket.setType(type); + iqPacket.setError(error); + + return iqPacket; + } + + private static Authentication parseAuthentication(XmlPullParser parser) throws Exception { + Authentication authentication = new Authentication(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("username")) { + authentication.setUsername(parser.nextText()); + } + else if (parser.getName().equals("password")) { + authentication.setPassword(parser.nextText()); + } + else if (parser.getName().equals("digest")) { + authentication.setDigest(parser.nextText()); + } + else if (parser.getName().equals("resource")) { + authentication.setResource(parser.nextText()); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("query")) { + done = true; + } + } + } + return authentication; + } + + private static RosterPacket parseRoster(XmlPullParser parser) throws Exception { + RosterPacket roster = new RosterPacket(); + boolean done = false; + RosterPacket.Item item = null; + while (!done) { + if(parser.getEventType()==XmlPullParser.START_TAG && + parser.getName().equals("query")){ + String version = parser.getAttributeValue(null, "ver"); + roster.setVersion(version); + } + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("item")) { + String jid = parser.getAttributeValue("", "jid"); + String name = parser.getAttributeValue("", "name"); + // Create packet. + item = new RosterPacket.Item(jid, name); + // Set status. + String ask = parser.getAttributeValue("", "ask"); + RosterPacket.ItemStatus status = RosterPacket.ItemStatus.fromString(ask); + item.setItemStatus(status); + // Set type. + String subscription = parser.getAttributeValue("", "subscription"); + RosterPacket.ItemType type = RosterPacket.ItemType.valueOf(subscription != null ? subscription : "none"); + item.setItemType(type); + } + if (parser.getName().equals("group") && item!= null) { + final String groupName = parser.nextText(); + if (groupName != null && groupName.trim().length() > 0) { + item.addGroupName(groupName); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("item")) { + roster.addRosterItem(item); + } + if (parser.getName().equals("query")) { + done = true; + } + } + } + return roster; + } + + private static Registration parseRegistration(XmlPullParser parser) throws Exception { + Registration registration = new Registration(); + Map<String, String> fields = null; + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + // Any element that's in the jabber:iq:register namespace, + // attempt to parse it if it's in the form <name>value</name>. + if (parser.getNamespace().equals("jabber:iq:register")) { + String name = parser.getName(); + String value = ""; + if (fields == null) { + fields = new HashMap<String, String>(); + } + + if (parser.next() == XmlPullParser.TEXT) { + value = parser.getText(); + } + // Ignore instructions, but anything else should be added to the map. + if (!name.equals("instructions")) { + fields.put(name, value); + } + else { + registration.setInstructions(value); + } + } + // Otherwise, it must be a packet extension. + else { + registration.addExtension( + PacketParserUtils.parsePacketExtension( + parser.getName(), + parser.getNamespace(), + parser)); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("query")) { + done = true; + } + } + } + registration.setAttributes(fields); + return registration; + } + + private static Bind parseResourceBinding(XmlPullParser parser) throws IOException, + XmlPullParserException { + Bind bind = new Bind(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("resource")) { + bind.setResource(parser.nextText()); + } + else if (parser.getName().equals("jid")) { + bind.setJid(parser.nextText()); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("bind")) { + done = true; + } + } + } + + return bind; + } + + /** + * Parse the available SASL mechanisms reported from the server. + * + * @param parser the XML parser, positioned at the start of the mechanisms stanza. + * @return a collection of Stings with the mechanisms included in the mechanisms stanza. + * @throws Exception if an exception occurs while parsing the stanza. + */ + public static Collection<String> parseMechanisms(XmlPullParser parser) throws Exception { + List<String> mechanisms = new ArrayList<String>(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + if (elementName.equals("mechanism")) { + mechanisms.add(parser.nextText()); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("mechanisms")) { + done = true; + } + } + } + return mechanisms; + } + + /** + * Parse the available compression methods reported from the server. + * + * @param parser the XML parser, positioned at the start of the compression stanza. + * @return a collection of Stings with the methods included in the compression stanza. + * @throws Exception if an exception occurs while parsing the stanza. + */ + public static Collection<String> parseCompressionMethods(XmlPullParser parser) + throws IOException, XmlPullParserException { + List<String> methods = new ArrayList<String>(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + if (elementName.equals("method")) { + methods.add(parser.nextText()); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("compression")) { + done = true; + } + } + } + return methods; + } + + /** + * Parse a properties sub-packet. If any errors occur while de-serializing Java object + * properties, an exception will be printed and not thrown since a thrown + * exception will shut down the entire connection. ClassCastExceptions will occur + * when both the sender and receiver of the packet don't have identical versions + * of the same class. + * + * @param parser the XML parser, positioned at the start of a properties sub-packet. + * @return a map of the properties. + * @throws Exception if an error occurs while parsing the properties. + */ + public static Map<String, Object> parseProperties(XmlPullParser parser) throws Exception { + Map<String, Object> properties = new HashMap<String, Object>(); + while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG && parser.getName().equals("property")) { + // Parse a property + boolean done = false; + String name = null; + String type = null; + String valueText = null; + Object value = null; + while (!done) { + eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + String elementName = parser.getName(); + if (elementName.equals("name")) { + name = parser.nextText(); + } + else if (elementName.equals("value")) { + type = parser.getAttributeValue("", "type"); + valueText = parser.nextText(); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("property")) { + if ("integer".equals(type)) { + value = Integer.valueOf(valueText); + } + else if ("long".equals(type)) { + value = Long.valueOf(valueText); + } + else if ("float".equals(type)) { + value = Float.valueOf(valueText); + } + else if ("double".equals(type)) { + value = Double.valueOf(valueText); + } + else if ("boolean".equals(type)) { + value = Boolean.valueOf(valueText); + } + else if ("string".equals(type)) { + value = valueText; + } + else if ("java-object".equals(type)) { + try { + byte [] bytes = StringUtils.decodeBase64(valueText); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); + value = in.readObject(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + if (name != null && value != null) { + properties.put(name, value); + } + done = true; + } + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("properties")) { + break; + } + } + } + return properties; + } + + /** + * Parses SASL authentication error packets. + * + * @param parser the XML parser. + * @return a SASL Failure packet. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static Failure parseSASLFailure(XmlPullParser parser) throws Exception { + String condition = null; + boolean done = false; + while (!done) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + if (!parser.getName().equals("failure")) { + condition = parser.getName(); + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("failure")) { + done = true; + } + } + } + return new Failure(condition); + } + + /** + * Parses stream error packets. + * + * @param parser the XML parser. + * @return an stream error packet. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static StreamError parseStreamError(XmlPullParser parser) throws IOException, + XmlPullParserException { + StreamError streamError = null; + boolean done = false; + while (!done) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + streamError = new StreamError(parser.getName()); + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("error")) { + done = true; + } + } + } + return streamError; +} + + /** + * Parses error sub-packets. + * + * @param parser the XML parser. + * @return an error sub-packet. + * @throws Exception if an exception occurs while parsing the packet. + */ + public static XMPPError parseError(XmlPullParser parser) throws Exception { + final String errorNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas"; + String errorCode = "-1"; + String type = null; + String message = null; + String condition = null; + List<PacketExtension> extensions = new ArrayList<PacketExtension>(); + + // Parse the error header + for (int i=0; i<parser.getAttributeCount(); i++) { + if (parser.getAttributeName(i).equals("code")) { + errorCode = parser.getAttributeValue("", "code"); + } + if (parser.getAttributeName(i).equals("type")) { + type = parser.getAttributeValue("", "type"); + } + } + boolean done = false; + // Parse the text and condition tags + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("text")) { + message = parser.nextText(); + } + else { + // Condition tag, it can be xmpp error or an application defined error. + String elementName = parser.getName(); + String namespace = parser.getNamespace(); + if (errorNamespace.equals(namespace)) { + condition = elementName; + } + else { + extensions.add(parsePacketExtension(elementName, namespace, parser)); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("error")) { + done = true; + } + } + } + // Parse the error type. + XMPPError.Type errorType = XMPPError.Type.CANCEL; + try { + if (type != null) { + errorType = XMPPError.Type.valueOf(type.toUpperCase()); + } + } + catch (IllegalArgumentException iae) { + // Print stack trace. We shouldn't be getting an illegal error type. + iae.printStackTrace(); + } + return new XMPPError(Integer.parseInt(errorCode), errorType, condition, message, extensions); + } + + /** + * Parses a packet extension sub-packet. + * + * @param elementName the XML element name of the packet extension. + * @param namespace the XML namespace of the packet extension. + * @param parser the XML parser, positioned at the starting element of the extension. + * @return a PacketExtension. + * @throws Exception if a parsing error occurs. + */ + public static PacketExtension parsePacketExtension(String elementName, String namespace, XmlPullParser parser) + throws Exception + { + // See if a provider is registered to handle the extension. + Object provider = ProviderManager.getInstance().getExtensionProvider(elementName, namespace); + if (provider != null) { + if (provider instanceof PacketExtensionProvider) { + return ((PacketExtensionProvider)provider).parseExtension(parser); + } + else if (provider instanceof Class) { + return (PacketExtension)parseWithIntrospection( + elementName, (Class<?>)provider, parser); + } + } + // No providers registered, so use a default extension. + DefaultPacketExtension extension = new DefaultPacketExtension(elementName, namespace); + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + String name = parser.getName(); + // If an empty element, set the value with the empty string. + if (parser.isEmptyElementTag()) { + extension.setValue(name,""); + } + // Otherwise, get the the element text. + else { + eventType = parser.next(); + if (eventType == XmlPullParser.TEXT) { + String value = parser.getText(); + extension.setValue(name, value); + } + } + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(elementName)) { + done = true; + } + } + } + return extension; + } + + private static String getLanguageAttribute(XmlPullParser parser) { + for (int i = 0; i < parser.getAttributeCount(); i++) { + String attributeName = parser.getAttributeName(i); + if ( "xml:lang".equals(attributeName) || + ("lang".equals(attributeName) && + "xml".equals(parser.getAttributePrefix(i)))) { + return parser.getAttributeValue(i); + } + } + return null; + } + + public static Object parseWithIntrospection(String elementName, + Class<?> objectClass, XmlPullParser parser) throws Exception + { + boolean done = false; + Object object = objectClass.newInstance(); + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + String name = parser.getName(); + String stringValue = parser.nextText(); + Class propertyType = object.getClass().getMethod( + "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType(); + // Get the value of the property by converting it from a + // String to the correct object type. + Object value = decode(propertyType, stringValue); + // Set the value of the bean. + object.getClass().getMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), propertyType) + .invoke(object, value); + } + else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals(elementName)) { + done = true; + } + } + } + return object; + } + + /** + * Decodes a String into an object of the specified type. If the object + * type is not supported, null will be returned. + * + * @param type the type of the property. + * @param value the encode String value to decode. + * @return the String value decoded into the specified type. + * @throws Exception If decoding failed due to an error. + */ + private static Object decode(Class<?> type, String value) throws Exception { + if (type.getName().equals("java.lang.String")) { + return value; + } + if (type.getName().equals("boolean")) { + return Boolean.valueOf(value); + } + if (type.getName().equals("int")) { + return Integer.valueOf(value); + } + if (type.getName().equals("long")) { + return Long.valueOf(value); + } + if (type.getName().equals("float")) { + return Float.valueOf(value); + } + if (type.getName().equals("double")) { + return Double.valueOf(value); + } + if (type.getName().equals("java.lang.Class")) { + return Class.forName(value); + } + return null; + } + + /** + * This class represents and unparsed IQ of the type 'result'. Usually it's created when no IQProvider + * was found for the IQ element. + * + * The child elements can be examined with the getChildElementXML() method. + * + */ + public static class UnparsedResultIQ extends IQ { + public UnparsedResultIQ(String content) { + this.str = content; + } + + private final String str; + + @Override + public String getChildElementXML() { + return this.str; + } + } +} |