Skip to content
This repository
Browse code

Improving xmppParser and improving parallelization. Adding some advan…

…ced options to xmppStream.
  • Loading branch information...
commit b793e603c3f36be71d297f34623a622e51e2399d 1 parent 9e3171e
Robbie Hanson authored
6 Core/XMPPInternal.h
@@ -53,4 +53,10 @@ extern NSString *const XMPPStreamDidChangeMyJIDNotification;
53 53 **/
54 54 - (void)sendAuthElement:(NSXMLElement *)element;
55 55
  56 +/**
  57 + * This method allows you to inject an element into the stream as if it was received on the socket.
  58 + * This is an advanced technique, but makes for some interesting possibilities.
  59 +**/
  60 +- (void)injectElement:(NSXMLElement *)element;
  61 +
56 62 @end
21 Core/XMPPParser.h
... ... @@ -1,5 +1,4 @@
1 1 #import <Foundation/Foundation.h>
2   -#import <libxml2/libxml/parser.h>
3 2
4 3 #if TARGET_OS_IPHONE
5 4 #import "DDXML.h"
@@ -7,23 +6,15 @@
7 6
8 7
9 8 @interface XMPPParser : NSObject
10   -{
11   - id delegate;
12   -
13   - BOOL hasReportedRoot;
14   - unsigned depth;
15   -
16   - xmlParserCtxt *parserCtxt;
17   -}
18 9
19   -- (id)initWithDelegate:(id)delegate;
  10 +- (id)initWithDelegate:(id)delegate delegateQueue:(dispatch_queue_t)dq;
  11 +- (id)initWithDelegate:(id)delegate delegateQueue:(dispatch_queue_t)dq parserQueue:(dispatch_queue_t)pq;
20 12
21   -- (id)delegate;
22   -- (void)setDelegate:(id)delegate;
  13 +- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
23 14
24 15 /**
25   - * Synchronously parses the given data.
26   - * This means the delegate methods will get called before this method returns.
  16 + * Asynchronously parses the given data.
  17 + * The delegate methods will be dispatch_async'd as events occur.
27 18 **/
28 19 - (void)parseData:(NSData *)data;
29 20
@@ -44,4 +35,6 @@
44 35
45 36 - (void)xmppParser:(XMPPParser *)sender didFail:(NSError *)error;
46 37
  38 +- (void)xmppParserDidParseData:(XMPPParser *)sender;
  39 +
47 40 @end
337 Core/XMPPParser.m
... ... @@ -1,5 +1,6 @@
1 1 #import "XMPPParser.h"
2 2 #import "XMPPLogging.h"
  3 +#import <libxml/parser.h>
3 4 #import <libxml/parserInternals.h>
4 5
5 6 #if TARGET_OS_IPHONE
@@ -10,6 +11,32 @@
10 11 #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
11 12 #endif
12 13
  14 +/**
  15 + * Does ARC support support GCD objects?
  16 + * It does if the minimum deployment target is iOS 6+ or Mac OS X 10.8+
  17 +**/
  18 +#if TARGET_OS_IPHONE
  19 +
  20 + // Compiling for iOS
  21 +
  22 + #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
  23 + #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  24 + #else // iOS 5.X or earlier
  25 + #define NEEDS_DISPATCH_RETAIN_RELEASE 1
  26 + #endif
  27 +
  28 +#else
  29 +
  30 + // Compiling for Mac OS X
  31 +
  32 + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
  33 + #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  34 + #else
  35 + #define NEEDS_DISPATCH_RETAIN_RELEASE 1 // Mac OS X 10.7 or earlier
  36 + #endif
  37 +
  38 +#endif
  39 +
