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