PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/XMPP_Demo/Core/XMPPParser.m

https://gitlab.com/praveenvelanati/ios-demo
Objective C | 875 lines | 550 code | 178 blank | 147 comment | 125 complexity | 9c52b3169fd0ab5e2e963b7835c4c3fb MD5 | raw file
  1. #import "XMPPParser.h"
  2. #import "XMPPLogging.h"
  3. #import <libxml/parser.h>
  4. #import <libxml/parserInternals.h>
  5. #if TARGET_OS_IPHONE
  6. #import "DDXMLPrivate.h"
  7. #endif
  8. #if ! __has_feature(objc_arc)
  9. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  10. #endif
  11. /**
  12. * Does ARC support support GCD objects?
  13. * It does if the minimum deployment target is iOS 6+ or Mac OS X 10.8+
  14. **/
  15. #if TARGET_OS_IPHONE
  16. // Compiling for iOS
  17. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
  18. #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  19. #else // iOS 5.X or earlier
  20. #define NEEDS_DISPATCH_RETAIN_RELEASE 1
  21. #endif
  22. #else
  23. // Compiling for Mac OS X
  24. #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
  25. #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  26. #else
  27. #define NEEDS_DISPATCH_RETAIN_RELEASE 1 // Mac OS X 10.7 or earlier
  28. #endif
  29. #endif
  30. // Log levels: off, error, warn, info, verbose
  31. #if DEBUG
  32. static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE;
  33. #else
  34. static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
  35. #endif
  36. #define CHECK_FOR_NULL(value) \
  37. do { \
  38. if (value == NULL) { \
  39. xmpp_xmlAbortDueToMemoryShortage(ctxt); \
  40. return; \
  41. } \
  42. } while(false)
  43. #if !TARGET_OS_IPHONE
  44. static void xmpp_recursiveAddChild(NSXMLElement *parent, xmlNodePtr childNode);
  45. #endif
  46. @implementation XMPPParser
  47. {
  48. #if __has_feature(objc_arc_weak)
  49. __weak id delegate;
  50. #else
  51. __unsafe_unretained id delegate;
  52. #endif
  53. dispatch_queue_t delegateQueue;
  54. dispatch_queue_t parserQueue;
  55. BOOL hasReportedRoot;
  56. unsigned depth;
  57. xmlParserCtxt *parserCtxt;
  58. }
  59. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  60. #pragma mark iPhone
  61. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  62. #if TARGET_OS_IPHONE
  63. static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
  64. {
  65. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
  66. {
  67. // We first copy the root node.
  68. // We do this to allow the delegate to retain and make changes to the reported root
  69. // without affecting the underlying xmpp parser.
  70. // xmlCopyNode(const xmlNodePtr node, int extended)
  71. //
  72. // node:
  73. // the node to copy
  74. // extended:
  75. // if 1 do a recursive copy (properties, namespaces and children when applicable)
  76. // if 2 copy properties and namespaces (when applicable)
  77. xmlNodePtr rootCopy = xmlCopyNode(root, 2);
  78. DDXMLElement *rootCopyWrapper = [DDXMLElement nodeWithElementPrimitive:rootCopy owner:nil];
  79. __strong id theDelegate = parser->delegate;
  80. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  81. [theDelegate xmppParser:parser didReadRoot:rootCopyWrapper];
  82. }});
  83. // Note: DDXMLElement will properly free the rootCopy when it's deallocated.
  84. }
  85. }
  86. static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
  87. {
  88. // Detach the child from the xml tree.
  89. //
  90. // clean: Nullify next, prev, parent and doc pointers of child.
  91. // fixNamespaces: Recurse through subtree, and ensure no namespaces are pointing to xmlNs nodes outside the tree.
  92. // E.G. in a parent node that will no longer be available after the child is detached.
  93. //
  94. // We don't need to fix namespaces since we used xmpp_xmlSearchNs() to ensure we never created any
  95. // namespaces outside the subtree of the child in the first place.
  96. [DDXMLNode detachChild:child andClean:YES andFixNamespaces:NO];
  97. DDXMLElement *childWrapper = [DDXMLElement nodeWithElementPrimitive:child owner:nil];
  98. // Note: We want to detach the child from the root even if the delegate method isn't setup.
  99. // This prevents the doc from growing infinitely large.
  100. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
  101. {
  102. __strong id theDelegate = parser->delegate;
  103. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  104. [theDelegate xmppParser:parser didReadElement:childWrapper];
  105. }});
  106. }
  107. // Note: DDXMLElement will properly free the child when it's deallocated.
  108. }
  109. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. #pragma mark Mac
  111. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. #else
  113. static void xmpp_setName(NSXMLElement *element, xmlNodePtr node)
  114. {
  115. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  116. if (node->name == NULL)
  117. {
  118. [element setName:@""];
  119. return;
  120. }
  121. if ((node->ns != NULL) && (node->ns->prefix != NULL))
  122. {
  123. // E.g: <deusty:element xmlns:deusty="deusty.com"/>
  124. NSString *prefix = [[NSString alloc] initWithUTF8String:(const char *)node->ns->prefix];
  125. NSString *name = [[NSString alloc] initWithUTF8String:(const char *)node->name];
  126. NSString *elementName = [[NSString alloc] initWithFormat:@"%@:%@", prefix, name];
  127. [element setName:elementName];
  128. }
  129. else
  130. {
  131. NSString *elementName = [[NSString alloc] initWithUTF8String:(const char *)node->name];
  132. [element setName:elementName];
  133. }
  134. }
  135. static void xmpp_addNamespaces(NSXMLElement *element, xmlNodePtr node)
  136. {
  137. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  138. xmlNsPtr nsNode = node->nsDef;
  139. while (nsNode != NULL)
  140. {
  141. if (nsNode->href == NULL)
  142. {
  143. // Namespace doesn't have a value!
  144. }
  145. else
  146. {
  147. NSXMLNode *ns = [[NSXMLNode alloc] initWithKind:NSXMLNamespaceKind];
  148. if (nsNode->prefix != NULL)
  149. {
  150. NSString *nsName = [[NSString alloc] initWithUTF8String:(const char *)nsNode->prefix];
  151. [ns setName:nsName];
  152. }
  153. else
  154. {
  155. // Default namespace.
  156. // E.g: xmlns="deusty.com"
  157. [ns setName:@""];
  158. }
  159. NSString *nsValue = [[NSString alloc] initWithUTF8String:(const char *)nsNode->href];
  160. [ns setStringValue:nsValue];
  161. [element addNamespace:ns];
  162. }
  163. nsNode = nsNode->next;
  164. }
  165. }
  166. static void xmpp_addChildren(NSXMLElement *element, xmlNodePtr node)
  167. {
  168. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  169. xmlNodePtr childNode = node->children;
  170. while (childNode != NULL)
  171. {
  172. if (childNode->type == XML_ELEMENT_NODE)
  173. {
  174. xmpp_recursiveAddChild(element, childNode);
  175. }
  176. else if (childNode->type == XML_TEXT_NODE)
  177. {
  178. if (childNode->content != NULL)
  179. {
  180. NSString *value = [[NSString alloc] initWithUTF8String:(const char *)childNode->content];
  181. [element setStringValue:value];
  182. }
  183. }
  184. childNode = childNode->next;
  185. }
  186. }
  187. static void xmpp_addAttributes(NSXMLElement *element, xmlNodePtr node)
  188. {
  189. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  190. xmlAttrPtr attrNode = node->properties;
  191. while (attrNode != NULL)
  192. {
  193. if (attrNode->name == NULL)
  194. {
  195. // Attribute doesn't have a name!
  196. }
  197. else if (attrNode->children == NULL)
  198. {
  199. // Attribute doesn't have a value node!
  200. }
  201. else if (attrNode->children->content == NULL)
  202. {
  203. // Attribute doesn't have a value!
  204. }
  205. else
  206. {
  207. NSXMLNode *attr = [[NSXMLNode alloc] initWithKind:NSXMLAttributeKind];
  208. if ((attrNode->ns != NULL) && (attrNode->ns->prefix != NULL))
  209. {
  210. // E.g: <element xmlns:deusty="deusty.com" deusty:attr="value"/>
  211. NSString *prefix = [[NSString alloc] initWithUTF8String:(const char *)attrNode->ns->prefix];
  212. NSString *name = [[NSString alloc] initWithUTF8String:(const char *)attrNode->name];
  213. NSString *attrName = [[NSString alloc] initWithFormat:@"%@:%@", prefix, name];
  214. [attr setName:attrName];
  215. }
  216. else
  217. {
  218. NSString *attrName = [[NSString alloc] initWithUTF8String:(const char *)attrNode->name];
  219. [attr setName:attrName];
  220. }
  221. NSString *attrValue = [[NSString alloc] initWithUTF8String:(const char *)attrNode->children->content];
  222. [attr setStringValue:attrValue];
  223. [element addAttribute:attr];
  224. }
  225. attrNode = attrNode->next;
  226. }
  227. }
  228. /**
  229. * Recursively adds all the child elements to the given parent.
  230. *
  231. * Note: This method is almost the same as xmpp_nsxmlFromLibxml, with one important difference.
  232. * It doen't add any objects to the autorelease pool (xmpp_nsxmlFromLibXml has return value).
  233. **/
  234. static void xmpp_recursiveAddChild(NSXMLElement *parent, xmlNodePtr childNode)
  235. {
  236. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  237. NSXMLElement *child = [[NSXMLElement alloc] initWithKind:NSXMLElementKind];
  238. xmpp_setName(child, childNode);
  239. xmpp_addNamespaces(child, childNode);
  240. xmpp_addChildren(child, childNode);
  241. xmpp_addAttributes(child, childNode);
  242. [parent addChild:child];
  243. }
  244. /**
  245. * Creates and returns an NSXMLElement from the given node.
  246. * Use this method after finding the root element, or root.child element.
  247. **/
  248. static NSXMLElement* xmpp_nsxmlFromLibxml(xmlNodePtr rootNode)
  249. {
  250. // Remember: The NSString initWithUTF8String raises an exception if passed NULL
  251. NSXMLElement *root = [[NSXMLElement alloc] initWithKind:NSXMLElementKind];
  252. xmpp_setName(root, rootNode);
  253. xmpp_addNamespaces(root, rootNode);
  254. xmpp_addChildren(root, rootNode);
  255. xmpp_addAttributes(root, rootNode);
  256. return root;
  257. }
  258. static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
  259. {
  260. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
  261. {
  262. NSXMLElement *nsRoot = xmpp_nsxmlFromLibxml(root);
  263. __strong id theDelegate = parser->delegate;
  264. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  265. [theDelegate xmppParser:parser didReadRoot:nsRoot];
  266. }});
  267. }
  268. }
  269. static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
  270. {
  271. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
  272. {
  273. NSXMLElement *nsChild = xmpp_nsxmlFromLibxml(child);
  274. __strong id theDelegate = parser->delegate;
  275. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  276. [theDelegate xmppParser:parser didReadElement:nsChild];
  277. }});
  278. }
  279. // Note: We want to detach the child from the root even if the delegate method isn't setup.
  280. // This prevents the doc from growing infinitely large.
  281. // Detach and free child to keep memory footprint small
  282. xmlUnlinkNode(child);
  283. xmlFreeNode(child);
  284. }
  285. #endif
  286. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  287. #pragma mark Common
  288. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  289. /**
  290. * This method is called at the end of the xmlStartElement method.
  291. * This allows us to inspect the parser and xml tree, and determine if we need to invoke any delegate methods.
  292. **/
  293. static void xmpp_postStartElement(xmlParserCtxt *ctxt)
  294. {
  295. XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
  296. parser->depth++;
  297. if (!(parser->hasReportedRoot) && (parser->depth == 1))
  298. {
  299. // We've received the full root - report it to the delegate
  300. if (ctxt->myDoc)
  301. {
  302. xmlNodePtr root = xmlDocGetRootElement(ctxt->myDoc);
  303. if (root)
  304. {
  305. xmpp_onDidReadRoot(parser, root);
  306. parser->hasReportedRoot = YES;
  307. }
  308. }
  309. }
  310. }
  311. /**
  312. * This method is called at the end of the xmlEndElement method.
  313. * This allows us to inspect the parser and xml tree, and determine if we need to invoke any delegate methods.
  314. **/
  315. static void xmpp_postEndElement(xmlParserCtxt *ctxt)
  316. {
  317. XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
  318. parser->depth--;
  319. if (parser->depth == 1)
  320. {
  321. // End of full xmpp element.
  322. // That is, a child of the root element.
  323. // Extract the child, and pass it to the delegate.
  324. xmlDocPtr doc = ctxt->myDoc;
  325. xmlNodePtr root = xmlDocGetRootElement(doc);
  326. xmlNodePtr child = root->children;
  327. while (child != NULL)
  328. {
  329. if (child->type == XML_ELEMENT_NODE)
  330. {
  331. xmpp_onDidReadElement(parser, child);
  332. // Exit while loop
  333. break;
  334. }
  335. child = child->next;
  336. }
  337. }
  338. else if (parser->depth == 0)
  339. {
  340. // End of the root element
  341. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParserDidEnd:)])
  342. {
  343. __strong id theDelegate = parser->delegate;
  344. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  345. [theDelegate xmppParserDidEnd:parser];
  346. }});
  347. }
  348. }
  349. }
  350. /**
  351. * We're screwed...
  352. **/
  353. static void xmpp_xmlAbortDueToMemoryShortage(xmlParserCtxt *ctxt)
  354. {
  355. XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
  356. xmlStopParser(ctxt);
  357. if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didFail:)])
  358. {
  359. NSString *errMsg = @"Unable to allocate memory in xmpp parser";
  360. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  361. NSError *error = [NSError errorWithDomain:@"libxmlErrorDomain" code:1001 userInfo:info];
  362. __strong id theDelegate = parser->delegate;
  363. dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  364. [theDelegate xmppParser:parser didFail:error];
  365. }});
  366. }
  367. }
  368. /**
  369. * (Similar to the libxml "xmlSearchNs" method, with one very important difference.)
  370. *
  371. * This method searches for an existing xmlNsPtr in the given node,
  372. * recursing on the parents but stopping before it reaches the root node of the document.
  373. *
  374. * Why do we skip the root node?
  375. * Because all nodes are going to be detached from the root node.
  376. * So it makes no sense to allow them to reference namespaces stored in the root node,
  377. * since the detach algorithm will be forced to copy the namespaces later anyway.
  378. **/
  379. static xmlNsPtr xmpp_xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace)
  380. {
  381. xmlNodePtr rootNode = xmlDocGetRootElement(doc);
  382. xmlNodePtr currentNode = node;
  383. while (currentNode && currentNode != rootNode)
  384. {
  385. xmlNsPtr currentNs = currentNode->nsDef;
  386. while (currentNs)
  387. {
  388. if (currentNs->href != NULL)
  389. {
  390. if ((currentNs->prefix == NULL) && (nameSpace == NULL))
  391. {
  392. return currentNs;
  393. }
  394. if ((currentNs->prefix != NULL) && (nameSpace != NULL))
  395. {
  396. if (xmlStrEqual(currentNs->prefix, nameSpace))
  397. return currentNs;
  398. }
  399. }
  400. currentNs = currentNs->next;
  401. }
  402. currentNode = currentNode->parent;
  403. }
  404. return NULL;
  405. }
  406. /**
  407. * SAX parser C-style callback.
  408. * Invoked when a new node element is started.
  409. **/
  410. static void xmpp_xmlStartElement(void *ctx, const xmlChar *nodeName,
  411. const xmlChar *nodePrefix,
  412. const xmlChar *nodeUri,
  413. int nb_namespaces,
  414. const xmlChar **namespaces,
  415. int nb_attributes,
  416. int nb_defaulted,
  417. const xmlChar **attributes)
  418. {
  419. int i, j;
  420. xmlNsPtr lastAddedNs = NULL;
  421. xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
  422. // We store the parent node in the context's node pointer.
  423. // We keep this updated by "pushing" the node in the startElement method,
  424. // and "popping" the node in the endElement method.
  425. xmlNodePtr parent = ctxt->node;
  426. // Create the node
  427. xmlNodePtr newNode = xmlNewDocNode(ctxt->myDoc, NULL, nodeName, NULL);
  428. CHECK_FOR_NULL(newNode);
  429. // Add the node to the tree
  430. if (parent == NULL)
  431. {
  432. // Root node
  433. xmlAddChild((xmlNodePtr)ctxt->myDoc, newNode);
  434. }
  435. else
  436. {
  437. xmlAddChild(parent, newNode);
  438. }
  439. // Process the namespaces
  440. for (i = 0, j = 0; j < nb_namespaces; j++)
  441. {
  442. // Extract namespace prefix and uri
  443. const xmlChar *nsPrefix = namespaces[i++];
  444. const xmlChar *nsUri = namespaces[i++];
  445. // Create the namespace
  446. xmlNsPtr newNs = xmlNewNs(NULL, nsUri, nsPrefix);
  447. CHECK_FOR_NULL(newNs);
  448. // Add namespace to node.
  449. // Each node has a linked list of nodes (in the nsDef variable).
  450. // The linked list is forward only.
  451. // In other words, each ns has a next, but not a prev pointer.
  452. if (newNode->nsDef == NULL)
  453. {
  454. newNode->nsDef = newNs;
  455. lastAddedNs = newNs;
  456. }
  457. else
  458. {
  459. lastAddedNs->next = newNs;
  460. lastAddedNs = newNs;
  461. }
  462. // Is this the namespace for the node?
  463. if (nodeUri && (nodePrefix == nsPrefix))
  464. {
  465. // Ex 1: node == <stream:stream xmlns:stream="url"> && newNs == stream:url
  466. // Ex 2: node == <starttls xmlns="url"> && newNs == null:url
  467. newNode->ns = newNs;
  468. }
  469. }
  470. // Search for the node's namespace if it wasn't already found
  471. if ((nodeUri) && (newNode->ns == NULL))
  472. {
  473. newNode->ns = xmpp_xmlSearchNs(ctxt->myDoc, newNode, nodePrefix);
  474. if (newNode->ns == NULL)
  475. {
  476. // We use href==NULL in the case of an element creation where the namespace was not defined.
  477. //
  478. // We do NOT use xmlNewNs(newNode, nodeUri, nodePrefix) because that method doesn't properly add
  479. // the namespace to BOTH nsDef and ns.
  480. xmlNsPtr newNs = xmlNewNs(NULL, nodeUri, nodePrefix);
  481. CHECK_FOR_NULL(newNs);
  482. if (newNode->nsDef == NULL)
  483. {
  484. newNode->nsDef = newNs;
  485. }
  486. else
  487. {
  488. lastAddedNs->next = newNs;
  489. }
  490. newNode->ns = newNs;
  491. }
  492. }
  493. // Process all the attributes
  494. for (i = 0, j = 0; j < nb_attributes; j++)
  495. {
  496. const xmlChar *attrName = attributes[i++];
  497. const xmlChar *attrPrefix = attributes[i++];
  498. const xmlChar *attrUri = attributes[i++];
  499. const xmlChar *valueBegin = attributes[i++];
  500. const xmlChar *valueEnd = attributes[i++];
  501. // The attribute value might contain character references which need to be decoded.
  502. //
  503. // "Franks &#38; Beans" -> "Franks & Beans"
  504. xmlChar *value = xmlStringLenDecodeEntities(ctxt, // the parser context
  505. valueBegin, // the input string
  506. (int)(valueEnd - valueBegin), // the input string length
  507. (XML_SUBSTITUTE_REF), // what to substitue
  508. 0, 0, 0); // end markers, 0 if none
  509. CHECK_FOR_NULL(value);
  510. if ((attrPrefix == NULL) && (attrUri == NULL))
  511. {
  512. // Normal attribute - no associated namespace
  513. xmlAttrPtr newAttr = xmlNewProp(newNode, attrName, value);
  514. CHECK_FOR_NULL(newAttr);
  515. }
  516. else
  517. {
  518. // Find the namespace for the attribute
  519. xmlNsPtr attrNs = xmpp_xmlSearchNs(ctxt->myDoc, newNode, attrPrefix);
  520. if (attrNs != NULL)
  521. {
  522. xmlAttrPtr newAttr = xmlNewNsProp(newNode, attrNs, attrName, value);
  523. CHECK_FOR_NULL(newAttr);
  524. }
  525. else
  526. {
  527. attrNs = xmlNewNs(NULL, NULL, nodePrefix);
  528. CHECK_FOR_NULL(attrNs);
  529. xmlAttrPtr newAttr = xmlNewNsProp(newNode, attrNs, attrName, value);
  530. CHECK_FOR_NULL(newAttr);
  531. }
  532. }
  533. xmlFree(value);
  534. }
  535. // Update our parent node pointer
  536. ctxt->node = newNode;
  537. // Invoke delegate methods if needed
  538. xmpp_postStartElement(ctxt);
  539. }
  540. /**
  541. * SAX parser C-style callback.
  542. * Invoked when characters are found within a node.
  543. **/
  544. static void xmpp_xmlCharacters(void *ctx, const xmlChar *ch, int len)
  545. {
  546. xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
  547. if (ctxt->node != NULL)
  548. {
  549. xmlNodePtr textNode = xmlNewTextLen(ch, len);
  550. // xmlAddChild(xmlNodePtr parent, xmlNodePtr cur)
  551. //
  552. // Add a new node to @parent, at the end of the child list
  553. // merging adjacent TEXT nodes (in which case @cur is freed).
  554. xmlAddChild(ctxt->node, textNode);
  555. }
  556. }
  557. /**
  558. * SAX parser C-style callback.
  559. * Invoked when a new node element is ended.
  560. **/
  561. static void xmpp_xmlEndElement(void *ctx, const xmlChar *localname,
  562. const xmlChar *prefix,
  563. const xmlChar *URI)
  564. {
  565. xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
  566. // Update our parent node pointer
  567. if (ctxt->node != NULL)
  568. ctxt->node = ctxt->node->parent;
  569. // Invoke delegate methods if needed
  570. xmpp_postEndElement(ctxt);
  571. }
  572. - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
  573. {
  574. return [self initWithDelegate:aDelegate delegateQueue:dq parserQueue:NULL];
  575. }
  576. - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq parserQueue:(dispatch_queue_t)pq
  577. {
  578. if ((self = [super init]))
  579. {
  580. delegate = aDelegate;
  581. delegateQueue = dq;
  582. #if NEEDS_DISPATCH_RETAIN_RELEASE
  583. if (delegateQueue)
  584. dispatch_retain(delegateQueue);
  585. #endif
  586. if (pq) {
  587. parserQueue = pq;
  588. #if NEEDS_DISPATCH_RETAIN_RELEASE
  589. dispatch_retain(parserQueue);
  590. #endif
  591. }
  592. else {
  593. parserQueue = dispatch_queue_create("xmpp.parser", NULL);
  594. }
  595. hasReportedRoot = NO;
  596. depth = 0;
  597. // Create SAX handler
  598. xmlSAXHandler saxHandler;
  599. memset(&saxHandler, 0, sizeof(xmlSAXHandler));
  600. saxHandler.initialized = XML_SAX2_MAGIC;
  601. saxHandler.startElementNs = xmpp_xmlStartElement;
  602. saxHandler.characters = xmpp_xmlCharacters;
  603. saxHandler.endElementNs = xmpp_xmlEndElement;
  604. // Create the push parser context
  605. parserCtxt = xmlCreatePushParserCtxt(&saxHandler, NULL, NULL, 0, NULL);
  606. // Note: This method copies the saxHandler, so we don't have to keep it around.
  607. // Create the document to hold the parsed elements
  608. parserCtxt->myDoc = xmlNewDoc(parserCtxt->version);
  609. // Store reference to ourself
  610. parserCtxt->_private = (__bridge void *)(self);
  611. // Note: The parserCtxt also has a userData variable, but it is used by the DOM building functions.
  612. // If we put a value there, it actually causes a crash!
  613. // We need to be sure to use the _private variable which libxml won't touch.
  614. }
  615. return self;
  616. }
  617. - (void)dealloc
  618. {
  619. if (parserCtxt)
  620. {
  621. // The xmlFreeParserCtxt method will not free the created document in parserCtxt->myDoc.
  622. if (parserCtxt->myDoc)
  623. {
  624. // Free the created xmlDoc
  625. xmlFreeDoc(parserCtxt->myDoc);
  626. }
  627. xmlFreeParserCtxt(parserCtxt);
  628. }
  629. #if NEEDS_DISPATCH_RETAIN_RELEASE
  630. if (delegateQueue)
  631. dispatch_release(delegateQueue);
  632. if (parserQueue)
  633. dispatch_release(parserQueue);
  634. #endif
  635. }
  636. - (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
  637. {
  638. #if NEEDS_DISPATCH_RETAIN_RELEASE
  639. if (newDelegateQueue)
  640. dispatch_retain(newDelegateQueue);
  641. #endif
  642. dispatch_block_t block = ^{
  643. delegate = newDelegate;
  644. #if NEEDS_DISPATCH_RETAIN_RELEASE
  645. if (delegateQueue)
  646. dispatch_release(delegateQueue);
  647. #endif
  648. delegateQueue = newDelegateQueue;
  649. };
  650. if (dispatch_get_current_queue() == parserQueue)
  651. block();
  652. else
  653. dispatch_async(parserQueue, block);
  654. }
  655. - (void)parseData:(NSData *)data
  656. {
  657. dispatch_block_t block = ^{ @autoreleasepool {
  658. int result = xmlParseChunk(parserCtxt, (const char *)[data bytes], (int)[data length], 0);
  659. if (result == 0)
  660. {
  661. if (delegateQueue && [delegate respondsToSelector:@selector(xmppParserDidParseData:)])
  662. {
  663. __strong id theDelegate = delegate;
  664. dispatch_async(delegateQueue, ^{ @autoreleasepool {
  665. [theDelegate xmppParserDidParseData:self];
  666. }});
  667. }
  668. }
  669. else
  670. {
  671. if (delegateQueue && [delegate respondsToSelector:@selector(xmppParser:didFail:)])
  672. {
  673. NSError *error;
  674. xmlError *xmlErr = xmlCtxtGetLastError(parserCtxt);
  675. if (xmlErr->message)
  676. {
  677. NSString *errMsg = [NSString stringWithFormat:@"%s", xmlErr->message];
  678. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  679. error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:info];
  680. }
  681. else
  682. {
  683. error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:nil];
  684. }
  685. __strong id theDelegate = delegate;
  686. dispatch_async(delegateQueue, ^{ @autoreleasepool {
  687. [theDelegate xmppParser:self didFail:error];
  688. }});
  689. }
  690. }
  691. }};
  692. if (dispatch_get_current_queue() == parserQueue)
  693. block();
  694. else
  695. dispatch_async(parserQueue, block);
  696. }
  697. @end