13 40 // Log levels: off, error, warn, info, verbose
14 41 #if DEBUG
15 42 static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE;
@@ -17,24 +44,9 @@
17 44 static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
18 45 #endif
19 46
20   -
21   -// When the xmpp parser invokes a delegate method, such as xmppParser:didReadElement:,
22   -// it exposes itself to the possibility of exceptions mid-parse.
23   -// This aborts the current run loop,
24   -// and thus causes the parser to lose the rest of the data that was passed to it via the parseData method.
25   -//
26   -// The end result is that our parser will likely barf the next time it tries to parse data.
27   -// Probably with a "EndTag: '</' not found" error.
28   -// After this the xmpp stream would be closed.
29   -//
30   -// Now during development, it's probably good to be exposed to these exceptions so they can be tracked down and fixed.
31   -// But for release, we might not want these exceptions to break the xmpp stream.
32   -// So for release mode you may consider enabling the try/catch.
33   -#define USE_TRY_CATCH 0
34   -
35 47 #define CHECK_FOR_NULL(value) \
36 48 do { \
37   - if(value == NULL) { \
  49 + if (value == NULL) { \
38 50 xmpp_xmlAbortDueToMemoryShortage(ctxt); \
39 51 return; \
40 52 } \
@@ -46,6 +58,21 @@
46 58
47 59
48 60 @implementation XMPPParser
  61 +{
  62 + #if __has_feature(objc_arc_weak)
  63 + __weak id delegate;
  64 + #else
  65 + __unsafe_unretained id delegate;
  66 + #endif
  67 + dispatch_queue_t delegateQueue;
  68 +
  69 + dispatch_queue_t parserQueue;
  70 +
  71 + BOOL hasReportedRoot;
  72 + unsigned depth;
  73 +
  74 + xmlParserCtxt *parserCtxt;
  75 +}
49 76
50 77 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51 78 #pragma mark iPhone
@@ -55,7 +82,7 @@ @implementation XMPPParser
55 82
56 83 static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
57 84 {
58   - if([parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
  85 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
59 86 {
60 87 // We first copy the root node.
61 88 // We do this to allow the delegate to retain and make changes to the reported root
@@ -72,22 +99,13 @@ static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
72 99 xmlNodePtr rootCopy = xmlCopyNode(root, 2);
73 100 DDXMLElement *rootCopyWrapper = [DDXMLElement nodeWithElementPrimitive:rootCopy owner:nil];
74 101
75   -#if USE_TRY_CATCH
76   - @try
77   - {
78   - // If the delegate throws an exception that we don't catch,
79   - // this would cause our parser to abort,
80   - // and ignore the rest of the data that was passed to us in parseData.
81   - //
82   - // The end result is that our parser will likely barf the next time it tries to parse data.
83   - // Probably with a "EndTag: '</' not found" error.
  102 + __strong id theDelegate = parser->delegate;
  103 +
  104 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
84 105
85   - [parser->delegate xmppParser:parser didReadRoot:rootCopyWrapper];
86   - }
87   - @catch (id exception) { /* Ignore */ }
88   -#else
89   - [parser->delegate xmppParser:parser didReadRoot:rootCopyWrapper];
90   -#endif
  106 + [theDelegate xmppParser:parser didReadRoot:rootCopyWrapper];
  107 + }});
  108 +
91 109 // Note: DDXMLElement will properly free the rootCopy when it's deallocated.
92 110 }
93 111 }
@@ -110,24 +128,14 @@ static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
110 128 // Note: We want to detach the child from the root even if the delegate method isn't setup.
111 129 // This prevents the doc from growing infinitely large.
112 130
113   - if([parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
  131 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
114 132 {
115   -#if USE_TRY_CATCH
116   - @try
117   - {
118   - // If the delegate throws an exception that we don't catch,
119   - // this would cause our parser to abort,
120   - // and ignore the rest of the data that was passed to us in parseData.
121   - //
122   - // The end result is that our parser will likely barf the next time it tries to parse data.
123   - // Probably with a "EndTag: '</' not found" error.
  133 + __strong id theDelegate = parser->delegate;
  134 +
  135 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
124 136
125   - [parser->delegate xmppParser:parser didReadElement:childWrapper];
126   - }
127   - @catch (id exception) { /* Ignore */ }
128   -#else
129   - [parser->delegate xmppParser:parser didReadElement:childWrapper];
130   -#endif
  137 + [theDelegate xmppParser:parser didReadElement:childWrapper];
  138 + }});
131 139 }
132 140
133 141 // Note: DDXMLElement will properly free the child when it's deallocated.
@@ -143,13 +151,13 @@ static void xmpp_setName(NSXMLElement *element, xmlNodePtr node)
143 151 {
144 152 // Remember: The NSString initWithUTF8String raises an exception if passed NULL
145 153
146   - if(node->name == NULL)
  154 + if (node->name == NULL)
147 155 {
148 156 [element setName:@""];
149 157 return;
150 158 }
151 159
152   - if((node->ns != NULL) && (node->ns->prefix != NULL))
  160 + if ((node->ns != NULL) && (node->ns->prefix != NULL))
153 161 {
154 162 // E.g: <deusty:element xmlns:deusty="deusty.com"/>
155 163
@@ -172,9 +180,9 @@ static void xmpp_addNamespaces(NSXMLElement *element, xmlNodePtr node)
172 180 // Remember: The NSString initWithUTF8String raises an exception if passed NULL
173 181
174 182 xmlNsPtr nsNode = node->nsDef;
175   - while(nsNode != NULL)
  183 + while (nsNode != NULL)
176 184 {
177   - if(nsNode->href == NULL)
  185 + if (nsNode->href == NULL)
178 186 {
179 187 // Namespace doesn't have a value!
180 188 }
@@ -182,7 +190,7 @@ static void xmpp_addNamespaces(NSXMLElement *element, xmlNodePtr node)
182 190 {
183 191 NSXMLNode *ns = [[NSXMLNode alloc] initWithKind:NSXMLNamespaceKind];
184 192
185   - if(nsNode->prefix != NULL)
  193 + if (nsNode->prefix != NULL)
186 194 {
187 195 NSString *nsName = [[NSString alloc] initWithUTF8String:(const char *)nsNode->prefix];
188 196 [ns setName:nsName];
@@ -210,15 +218,15 @@ static void xmpp_addChildren(NSXMLElement *element, xmlNodePtr node)
210 218 // Remember: The NSString initWithUTF8String raises an exception if passed NULL
211 219
212 220 xmlNodePtr childNode = node->children;
213   - while(childNode != NULL)
  221 + while (childNode != NULL)
214 222 {
215   - if(childNode->type == XML_ELEMENT_NODE)
  223 + if (childNode->type == XML_ELEMENT_NODE)
216 224 {
217 225 xmpp_recursiveAddChild(element, childNode);
218 226 }
219   - else if(childNode->type == XML_TEXT_NODE)
  227 + else if (childNode->type == XML_TEXT_NODE)
220 228 {
221   - if(childNode->content != NULL)
  229 + if (childNode->content != NULL)
222 230 {
223 231 NSString *value = [[NSString alloc] initWithUTF8String:(const char *)childNode->content];
224 232 [element setStringValue:value];
@@ -234,17 +242,17 @@ static void xmpp_addAttributes(NSXMLElement *element, xmlNodePtr node)
234 242 // Remember: The NSString initWithUTF8String raises an exception if passed NULL
235 243
236 244 xmlAttrPtr attrNode = node->properties;
237   - while(attrNode != NULL)
  245 + while (attrNode != NULL)
238 246 {
239   - if(attrNode->name == NULL)
  247 + if (attrNode->name == NULL)
240 248 {
241 249 // Attribute doesn't have a name!
242 250 }
243   - else if(attrNode->children == NULL)
  251 + else if (attrNode->children == NULL)
244 252 {
245 253 // Attribute doesn't have a value node!
246 254 }
247   - else if(attrNode->children->content == NULL)
  255 + else if (attrNode->children->content == NULL)
248 256 {
249 257 // Attribute doesn't have a value!
250 258 }
@@ -252,7 +260,7 @@ static void xmpp_addAttributes(NSXMLElement *element, xmlNodePtr node)
252 260 {
253 261 NSXMLNode *attr = [[NSXMLNode alloc] initWithKind:NSXMLAttributeKind];
254 262
255   - if((attrNode->ns != NULL) && (attrNode->ns->prefix != NULL))
  263 + if ((attrNode->ns != NULL) && (attrNode->ns->prefix != NULL))
256 264 {
257 265 // E.g: <element xmlns:deusty="deusty.com" deusty:attr="value"/>
258 266
@@ -323,51 +331,31 @@ static void xmpp_recursiveAddChild(NSXMLElement *parent, xmlNodePtr childNode)
323 331
324 332 static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
325 333 {
326   - if([parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
  334 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
327 335 {
328 336 NSXMLElement *nsRoot = xmpp_nsxmlFromLibxml(root);
329 337
330   -#if USE_TRY_CATCH
331   - @try
332   - {
333   - // If the delegate throws an exception that we don't catch,
334   - // this would cause our parser to abort,
335   - // and ignore the rest of the data that was passed to us in parseData.
336   - //
337   - // The end result is that our parser will likely barf the next time it tries to parse data.
338   - // Probably with a "EndTag: '</' not found" error.
  338 + __strong id delegate = parser->delegate;
  339 +
  340 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
339 341
340   - [parser->delegate xmppParser:parser didReadRoot:nsRoot];
341   - }
342   - @catch (id exception) { /* Ignore */ }
343   -#else
344   - [parser->delegate xmppParser:parser didReadRoot:nsRoot];
345   -#endif
  342 + [theDelegate xmppParser:parser didReadRoot:nsRoot];
  343 + }});
346 344 }
347 345 }
348 346
349 347 static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
350 348 {
351   - if([parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
  349 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
352 350 {
353 351 NSXMLElement *nsChild = xmpp_nsxmlFromLibxml(child);
354 352
355   -#if USE_TRY_CATCH
356   - @try
357   - {
358   - // If the delegate throws an exception that we don't catch,
359   - // this would cause our parser to abort,
360   - // and ignore the rest of the data that was passed to us in parseData.
361   - //
362   - // The end result is that our parser will likely barf the next time it tries to parse data.
363   - // Probably with a "EndTag: '</' not found" error.
364   -
365   - [parser->delegate xmppParser:parser didReadElement:nsChild];
366   - }
367   - @catch (id exception) { /* Ignore */ }
368   -#else
369   - [parser->delegate xmppParser:parser didReadElement:nsChild];
370   -#endif
  353 + __strong id theDelegate = parser->delegate;
  354 +
  355 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  356 +
  357 + [theDelegate xmppParser:parser didReadElement:nsChild];
  358 + }});
371 359 }
372 360
373 361 // Note: We want to detach the child from the root even if the delegate method isn't setup.
@@ -393,14 +381,14 @@ static void xmpp_postStartElement(xmlParserCtxt *ctxt)
393 381 XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
394 382 parser->depth++;
395 383
396   - if(!(parser->hasReportedRoot) && (parser->depth == 1))
  384 + if (!(parser->hasReportedRoot) && (parser->depth == 1))
397 385 {
398 386 // We've received the full root - report it to the delegate
399 387
400   - if(ctxt->myDoc)
  388 + if (ctxt->myDoc)
401 389 {
402 390 xmlNodePtr root = xmlDocGetRootElement(ctxt->myDoc);
403   - if(root)
  391 + if (root)
404 392 {
405 393 xmpp_onDidReadRoot(parser, root);
406 394
@@ -419,7 +407,7 @@ static void xmpp_postEndElement(xmlParserCtxt *ctxt)
419 407 XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
420 408 parser->depth--;
421 409
422   - if(parser->depth == 1)
  410 + if (parser->depth == 1)
423 411 {
424 412 // End of full xmpp element.
425 413 // That is, a child of the root element.
@@ -429,9 +417,9 @@ static void xmpp_postEndElement(xmlParserCtxt *ctxt)
429 417 xmlNodePtr root = xmlDocGetRootElement(doc);
430 418
431 419 xmlNodePtr child = root->children;
432   - while(child != NULL)
  420 + while (child != NULL)
433 421 {
434   - if(child->type == XML_ELEMENT_NODE)
  422 + if (child->type == XML_ELEMENT_NODE)
435 423 {
436 424 xmpp_onDidReadElement(parser, child);
437 425
@@ -442,13 +430,18 @@ static void xmpp_postEndElement(xmlParserCtxt *ctxt)
442 430 child = child->next;
443 431 }
444 432 }
445   - else if(parser->depth == 0)
  433 + else if (parser->depth == 0)
446 434 {
447 435 // End of the root element
448 436
449   - if([parser->delegate respondsToSelector:@selector(xmppParserDidEnd:)])
  437 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParserDidEnd:)])
450 438 {
451   - [parser->delegate xmppParserDidEnd:parser];
  439 + __strong id theDelegate = parser->delegate;
  440 +
  441 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  442 +
  443 + [theDelegate xmppParserDidEnd:parser];
  444 + }});
452 445 }
453 446 }
454 447 }
@@ -462,14 +455,19 @@ static void xmpp_xmlAbortDueToMemoryShortage(xmlParserCtxt *ctxt)
462 455
463 456 xmlStopParser(ctxt);
464 457
465   - if([parser->delegate respondsToSelector:@selector(xmppParser:didFail:)])
  458 + if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didFail:)])
466 459 {
467 460 NSString *errMsg = @"Unable to allocate memory in xmpp parser";
468 461 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
469 462
470 463 NSError *error = [NSError errorWithDomain:@"libxmlErrorDomain" code:1001 userInfo:info];
471 464
472   - [parser->delegate xmppParser:parser didFail:error];
  465 + __strong id theDelegate = parser->delegate;
  466 +
  467 + dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
  468 +
  469 + [theDelegate xmppParser:parser didFail:error];
  470 + }});
473 471 }
474 472 }
475 473
@@ -544,7 +542,7 @@ static void xmpp_xmlStartElement(void *ctx, const xmlChar *nodeName,
544 542 CHECK_FOR_NULL(newNode);
545 543
546 544 // Add the node to the tree
547   - if(parent == NULL)
  545 + if (parent == NULL)
548 546 {
549 547 // Root node
550 548 xmlAddChild((xmlNodePtr)ctxt->myDoc, newNode);
@@ -715,11 +713,33 @@ static void xmpp_xmlEndElement(void *ctx, const xmlChar *localname,
715 713 xmpp_postEndElement(ctxt);
716 714 }
717 715
718   -- (id)initWithDelegate:(id)aDelegate
  716 +- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
  717 +{
  718 + return [self initWithDelegate:aDelegate delegateQueue:dq parserQueue:NULL];
  719 +}
  720 +
  721 +- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq parserQueue:(dispatch_queue_t)pq
719 722 {
720 723 if ((self = [super init]))
721 724 {
722 725 delegate = aDelegate;
  726 + delegateQueue = dq;
  727 +
  728 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  729 + if (delegateQueue)
  730 + dispatch_retain(delegateQueue);
  731 + #endif
  732 +
  733 + if (pq) {
  734 + parserQueue = pq;
  735 +
  736 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  737 + dispatch_retain(parserQueue);
  738 + #endif
  739 + }
  740 + else {
  741 + parserQueue = dispatch_queue_create("xmpp.parser", NULL);
  742 + }
723 743
724 744 hasReportedRoot = NO;
725 745 depth = 0;
@@ -756,7 +776,6 @@ - (void)dealloc
756 776 if (parserCtxt)
757 777 {
758 778 // The xmlFreeParserCtxt method will not free the created document in parserCtxt->myDoc.
759   -
760 779 if (parserCtxt->myDoc)
761 780 {
762 781 // Free the created xmlDoc
@@ -766,51 +785,91 @@ - (void)dealloc
766 785 xmlFreeParserCtxt(parserCtxt);
767 786 }
768 787
  788 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  789 + if (delegateQueue)
  790 + dispatch_release(delegateQueue);
  791 + if (parserQueue)
  792 + dispatch_release(parserQueue);
  793 + #endif
769 794 }
770 795
771   -- (id)delegate {
772   - return delegate;
773   -}
774   -- (void)setDelegate:(id)aDelegate {
775   - delegate = aDelegate;
  796 +- (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
  797 +{
  798 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  799 + if (newDelegateQueue)
  800 + dispatch_retain(newDelegateQueue);
  801 + #endif
  802 +
  803 + dispatch_block_t block = ^{
  804 +
  805 + delegate = newDelegate;
  806 +
  807 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  808 + if (delegateQueue)
  809 + dispatch_release(delegateQueue);
  810 + #endif
  811 +
  812 + delegateQueue = newDelegateQueue;
  813 + };
  814 +
  815 + if (dispatch_get_current_queue() == parserQueue)
  816 + block();
  817 + else
  818 + dispatch_async(parserQueue, block);
776 819 }
777 820
778 821 - (void)parseData:(NSData *)data
779 822 {
780   - // The xmlParseChunk method below will cause the delegate methods to be invoked before this method returns.
781   - // If the delegate subsequently attempts to release us in one of those methods, and our dealloc method
782   - // gets invoked, then the parserCtxt will be freed in the middle of the xmlParseChunk method.
783   - // This often has the effect of crashing the application.
784   - // To get around this problem we simply retain/release within the method.
785   - XMPPParser *selfRetain = self;
786   -
787   - int result = xmlParseChunk(parserCtxt, (const char *)[data bytes], (int)[data length], 0);
  823 + dispatch_block_t block = ^{ @autoreleasepool {
788 824
789   - if(result != 0)
790   - {
791   - if([delegate respondsToSelector:@selector(xmppParser:didFail:)])
  825 + int result = xmlParseChunk(parserCtxt, (const char *)[data bytes], (int)[data length], 0);
  826 +
  827 + if (result == 0)
792 828 {
793   - NSError *error;
794   -
795   - xmlError *xmlErr = xmlCtxtGetLastError(parserCtxt);
796   -
797   - if(xmlErr->message)
  829 + if (delegateQueue && [delegate respondsToSelector:@selector(xmppParserDidParseData:)])
798 830 {
799   - NSString *errMsg = [NSString stringWithFormat:@"%s", xmlErr->message];
800   - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  831 + __strong id theDelegate = delegate;
801 832
802   - error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:info];
  833 + dispatch_async(delegateQueue, ^{ @autoreleasepool {
  834 +
  835 + [theDelegate xmppParserDidParseData:self];
  836 + }});
803 837 }
804   - else
  838 + }
  839 + else
  840 + {
  841 + if (delegateQueue && [delegate respondsToSelector:@selector(xmppParser:didFail:)])
805 842 {
806   - error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:nil];
  843 + NSError *error;
  844 +
  845 + xmlError *xmlErr = xmlCtxtGetLastError(parserCtxt);
  846 +
  847 + if (xmlErr->message)
  848 + {
  849 + NSString *errMsg = [NSString stringWithFormat:@"%s", xmlErr->message];
  850 + NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  851 +
  852 + error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:info];
  853 + }
  854 + else
  855 + {
  856 + error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:nil];
  857 + }
  858 +
  859 + __strong id theDelegate = delegate;
  860 +
  861 + dispatch_async(delegateQueue, ^{ @autoreleasepool {
  862 +
  863 + [theDelegate xmppParser:self didFail:error];
  864 + }});
807 865 }
808   -
809   - [delegate xmppParser:self didFail:error];
810 866 }
811   - }
  867 + }};
812 868
813   - selfRetain = nil;
  869 + if (dispatch_get_current_queue() == parserQueue)
  870 + block();
  871 + else
  872 + dispatch_async(parserQueue, block);
814 873 }
815 874
816 875 @end
25 Core/XMPPStream.h
@@ -766,6 +766,31 @@ typedef enum XMPPStreamErrorCode XMPPStreamErrorCode;
766 766 - (NSString *)xmppStream:(XMPPStream *)sender alternativeResourceForConflictingResource:(NSString *)conflictingResource;
767 767
768 768 /**
  769 + * These methods are called before their respective XML elements are broadcast as received to the rest of the stack.
  770 + * These methods can be used to modify elements on the fly.
  771 + * (E.g. perform custom decryption so the rest of the stack sees readable text.)
  772 + *
  773 + * You may also filter incoming elements by returning nil.
  774 + *
  775 + * When implementing these methods to modify the element, you do not need to copy the given element.
  776 + * You can simply edit the given element, and return it.
  777 + * The reason these methods return an element, instead of void, is to allow filtering.
  778 + *
  779 + * Concerning thread-safety, delegates implementing the method are invoked one-at-a-time to
  780 + * allow thread-safe modification of the given elements.
  781 + *
  782 + * You should NOT implement these methods unless you have good reason to do so.
  783 + * For general processing and notification of received elements, please use xmppStream:didReceiveX: methods.
  784 + *
  785 + * @see xmppStream:didReceiveIQ:
  786 + * @see xmppStream:didReceiveMessage:
  787 + * @see xmppStream:didReceivePresence:
  788 +**/
  789 +- (XMPPIQ *)xmppStream:(XMPPStream *)sender willReceiveIQ:(XMPPIQ *)iq;
  790 +- (XMPPMessage *)xmppStream:(XMPPStream *)sender willReceiveMessage:(XMPPMessage *)message;
  791 +- (XMPPPresence *)xmppStream:(XMPPStream *)sender willReceivePresence:(XMPPPresence *)presence;
  792 +
  793 +/**
769 794 * These methods are called after their respective XML elements are received on the stream.
770 795 *
771 796 * In the case of an IQ, the delegate method should return YES if it has or will respond to the given IQ.
859 Core/XMPPStream.m
@@ -50,12 +50,6 @@
50 50 static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
51 51 #endif
52 52
53   -#if TARGET_OS_IPHONE
54   - #define SOCKET_BUFFER_SIZE 512 // bytes
55   -#else
56   - #define SOCKET_BUFFER_SIZE 1024 // bytes
57   -#endif
58   -
59 53 /**
60 54 * Seeing a return statements within an inner block
61 55 * can sometimes be mistaken for a return point of the enclosing method.
@@ -103,18 +97,22 @@
103 97 @interface XMPPStream ()
104 98 {
105 99 dispatch_queue_t xmppQueue;
106   - dispatch_queue_t parserQueue;
107 100
108 101 dispatch_queue_t willSendIqQueue;
109 102 dispatch_queue_t willSendMessageQueue;
110 103 dispatch_queue_t willSendPresenceQueue;
111 104
  105 + dispatch_queue_t willReceiveIqQueue;
  106 + dispatch_queue_t willReceiveMessageQueue;
  107 + dispatch_queue_t willReceivePresenceQueue;
  108 +
  109 + dispatch_queue_t didReceiveIqQueue;
  110 +
112 111 GCDMulticastDelegate <XMPPStreamDelegate> *multicastDelegate;
113 112
114 113 int state;
115 114
116 115 GCDAsyncSocket *asyncSocket;
117   - NSMutableData *socketBuffer;
118 116
119 117 UInt64 numberOfBytesSent;
120 118 UInt64 numberOfBytesReceived;
@@ -192,12 +190,17 @@ @implementation XMPPStream
192 190 - (void)commonInit
193 191 {
194 192 xmppQueue = dispatch_queue_create("xmpp", NULL);
195   - parserQueue = dispatch_queue_create("xmpp.parser", NULL);
196 193
197 194 willSendIqQueue = dispatch_queue_create("xmpp.willSendIq", NULL);
198 195 willSendMessageQueue = dispatch_queue_create("xmpp.willSendMessage", NULL);
199 196 willSendPresenceQueue = dispatch_queue_create("xmpp.willSendPresence", NULL);
200 197
  198 + willReceiveIqQueue = dispatch_queue_create("xmpp.willReceiveIq", NULL);
  199 + willReceiveMessageQueue = dispatch_queue_create("xmpp.willReceiveMessage", NULL);
  200 + willReceivePresenceQueue = dispatch_queue_create("xmpp.willReceivePresence", NULL);
  201 +
  202 + didReceiveIqQueue = dispatch_queue_create("xmpp.didReceiveIq", NULL);
  203 +
201 204 multicastDelegate = (GCDMulticastDelegate <XMPPStreamDelegate> *)[[GCDMulticastDelegate alloc] init];
202 205
203 206 state = STATE_XMPP_DISCONNECTED;
@@ -208,7 +211,7 @@ - (void)commonInit
208 211 numberOfBytesSent = 0;
209 212 numberOfBytesReceived = 0;
210 213
211   - parser = [[XMPPParser alloc] initWithDelegate:self];
  214 + parser = [[XMPPParser alloc] initWithDelegate:self delegateQueue:xmppQueue];
212 215
213 216 hostPort = 5222;
214 217 keepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
@@ -274,16 +277,19 @@ - (void)dealloc
274 277 {
275 278 #if NEEDS_DISPATCH_RETAIN_RELEASE
276 279 dispatch_release(xmppQueue);
277   - dispatch_release(parserQueue);
278 280 dispatch_release(willSendIqQueue);
279 281 dispatch_release(willSendMessageQueue);
280 282 dispatch_release(willSendPresenceQueue);
  283 + dispatch_release(willReceiveIqQueue);
  284 + dispatch_release(willReceiveMessageQueue);
  285 + dispatch_release(willReceivePresenceQueue);
  286 + dispatch_release(didReceiveIqQueue);
281 287 #endif
282 288
283 289 [asyncSocket setDelegate:nil delegateQueue:NULL];
284 290 [asyncSocket disconnect];
285 291
286   - [parser setDelegate:nil];
  292 + [parser setDelegate:nil delegateQueue:NULL];
287 293
288 294 if (keepAliveTimer)
289 295 {
@@ -2280,6 +2286,337 @@ - (void)sendAuthElement:(NSXMLElement *)element
2280 2286 dispatch_async(xmppQueue, block);
2281 2287 }
2282 2288
  2289 +- (void)receiveIQ:(XMPPIQ *)iq
  2290 +{
  2291 + NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  2292 + NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2293 +
  2294 + // We're getting ready to receive an IQ.
  2295 + // Notify delegates to allow them to optionally alter/filter the incoming IQ element.
  2296 +
  2297 + SEL selector = @selector(xmppStream:willReceiveIQ:);
  2298 +
  2299 + if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2300 + {
  2301 + // None of the delegates implement the method.
  2302 + // Use a shortcut.
  2303 +
  2304 + [self continueReceiveIQ:iq];
  2305 + }
  2306 + else
  2307 + {
  2308 + // Notify all interested delegates.
  2309 + // This must be done serially to allow them to alter the element in a thread-safe manner.
  2310 +
  2311 + GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2312 +
  2313 + dispatch_async(willReceiveIqQueue, ^{ @autoreleasepool {
  2314 +
  2315 + // Allow delegates to modify and/or filter incoming element
  2316 +
  2317 + __block XMPPIQ *modifiedIQ = iq;
  2318 +
  2319 + id del;
  2320 + dispatch_queue_t dq;
  2321 +
  2322 + while (modifiedIQ && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2323 + {
  2324 + dispatch_sync(dq, ^{ @autoreleasepool {
  2325 +
  2326 + modifiedIQ = [del xmppStream:self willReceiveIQ:modifiedIQ];
  2327 +
  2328 + }});
  2329 + }
  2330 +
  2331 + if (modifiedIQ)
  2332 + {
  2333 + dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2334 +
  2335 + if (state == STATE_XMPP_CONNECTED) {
  2336 + [self continueReceiveIQ:modifiedIQ];
  2337 + }
  2338 + }});
  2339 + }
  2340 + }});
  2341 + }
  2342 +}
  2343 +
  2344 +- (void)receiveMessage:(XMPPMessage *)message
  2345 +{
  2346 + NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  2347 + NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2348 +
  2349 + // We're getting ready to receive a message.
  2350 + // Notify delegates to allow them to optionally alter/filter the incoming message.
  2351 +
  2352 + SEL selector = @selector(xmppStream:willReceiveMessage:);
  2353 +
  2354 + if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2355 + {
  2356 + // None of the delegates implement the method.
  2357 + // Use a shortcut.
  2358 +
  2359 + [self continueReceiveMessage:message];
  2360 + }
  2361 + else
  2362 + {
  2363 + // Notify all interested delegates.
  2364 + // This must be done serially to allow them to alter the element in a thread-safe manner.
  2365 +
  2366 + GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2367 +
  2368 + dispatch_async(willReceiveMessageQueue, ^{ @autoreleasepool {
  2369 +
  2370 + // Allow delegates to modify incoming element
  2371 +
  2372 + __block XMPPMessage *modifiedMessage = message;
  2373 +
  2374 + id del;
  2375 + dispatch_queue_t dq;
  2376 +
  2377 + while (modifiedMessage && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2378 + {
  2379 + dispatch_sync(dq, ^{ @autoreleasepool {
  2380 +
  2381 + modifiedMessage = [del xmppStream:self willReceiveMessage:modifiedMessage];
  2382 +
  2383 + }});
  2384 + }
  2385 +
  2386 + if (modifiedMessage)
  2387 + {
  2388 + dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2389 +
  2390 + if (state == STATE_XMPP_CONNECTED) {
  2391 + [self continueReceiveMessage:modifiedMessage];
  2392 + }
  2393 + }});
  2394 + }
  2395 + }});
  2396 + }
  2397 +}
  2398 +
  2399 +- (void)receivePresence:(XMPPPresence *)presence
  2400 +{
  2401 + NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  2402 + NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2403 +
  2404 + // We're getting ready to receive a presence element.
  2405 + // Notify delegates to allow them to optionally alter/filter the incoming presence.
  2406 +
  2407 + SEL selector = @selector(xmppStream:willReceivePresence:);
  2408 +
  2409 + if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2410 + {
  2411 + // None of the delegates implement the method.
  2412 + // Use a shortcut.
  2413 +
  2414 + [self continueReceivePresence:presence];
  2415 + }
  2416 + else
  2417 + {
  2418 + // Notify all interested delegates.
  2419 + // This must be done serially to allow them to alter the element in a thread-safe manner.
  2420 +
  2421 + GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2422 +
  2423 + dispatch_async(willSendPresenceQueue, ^{ @autoreleasepool {
  2424 +
  2425 + // Allow delegates to modify outgoing element
  2426 +
  2427 + __block XMPPPresence *modifiedPresence = presence;
  2428 +
  2429 + id del;
  2430 + dispatch_queue_t dq;
  2431 +
  2432 + while (modifiedPresence && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2433 + {
  2434 + dispatch_sync(dq, ^{ @autoreleasepool {
  2435 +
  2436 + modifiedPresence = [del xmppStream:self willReceivePresence:modifiedPresence];
  2437 +
  2438 + }});
  2439 + }
  2440 +
  2441 + if (modifiedPresence)
  2442 + {
  2443 + dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2444 +
  2445 + if (state == STATE_XMPP_CONNECTED) {
  2446 + [self continueReceivePresence:presence];
  2447 + }
  2448 + }});
  2449 + }
  2450 + }});
  2451 + }
  2452 +}
  2453 +
  2454 +- (void)continueReceiveIQ:(XMPPIQ *)iq
  2455 +{
  2456 + if ([iq requiresResponse])
  2457 + {
  2458 + // As per the XMPP specificiation, if the IQ requires a response,
  2459 + // and we don't have any delegates or modules that can properly respond to the IQ,
  2460 + // we MUST send back and error IQ.
  2461 + //
  2462 + // So we notifiy all interested delegates and modules about the received IQ,
  2463 + // keeping track of whether or not any of them have handled it.
  2464 +
  2465 + GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2466 +
  2467 + id del;
  2468 + dispatch_queue_t dq;
  2469 +
  2470 + SEL selector = @selector(xmppStream:didReceiveIQ:);
  2471 +
  2472 + dispatch_semaphore_t delSemaphore = dispatch_semaphore_create(0);
  2473 + dispatch_group_t delGroup = dispatch_group_create();
  2474 +
  2475 + while ([delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2476 + {
  2477 + dispatch_group_async(delGroup, dq, ^{ @autoreleasepool {
  2478 +
  2479 + if ([del xmppStream:self didReceiveIQ:iq])
  2480 + {
  2481 + dispatch_semaphore_signal(delSemaphore);
  2482 + }
  2483 +
  2484 + }});
  2485 + }
  2486 +
  2487 + dispatch_async(didReceiveIqQueue, ^{ @autoreleasepool {
  2488 +
  2489 + dispatch_group_wait(delGroup, DISPATCH_TIME_FOREVER);
  2490 +
  2491 + // Did any of the delegates handle the IQ? (handle == will response)
  2492 +
  2493 + BOOL handled = (dispatch_semaphore_wait(delSemaphore, DISPATCH_TIME_NOW) == 0);
  2494 +
  2495 + // An entity that receives an IQ request of type "get" or "set" MUST reply
  2496 + // with an IQ response of type "result" or "error".
  2497 + //
  2498 + // The response MUST preserve the 'id' attribute of the request.
  2499 +
  2500 + if (!handled)
  2501 + {
  2502 + // Return error message:
  2503 + //
  2504 + // <iq to="jid" type="error" id="id">
  2505 + // <query xmlns="ns"/>
  2506 + // <error type="cancel" code="501">
  2507 + // <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
  2508 + // </error>
  2509 + // </iq>
  2510 +
  2511 + NSXMLElement *reason = [NSXMLElement elementWithName:@"feature-not-implemented"
  2512 + xmlns:@"urn:ietf:params:xml:ns:xmpp-stanzas"];
  2513 +
  2514 + NSXMLElement *error = [NSXMLElement elementWithName:@"error"];
  2515 + [error addAttributeWithName:@"type" stringValue:@"cancel"];
  2516 + [error addAttributeWithName:@"code" stringValue:@"501"];
  2517 + [error addChild:reason];
  2518 +
  2519 + XMPPIQ *iqResponse = [XMPPIQ iqWithType:@"error"
  2520 + to:[iq from]
  2521 + elementID:[iq elementID]
  2522 + child:error];
  2523 +
  2524 + NSXMLElement *iqChild = [iq childElement];
  2525 + if (iqChild)
  2526 + {
  2527 + NSXMLNode *iqChildCopy = [iqChild copy];
  2528 + [iqResponse insertChild:iqChildCopy atIndex:0];
  2529 + }
  2530 +
  2531 + // Purposefully go through the sendElement: method
  2532 + // so that it gets dispatched onto the xmppQueue,
  2533 + // and so that modules may get notified of the outgoing error message.
  2534 +
  2535 + [self sendElement:iqResponse];
  2536 + }
  2537 +
  2538 + #if NEEDS_DISPATCH_RETAIN_RELEASE
  2539 + dispatch_release(delSemaphore);
  2540 + dispatch_release(delGroup);
  2541 + #endif
  2542 +
  2543 + }});
  2544 + }
  2545 + else
  2546 + {
  2547 + // The IQ doesn't require a response.
  2548 + // So we can just fire the delegate method and ignore the responses.
  2549 +
  2550 + [multicastDelegate xmppStream:self didReceiveIQ:iq];
  2551 + }
  2552 +}
  2553 +
  2554 +- (void)continueReceiveMessage:(XMPPMessage *)message
  2555 +{
  2556 + [multicastDelegate xmppStream:self didReceiveMessage:message];
  2557 +}
  2558 +
  2559 +- (void)continueReceivePresence:(XMPPPresence *)presence
  2560 +{
  2561 + [multicastDelegate xmppStream:self didReceivePresence:presence];
  2562 +}
  2563 +
  2564 +/**
  2565 + * This method allows you to inject an element into the stream as if it was received on the socket.
  2566 + * This is an advanced technique, but makes for some interesting possibilities.
  2567 +**/
  2568 +- (void)injectElement:(NSXMLElement *)element
  2569 +{
  2570 + if (element == nil) return;
  2571 +
  2572 + dispatch_block_t block = ^{ @autoreleasepool {
  2573 +
  2574 + if (state != STATE_XMPP_CONNECTED)
  2575 + {
  2576 + return_from_block;
  2577 + }
  2578 +
  2579 + if ([element isKindOfClass:[XMPPIQ class]])
  2580 + {
  2581 + [self receiveIQ:(XMPPIQ *)element];
  2582 + }
  2583 + else if ([element isKindOfClass:[XMPPMessage class]])
  2584 + {
  2585 + [self receiveMessage:(XMPPMessage *)element];
  2586 + }
  2587 + else if ([element isKindOfClass:[XMPPPresence class]])
  2588 + {
  2589 + [self receivePresence:(XMPPPresence *)element];
  2590 + }
  2591 + else
  2592 + {
  2593 + NSString *elementName = [element name];
  2594 +
  2595 + if ([elementName isEqualToString:@"iq"])
  2596 + {
  2597 + [self receiveIQ:[XMPPIQ iqFromElement:element]];
  2598 + }
  2599 + else if ([elementName isEqualToString:@"message"])
  2600 + {
  2601 + [self receiveMessage:[XMPPMessage messageFromElement:element]];
  2602 + }
  2603 + else if ([elementName isEqualToString:@"presence"])
  2604 + {
  2605 + [self receivePresence:[XMPPPresence presenceFromElement:element]];
  2606 + }
  2607 + else
  2608 + {
  2609 + [multicastDelegate xmppStream:self didReceiveError:element];
  2610 + }
  2611 + }
  2612 + }};
  2613 +
  2614 + if (dispatch_get_current_queue() == xmppQueue)
  2615 + block();
  2616 + else
  2617 + dispatch_async(xmppQueue, block);
  2618 +}
  2619 +
2283 2620 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2284 2621 #pragma mark Stream Negotiation
2285 2622 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2300,18 +2637,8 @@ - (void)startNegotiation
2300 2637 // Inform delegate that the TCP connection is open, and the stream handshake has begun
2301 2638 [multicastDelegate xmppStreamDidStartNegotiation:self];
2302 2639
2303   - // Initialize socket buffer
2304   - if (socketBuffer == nil)
2305   - {
2306   - socketBuffer = [[NSMutableData alloc] initWithLength:SOCKET_BUFFER_SIZE];
2307   - }
2308   -
2309 2640 // And start reading in the server's XML stream
2310   - [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START
2311   - buffer:socketBuffer
2312   - bufferOffset:0
2313   - maxLength:[socketBuffer length]
2314   - tag:TAG_XMPP_READ_START];
  2641 + [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
2315 2642 }
2316 2643
2317 2644 /**
@@ -2345,16 +2672,16 @@ - (void)sendOpeningNegotiation
2345 2672 XMPPLogVerbose(@"%@: Resetting parser...", THIS_FILE);
2346 2673
2347 2674 // We're restarting our negotiation, so we need to reset the parser.
2348   - [parser setDelegate:nil];
  2675 + [parser setDelegate:nil delegateQueue:NULL];
2349 2676
2350   - parser = [(XMPPParser *)[XMPPParser alloc] initWithDelegate:self];
  2677 + parser = [[XMPPParser alloc] initWithDelegate:self delegateQueue:xmppQueue];
2351 2678 }
2352 2679 else if (parser == nil)
2353 2680 {
2354 2681 XMPPLogVerbose(@"%@: Initializing parser...", THIS_FILE);
2355 2682
2356 2683 // Need to create parser (it was destroyed when the socket was last disconnected)
2357   - parser = [(XMPPParser *)[XMPPParser alloc] initWithDelegate:self];
  2684 + parser = [[XMPPParser alloc] initWithDelegate:self delegateQueue:NULL];
2358 2685 }
2359 2686 else
2360 2687 {
@@ -2504,11 +2831,7 @@ - (void)continueStartTLS:(NSMutableDictionary *)settings
2504 2831
2505 2832 // We paused reading from the socket.
2506 2833 // We're ready to continue now.
2507   - [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM
2508   - buffer:socketBuffer
2509   - bufferOffset:0
2510   - maxLength:[socketBuffer length]
2511   - tag:TAG_XMPP_READ_STREAM];
  2834 + [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
2512 2835 }
2513 2836 else
2514 2837 {
@@ -3094,43 +3417,30 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t
3094 3417 XMPPLogTrace();
3095 3418
3096 3419 lastSendReceiveTime = [NSDate timeIntervalSinceReferenceDate];
  3420 + numberOfBytesReceived += [data length];
3097 3421
3098   - if (XMPP_LOG_RECV_PRE)
3099   - {
3100   - NSString *dataAsStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
3101   -
3102   - XMPPLogRecvPre(@"RECV: %@", dataAsStr);
3103   - }
  3422 + XMPPLogRecvPre(@"RECV: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
3104 3423
3105   - numberOfBytesReceived += [data length];
  3424 + // Asynchronously parse the xml data
  3425 + [parser parseData:data];
3106 3426
3107   - dispatch_async(parserQueue, ^{ @autoreleasepool {
3108   -
3109   - [parser parseData:data];
3110   -
3111   - dispatch_async(xmppQueue, ^{ @autoreleasepool {
3112   -
3113   - // Continue reading for XML elements.
3114   -
3115   - if (state == STATE_XMPP_OPENING)
3116   - {
3117   - [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START
3118   - buffer:socketBuffer
3119   - bufferOffset:0
3120   - maxLength:[socketBuffer length]
3121   - tag:TAG_XMPP_READ_START];
3122   - }
3123   - else if (state != STATE_XMPP_STARTTLS_2)
3124   - {
3125   - [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM
3126   - buffer:socketBuffer
3127   - bufferOffset:0
3128   - maxLength:[socketBuffer length]
3129   - tag:TAG_XMPP_READ_STREAM];
3130   - }
3131   -
3132   - }});
3133   - }});
  3427 + if ([self isSecure])
  3428 + {
  3429 + // Continue reading for XML elements
  3430 + if (state == STATE_XMPP_OPENING)
  3431 + {
  3432 + [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
  3433 + }
  3434 + else if (state != STATE_XMPP_STARTTLS_2)
  3435 + {
  3436 + [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
  3437 + }
  3438 + }
  3439 + else
  3440 + {
  3441 + // Don't queue up a read on the socket as we may need to upgrade to TLS.
  3442 + // We'll read more data after we've parsed the current chunk of data.
  3443 + }
3134 3444 }
3135 3445
3136 3446 /**
@@ -3176,11 +3486,8 @@ - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
3176 3486 // Update state
3177 3487 state = STATE_XMPP_DISCONNECTED;