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