• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision$
4  * $Date$
5  *
6  * Copyright 2003-2007 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.util;
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.io.ObjectInputStream;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 
32 import org.jivesoftware.smack.Connection;
33 import org.jivesoftware.smack.packet.Authentication;
34 import org.jivesoftware.smack.packet.Bind;
35 import org.jivesoftware.smack.packet.DefaultPacketExtension;
36 import org.jivesoftware.smack.packet.IQ;
37 import org.jivesoftware.smack.packet.Message;
38 import org.jivesoftware.smack.packet.Packet;
39 import org.jivesoftware.smack.packet.PacketExtension;
40 import org.jivesoftware.smack.packet.Presence;
41 import org.jivesoftware.smack.packet.Registration;
42 import org.jivesoftware.smack.packet.RosterPacket;
43 import org.jivesoftware.smack.packet.StreamError;
44 import org.jivesoftware.smack.packet.XMPPError;
45 import org.jivesoftware.smack.provider.IQProvider;
46 import org.jivesoftware.smack.provider.PacketExtensionProvider;
47 import org.jivesoftware.smack.provider.ProviderManager;
48 import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
49 import org.xmlpull.v1.XmlPullParser;
50 import org.xmlpull.v1.XmlPullParserException;
51 
52 /**
53  * Utility class that helps to parse packets. Any parsing packets method that must be shared
54  * between many clients must be placed in this utility class.
55  *
56  * @author Gaston Dombiak
57  */
58 public class PacketParserUtils {
59 
60     /**
61      * Namespace used to store packet properties.
62      */
63     private static final String PROPERTIES_NAMESPACE =
64             "http://www.jivesoftware.com/xmlns/xmpp/properties";
65 
66     /**
67      * Parses a message packet.
68      *
69      * @param parser the XML parser, positioned at the start of a message packet.
70      * @return a Message packet.
71      * @throws Exception if an exception occurs while parsing the packet.
72      */
parseMessage(XmlPullParser parser)73     public static Packet parseMessage(XmlPullParser parser) throws Exception {
74         Message message = new Message();
75         String id = parser.getAttributeValue("", "id");
76         message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
77         message.setTo(parser.getAttributeValue("", "to"));
78         message.setFrom(parser.getAttributeValue("", "from"));
79         message.setType(Message.Type.fromString(parser.getAttributeValue("", "type")));
80         String language = getLanguageAttribute(parser);
81 
82         // determine message's default language
83         String defaultLanguage = null;
84         if (language != null && !"".equals(language.trim())) {
85             message.setLanguage(language);
86             defaultLanguage = language;
87         }
88         else {
89             defaultLanguage = Packet.getDefaultLanguage();
90         }
91 
92         // Parse sub-elements. We include extra logic to make sure the values
93         // are only read once. This is because it's possible for the names to appear
94         // in arbitrary sub-elements.
95         boolean done = false;
96         String thread = null;
97         Map<String, Object> properties = null;
98         while (!done) {
99             int eventType = parser.next();
100             if (eventType == XmlPullParser.START_TAG) {
101                 String elementName = parser.getName();
102                 String namespace = parser.getNamespace();
103                 if (elementName.equals("subject")) {
104                     String xmlLang = getLanguageAttribute(parser);
105                     if (xmlLang == null) {
106                         xmlLang = defaultLanguage;
107                     }
108 
109                     String subject = parseContent(parser);
110 
111                     if (message.getSubject(xmlLang) == null) {
112                         message.addSubject(xmlLang, subject);
113                     }
114                 }
115                 else if (elementName.equals("body")) {
116                     String xmlLang = getLanguageAttribute(parser);
117                     if (xmlLang == null) {
118                         xmlLang = defaultLanguage;
119                     }
120 
121                     String body = parseContent(parser);
122 
123                     if (message.getBody(xmlLang) == null) {
124                         message.addBody(xmlLang, body);
125                     }
126                 }
127                 else if (elementName.equals("thread")) {
128                     if (thread == null) {
129                         thread = parser.nextText();
130                     }
131                 }
132                 else if (elementName.equals("error")) {
133                     message.setError(parseError(parser));
134                 }
135                 else if (elementName.equals("properties") &&
136                         namespace.equals(PROPERTIES_NAMESPACE))
137                 {
138                     properties = parseProperties(parser);
139                 }
140                 // Otherwise, it must be a packet extension.
141                 else {
142                     message.addExtension(
143                     PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
144                 }
145             }
146             else if (eventType == XmlPullParser.END_TAG) {
147                 if (parser.getName().equals("message")) {
148                     done = true;
149                 }
150             }
151         }
152 
153         message.setThread(thread);
154         // Set packet properties.
155         if (properties != null) {
156             for (String name : properties.keySet()) {
157                 message.setProperty(name, properties.get(name));
158             }
159         }
160         return message;
161     }
162 
163     /**
164      * Returns the content of a tag as string regardless of any tags included.
165      *
166      * @param parser the XML pull parser
167      * @return the content of a tag as string
168      * @throws XmlPullParserException if parser encounters invalid XML
169      * @throws IOException if an IO error occurs
170      */
parseContent(XmlPullParser parser)171     private static String parseContent(XmlPullParser parser)
172                     throws XmlPullParserException, IOException {
173         StringBuffer content = new StringBuffer();
174         int parserDepth = parser.getDepth();
175         while (!(parser.next() == XmlPullParser.END_TAG && parser
176                         .getDepth() == parserDepth)) {
177             content.append(parser.getText());
178         }
179         return content.toString();
180     }
181 
182     /**
183      * Parses a presence packet.
184      *
185      * @param parser the XML parser, positioned at the start of a presence packet.
186      * @return a Presence packet.
187      * @throws Exception if an exception occurs while parsing the packet.
188      */
parsePresence(XmlPullParser parser)189     public static Presence parsePresence(XmlPullParser parser) throws Exception {
190         Presence.Type type = Presence.Type.available;
191         String typeString = parser.getAttributeValue("", "type");
192         if (typeString != null && !typeString.equals("")) {
193             try {
194                 type = Presence.Type.valueOf(typeString);
195             }
196             catch (IllegalArgumentException iae) {
197                 System.err.println("Found invalid presence type " + typeString);
198             }
199         }
200         Presence presence = new Presence(type);
201         presence.setTo(parser.getAttributeValue("", "to"));
202         presence.setFrom(parser.getAttributeValue("", "from"));
203         String id = parser.getAttributeValue("", "id");
204         presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
205 
206         String language = getLanguageAttribute(parser);
207         if (language != null && !"".equals(language.trim())) {
208         	presence.setLanguage(language);
209         }
210         presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
211 
212         // Parse sub-elements
213         boolean done = false;
214         while (!done) {
215             int eventType = parser.next();
216             if (eventType == XmlPullParser.START_TAG) {
217                 String elementName = parser.getName();
218                 String namespace = parser.getNamespace();
219                 if (elementName.equals("status")) {
220                     presence.setStatus(parser.nextText());
221                 }
222                 else if (elementName.equals("priority")) {
223                     try {
224                         int priority = Integer.parseInt(parser.nextText());
225                         presence.setPriority(priority);
226                     }
227                     catch (NumberFormatException nfe) {
228                         // Ignore.
229                     }
230                     catch (IllegalArgumentException iae) {
231                         // Presence priority is out of range so assume priority to be zero
232                         presence.setPriority(0);
233                     }
234                 }
235                 else if (elementName.equals("show")) {
236                     String modeText = parser.nextText();
237                     try {
238                         presence.setMode(Presence.Mode.valueOf(modeText));
239                     }
240                     catch (IllegalArgumentException iae) {
241                         System.err.println("Found invalid presence mode " + modeText);
242                     }
243                 }
244                 else if (elementName.equals("error")) {
245                     presence.setError(parseError(parser));
246                 }
247                 else if (elementName.equals("properties") &&
248                         namespace.equals(PROPERTIES_NAMESPACE))
249                 {
250                     Map<String,Object> properties = parseProperties(parser);
251                     // Set packet properties.
252                     for (String name : properties.keySet()) {
253                         presence.setProperty(name, properties.get(name));
254                     }
255                 }
256                 // Otherwise, it must be a packet extension.
257                 else {
258                 	try {
259                         presence.addExtension(PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
260                 	}
261                 	catch (Exception e) {
262                 		System.err.println("Failed to parse extension packet in Presence packet.");
263                 	}
264                 }
265             }
266             else if (eventType == XmlPullParser.END_TAG) {
267                 if (parser.getName().equals("presence")) {
268                     done = true;
269                 }
270             }
271         }
272         return presence;
273     }
274 
275     /**
276      * Parses an IQ packet.
277      *
278      * @param parser the XML parser, positioned at the start of an IQ packet.
279      * @return an IQ object.
280      * @throws Exception if an exception occurs while parsing the packet.
281      */
parseIQ(XmlPullParser parser, Connection connection)282     public static IQ parseIQ(XmlPullParser parser, Connection connection) throws Exception {
283         IQ iqPacket = null;
284 
285         String id = parser.getAttributeValue("", "id");
286         String to = parser.getAttributeValue("", "to");
287         String from = parser.getAttributeValue("", "from");
288         IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
289         XMPPError error = null;
290 
291         boolean done = false;
292         while (!done) {
293             int eventType = parser.next();
294 
295             if (eventType == XmlPullParser.START_TAG) {
296                 String elementName = parser.getName();
297                 String namespace = parser.getNamespace();
298                 if (elementName.equals("error")) {
299                     error = PacketParserUtils.parseError(parser);
300                 }
301                 else if (elementName.equals("query") && namespace.equals("jabber:iq:auth")) {
302                     iqPacket = parseAuthentication(parser);
303                 }
304                 else if (elementName.equals("query") && namespace.equals("jabber:iq:roster")) {
305                     iqPacket = parseRoster(parser);
306                 }
307                 else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) {
308                     iqPacket = parseRegistration(parser);
309                 }
310                 else if (elementName.equals("bind") &&
311                         namespace.equals("urn:ietf:params:xml:ns:xmpp-bind")) {
312                     iqPacket = parseResourceBinding(parser);
313                 }
314                 // Otherwise, see if there is a registered provider for
315                 // this element name and namespace.
316                 else {
317                     Object provider = ProviderManager.getInstance().getIQProvider(elementName, namespace);
318                     if (provider != null) {
319                         if (provider instanceof IQProvider) {
320                             iqPacket = ((IQProvider)provider).parseIQ(parser);
321                         }
322                         else if (provider instanceof Class) {
323                             iqPacket = (IQ)PacketParserUtils.parseWithIntrospection(elementName,
324                                     (Class<?>)provider, parser);
325                         }
326                     }
327                     // Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood
328                     // have to be answered with an IQ error response. See the code a few lines below
329                     else if (IQ.Type.RESULT == type){
330                         // No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
331                         // so that the content of the IQ can be examined later on
332                         iqPacket = new UnparsedResultIQ(parseContent(parser));
333                     }
334                 }
335             }
336             else if (eventType == XmlPullParser.END_TAG) {
337                 if (parser.getName().equals("iq")) {
338                     done = true;
339                 }
340             }
341         }
342         // Decide what to do when an IQ packet was not understood
343         if (iqPacket == null) {
344             if (IQ.Type.GET == type || IQ.Type.SET == type ) {
345                 // If the IQ stanza is of type "get" or "set" containing a child element
346                 // qualified by a namespace it does not understand, then answer an IQ of
347                 // type "error" with code 501 ("feature-not-implemented")
348                 iqPacket = new IQ() {
349                     @Override
350                     public String getChildElementXML() {
351                         return null;
352                     }
353                 };
354                 iqPacket.setPacketID(id);
355                 iqPacket.setTo(from);
356                 iqPacket.setFrom(to);
357                 iqPacket.setType(IQ.Type.ERROR);
358                 iqPacket.setError(new XMPPError(XMPPError.Condition.feature_not_implemented));
359                 connection.sendPacket(iqPacket);
360                 return null;
361             }
362             else {
363                 // If an IQ packet wasn't created above, create an empty IQ packet.
364                 iqPacket = new IQ() {
365                     @Override
366                     public String getChildElementXML() {
367                         return null;
368                     }
369                 };
370             }
371         }
372 
373         // Set basic values on the iq packet.
374         iqPacket.setPacketID(id);
375         iqPacket.setTo(to);
376         iqPacket.setFrom(from);
377         iqPacket.setType(type);
378         iqPacket.setError(error);
379 
380         return iqPacket;
381     }
382 
parseAuthentication(XmlPullParser parser)383     private static Authentication parseAuthentication(XmlPullParser parser) throws Exception {
384         Authentication authentication = new Authentication();
385         boolean done = false;
386         while (!done) {
387             int eventType = parser.next();
388             if (eventType == XmlPullParser.START_TAG) {
389                 if (parser.getName().equals("username")) {
390                     authentication.setUsername(parser.nextText());
391                 }
392                 else if (parser.getName().equals("password")) {
393                     authentication.setPassword(parser.nextText());
394                 }
395                 else if (parser.getName().equals("digest")) {
396                     authentication.setDigest(parser.nextText());
397                 }
398                 else if (parser.getName().equals("resource")) {
399                     authentication.setResource(parser.nextText());
400                 }
401             }
402             else if (eventType == XmlPullParser.END_TAG) {
403                 if (parser.getName().equals("query")) {
404                     done = true;
405                 }
406             }
407         }
408         return authentication;
409     }
410 
parseRoster(XmlPullParser parser)411     private static RosterPacket parseRoster(XmlPullParser parser) throws Exception {
412         RosterPacket roster = new RosterPacket();
413         boolean done = false;
414         RosterPacket.Item item = null;
415         while (!done) {
416         	if(parser.getEventType()==XmlPullParser.START_TAG &&
417         			parser.getName().equals("query")){
418         		String version = parser.getAttributeValue(null, "ver");
419         		roster.setVersion(version);
420         	}
421             int eventType = parser.next();
422             if (eventType == XmlPullParser.START_TAG) {
423                 if (parser.getName().equals("item")) {
424                     String jid = parser.getAttributeValue("", "jid");
425                     String name = parser.getAttributeValue("", "name");
426                     // Create packet.
427                     item = new RosterPacket.Item(jid, name);
428                     // Set status.
429                     String ask = parser.getAttributeValue("", "ask");
430                     RosterPacket.ItemStatus status = RosterPacket.ItemStatus.fromString(ask);
431                     item.setItemStatus(status);
432                     // Set type.
433                     String subscription = parser.getAttributeValue("", "subscription");
434                     RosterPacket.ItemType type = RosterPacket.ItemType.valueOf(subscription != null ? subscription : "none");
435                     item.setItemType(type);
436                 }
437                 if (parser.getName().equals("group") && item!= null) {
438                     final String groupName = parser.nextText();
439                     if (groupName != null && groupName.trim().length() > 0) {
440                         item.addGroupName(groupName);
441                     }
442                 }
443             }
444             else if (eventType == XmlPullParser.END_TAG) {
445                 if (parser.getName().equals("item")) {
446                     roster.addRosterItem(item);
447                 }
448                 if (parser.getName().equals("query")) {
449                     done = true;
450                 }
451             }
452         }
453         return roster;
454     }
455 
parseRegistration(XmlPullParser parser)456      private static Registration parseRegistration(XmlPullParser parser) throws Exception {
457         Registration registration = new Registration();
458         Map<String, String> fields = null;
459         boolean done = false;
460         while (!done) {
461             int eventType = parser.next();
462             if (eventType == XmlPullParser.START_TAG) {
463                 // Any element that's in the jabber:iq:register namespace,
464                 // attempt to parse it if it's in the form <name>value</name>.
465                 if (parser.getNamespace().equals("jabber:iq:register")) {
466                     String name = parser.getName();
467                     String value = "";
468                     if (fields == null) {
469                         fields = new HashMap<String, String>();
470                     }
471 
472                     if (parser.next() == XmlPullParser.TEXT) {
473                         value = parser.getText();
474                     }
475                     // Ignore instructions, but anything else should be added to the map.
476                     if (!name.equals("instructions")) {
477                         fields.put(name, value);
478                     }
479                     else {
480                         registration.setInstructions(value);
481                     }
482                 }
483                 // Otherwise, it must be a packet extension.
484                 else {
485                     registration.addExtension(
486                         PacketParserUtils.parsePacketExtension(
487                             parser.getName(),
488                             parser.getNamespace(),
489                             parser));
490                 }
491             }
492             else if (eventType == XmlPullParser.END_TAG) {
493                 if (parser.getName().equals("query")) {
494                     done = true;
495                 }
496             }
497         }
498         registration.setAttributes(fields);
499         return registration;
500     }
501 
parseResourceBinding(XmlPullParser parser)502     private static Bind parseResourceBinding(XmlPullParser parser) throws IOException,
503             XmlPullParserException {
504         Bind bind = new Bind();
505         boolean done = false;
506         while (!done) {
507             int eventType = parser.next();
508             if (eventType == XmlPullParser.START_TAG) {
509                 if (parser.getName().equals("resource")) {
510                     bind.setResource(parser.nextText());
511                 }
512                 else if (parser.getName().equals("jid")) {
513                     bind.setJid(parser.nextText());
514                 }
515             } else if (eventType == XmlPullParser.END_TAG) {
516                 if (parser.getName().equals("bind")) {
517                     done = true;
518                 }
519             }
520         }
521 
522         return bind;
523     }
524 
525     /**
526      * Parse the available SASL mechanisms reported from the server.
527      *
528      * @param parser the XML parser, positioned at the start of the mechanisms stanza.
529      * @return a collection of Stings with the mechanisms included in the mechanisms stanza.
530      * @throws Exception if an exception occurs while parsing the stanza.
531      */
parseMechanisms(XmlPullParser parser)532     public static Collection<String> parseMechanisms(XmlPullParser parser) throws Exception {
533         List<String> mechanisms = new ArrayList<String>();
534         boolean done = false;
535         while (!done) {
536             int eventType = parser.next();
537 
538             if (eventType == XmlPullParser.START_TAG) {
539                 String elementName = parser.getName();
540                 if (elementName.equals("mechanism")) {
541                     mechanisms.add(parser.nextText());
542                 }
543             }
544             else if (eventType == XmlPullParser.END_TAG) {
545                 if (parser.getName().equals("mechanisms")) {
546                     done = true;
547                 }
548             }
549         }
550         return mechanisms;
551     }
552 
553     /**
554      * Parse the available compression methods reported from the server.
555      *
556      * @param parser the XML parser, positioned at the start of the compression stanza.
557      * @return a collection of Stings with the methods included in the compression stanza.
558      * @throws Exception if an exception occurs while parsing the stanza.
559      */
parseCompressionMethods(XmlPullParser parser)560     public static Collection<String> parseCompressionMethods(XmlPullParser parser)
561             throws IOException, XmlPullParserException {
562         List<String> methods = new ArrayList<String>();
563         boolean done = false;
564         while (!done) {
565             int eventType = parser.next();
566 
567             if (eventType == XmlPullParser.START_TAG) {
568                 String elementName = parser.getName();
569                 if (elementName.equals("method")) {
570                     methods.add(parser.nextText());
571                 }
572             }
573             else if (eventType == XmlPullParser.END_TAG) {
574                 if (parser.getName().equals("compression")) {
575                     done = true;
576                 }
577             }
578         }
579         return methods;
580     }
581 
582     /**
583      * Parse a properties sub-packet. If any errors occur while de-serializing Java object
584      * properties, an exception will be printed and not thrown since a thrown
585      * exception will shut down the entire connection. ClassCastExceptions will occur
586      * when both the sender and receiver of the packet don't have identical versions
587      * of the same class.
588      *
589      * @param parser the XML parser, positioned at the start of a properties sub-packet.
590      * @return a map of the properties.
591      * @throws Exception if an error occurs while parsing the properties.
592      */
parseProperties(XmlPullParser parser)593     public static Map<String, Object> parseProperties(XmlPullParser parser) throws Exception {
594         Map<String, Object> properties = new HashMap<String, Object>();
595         while (true) {
596             int eventType = parser.next();
597             if (eventType == XmlPullParser.START_TAG && parser.getName().equals("property")) {
598                 // Parse a property
599                 boolean done = false;
600                 String name = null;
601                 String type = null;
602                 String valueText = null;
603                 Object value = null;
604                 while (!done) {
605                     eventType = parser.next();
606                     if (eventType == XmlPullParser.START_TAG) {
607                         String elementName = parser.getName();
608                         if (elementName.equals("name")) {
609                             name = parser.nextText();
610                         }
611                         else if (elementName.equals("value")) {
612                             type = parser.getAttributeValue("", "type");
613                             valueText = parser.nextText();
614                         }
615                     }
616                     else if (eventType == XmlPullParser.END_TAG) {
617                         if (parser.getName().equals("property")) {
618                             if ("integer".equals(type)) {
619                                 value = Integer.valueOf(valueText);
620                             }
621                             else if ("long".equals(type))  {
622                                 value = Long.valueOf(valueText);
623                             }
624                             else if ("float".equals(type)) {
625                                 value = Float.valueOf(valueText);
626                             }
627                             else if ("double".equals(type)) {
628                                 value = Double.valueOf(valueText);
629                             }
630                             else if ("boolean".equals(type)) {
631                                 value = Boolean.valueOf(valueText);
632                             }
633                             else if ("string".equals(type)) {
634                                 value = valueText;
635                             }
636                             else if ("java-object".equals(type)) {
637                                 try {
638                                     byte [] bytes = StringUtils.decodeBase64(valueText);
639                                     ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
640                                     value = in.readObject();
641                                 }
642                                 catch (Exception e) {
643                                     e.printStackTrace();
644                                 }
645                             }
646                             if (name != null && value != null) {
647                                 properties.put(name, value);
648                             }
649                             done = true;
650                         }
651                     }
652                 }
653             }
654             else if (eventType == XmlPullParser.END_TAG) {
655                 if (parser.getName().equals("properties")) {
656                     break;
657                 }
658             }
659         }
660         return properties;
661     }
662 
663     /**
664      * Parses SASL authentication error packets.
665      *
666      * @param parser the XML parser.
667      * @return a SASL Failure packet.
668      * @throws Exception if an exception occurs while parsing the packet.
669      */
parseSASLFailure(XmlPullParser parser)670     public static Failure parseSASLFailure(XmlPullParser parser) throws Exception {
671         String condition = null;
672         boolean done = false;
673         while (!done) {
674             int eventType = parser.next();
675 
676             if (eventType == XmlPullParser.START_TAG) {
677                 if (!parser.getName().equals("failure")) {
678                     condition = parser.getName();
679                 }
680             }
681             else if (eventType == XmlPullParser.END_TAG) {
682                 if (parser.getName().equals("failure")) {
683                     done = true;
684                 }
685             }
686         }
687         return new Failure(condition);
688     }
689 
690     /**
691      * Parses stream error packets.
692      *
693      * @param parser the XML parser.
694      * @return an stream error packet.
695      * @throws Exception if an exception occurs while parsing the packet.
696      */
parseStreamError(XmlPullParser parser)697     public static StreamError parseStreamError(XmlPullParser parser) throws IOException,
698             XmlPullParserException {
699     StreamError streamError = null;
700     boolean done = false;
701     while (!done) {
702         int eventType = parser.next();
703 
704         if (eventType == XmlPullParser.START_TAG) {
705             streamError = new StreamError(parser.getName());
706         }
707         else if (eventType == XmlPullParser.END_TAG) {
708             if (parser.getName().equals("error")) {
709                 done = true;
710             }
711         }
712     }
713     return streamError;
714 }
715 
716     /**
717      * Parses error sub-packets.
718      *
719      * @param parser the XML parser.
720      * @return an error sub-packet.
721      * @throws Exception if an exception occurs while parsing the packet.
722      */
parseError(XmlPullParser parser)723     public static XMPPError parseError(XmlPullParser parser) throws Exception {
724         final String errorNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
725     	String errorCode = "-1";
726         String type = null;
727         String message = null;
728         String condition = null;
729         List<PacketExtension> extensions = new ArrayList<PacketExtension>();
730 
731         // Parse the error header
732         for (int i=0; i<parser.getAttributeCount(); i++) {
733             if (parser.getAttributeName(i).equals("code")) {
734                 errorCode = parser.getAttributeValue("", "code");
735             }
736             if (parser.getAttributeName(i).equals("type")) {
737             	type = parser.getAttributeValue("", "type");
738             }
739         }
740         boolean done = false;
741         // Parse the text and condition tags
742         while (!done) {
743             int eventType = parser.next();
744             if (eventType == XmlPullParser.START_TAG) {
745                 if (parser.getName().equals("text")) {
746                     message = parser.nextText();
747                 }
748                 else {
749                 	// Condition tag, it can be xmpp error or an application defined error.
750                     String elementName = parser.getName();
751                     String namespace = parser.getNamespace();
752                     if (errorNamespace.equals(namespace)) {
753                     	condition = elementName;
754                     }
755                     else {
756                     	extensions.add(parsePacketExtension(elementName, namespace, parser));
757                     }
758                 }
759             }
760                 else if (eventType == XmlPullParser.END_TAG) {
761                     if (parser.getName().equals("error")) {
762                         done = true;
763                     }
764                 }
765         }
766         // Parse the error type.
767         XMPPError.Type errorType = XMPPError.Type.CANCEL;
768         try {
769             if (type != null) {
770                 errorType = XMPPError.Type.valueOf(type.toUpperCase());
771             }
772         }
773         catch (IllegalArgumentException iae) {
774             // Print stack trace. We shouldn't be getting an illegal error type.
775             iae.printStackTrace();
776         }
777         return new XMPPError(Integer.parseInt(errorCode), errorType, condition, message, extensions);
778     }
779 
780     /**
781      * Parses a packet extension sub-packet.
782      *
783      * @param elementName the XML element name of the packet extension.
784      * @param namespace the XML namespace of the packet extension.
785      * @param parser the XML parser, positioned at the starting element of the extension.
786      * @return a PacketExtension.
787      * @throws Exception if a parsing error occurs.
788      */
parsePacketExtension(String elementName, String namespace, XmlPullParser parser)789     public static PacketExtension parsePacketExtension(String elementName, String namespace, XmlPullParser parser)
790             throws Exception
791     {
792         // See if a provider is registered to handle the extension.
793         Object provider = ProviderManager.getInstance().getExtensionProvider(elementName, namespace);
794         if (provider != null) {
795             if (provider instanceof PacketExtensionProvider) {
796                 return ((PacketExtensionProvider)provider).parseExtension(parser);
797             }
798             else if (provider instanceof Class) {
799                 return (PacketExtension)parseWithIntrospection(
800                         elementName, (Class<?>)provider, parser);
801             }
802         }
803         // No providers registered, so use a default extension.
804         DefaultPacketExtension extension = new DefaultPacketExtension(elementName, namespace);
805         boolean done = false;
806         while (!done) {
807             int eventType = parser.next();
808             if (eventType == XmlPullParser.START_TAG) {
809                 String name = parser.getName();
810                 // If an empty element, set the value with the empty string.
811                 if (parser.isEmptyElementTag()) {
812                     extension.setValue(name,"");
813                 }
814                 // Otherwise, get the the element text.
815                 else {
816                     eventType = parser.next();
817                     if (eventType == XmlPullParser.TEXT) {
818                         String value = parser.getText();
819                         extension.setValue(name, value);
820                     }
821                 }
822             }
823             else if (eventType == XmlPullParser.END_TAG) {
824                 if (parser.getName().equals(elementName)) {
825                     done = true;
826                 }
827             }
828         }
829         return extension;
830     }
831 
getLanguageAttribute(XmlPullParser parser)832     private static String getLanguageAttribute(XmlPullParser parser) {
833     	for (int i = 0; i < parser.getAttributeCount(); i++) {
834             String attributeName = parser.getAttributeName(i);
835             if ( "xml:lang".equals(attributeName) ||
836                     ("lang".equals(attributeName) &&
837                             "xml".equals(parser.getAttributePrefix(i)))) {
838     			return parser.getAttributeValue(i);
839     		}
840     	}
841     	return null;
842     }
843 
parseWithIntrospection(String elementName, Class<?> objectClass, XmlPullParser parser)844     public static Object parseWithIntrospection(String elementName,
845             Class<?> objectClass, XmlPullParser parser) throws Exception
846     {
847         boolean done = false;
848         Object object = objectClass.newInstance();
849         while (!done) {
850             int eventType = parser.next();
851             if (eventType == XmlPullParser.START_TAG) {
852                 String name = parser.getName();
853                 String stringValue = parser.nextText();
854                 Class propertyType = object.getClass().getMethod(
855                     "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType();
856                 // Get the value of the property by converting it from a
857                 // String to the correct object type.
858                 Object value = decode(propertyType, stringValue);
859                 // Set the value of the bean.
860                 object.getClass().getMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), propertyType)
861                 .invoke(object, value);
862             }
863             else if (eventType == XmlPullParser.END_TAG) {
864                 if (parser.getName().equals(elementName)) {
865                     done = true;
866                 }
867             }
868         }
869         return object;
870             }
871 
872     /**
873      * Decodes a String into an object of the specified type. If the object
874      * type is not supported, null will be returned.
875      *
876      * @param type the type of the property.
877      * @param value the encode String value to decode.
878      * @return the String value decoded into the specified type.
879      * @throws Exception If decoding failed due to an error.
880      */
decode(Class<?> type, String value)881     private static Object decode(Class<?> type, String value) throws Exception {
882         if (type.getName().equals("java.lang.String")) {
883             return value;
884         }
885         if (type.getName().equals("boolean")) {
886             return Boolean.valueOf(value);
887         }
888         if (type.getName().equals("int")) {
889             return Integer.valueOf(value);
890         }
891         if (type.getName().equals("long")) {
892             return Long.valueOf(value);
893         }
894         if (type.getName().equals("float")) {
895             return Float.valueOf(value);
896         }
897         if (type.getName().equals("double")) {
898             return Double.valueOf(value);
899         }
900         if (type.getName().equals("java.lang.Class")) {
901             return Class.forName(value);
902         }
903         return null;
904     }
905 
906     /**
907      * This class represents and unparsed IQ of the type 'result'. Usually it's created when no IQProvider
908      * was found for the IQ element.
909      *
910      * The child elements can be examined with the getChildElementXML() method.
911      *
912      */
913     public static class UnparsedResultIQ extends IQ {
UnparsedResultIQ(String content)914         public UnparsedResultIQ(String content) {
915             this.str = content;
916         }
917 
918         private final String str;
919 
920         @Override
getChildElementXML()921         public String getChildElementXML() {
922             return this.str;
923         }
924     }
925 }
926