PageRenderTime 68ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/External/XMPPFramework/Core/XMPPStream.m

https://gitlab.com/intelij/ChattAR-ios
Objective C | 4676 lines | 3039 code | 1065 blank | 572 comment | 479 complexity | 62ca245eb1fb8372829e404e05ccf434 MD5 | raw file
  1. #import "XMPP.h"
  2. #import "XMPPParser.h"
  3. #import "XMPPLogging.h"
  4. #import "XMPPInternal.h"
  5. #import "XMPPSRVResolver.h"
  6. #import "NSData+XMPP.h"
  7. #import <objc/runtime.h>
  8. #import <libkern/OSAtomic.h>
  9. #if TARGET_OS_IPHONE
  10. // Note: You may need to add the CFNetwork Framework to your project
  11. #import <CFNetwork/CFNetwork.h>
  12. #endif
  13. #if ! __has_feature(objc_arc)
  14. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  15. #endif
  16. // Log levels: off, error, warn, info, verbose
  17. #if DEBUG
  18. static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO | XMPP_LOG_FLAG_SEND_RECV; // | XMPP_LOG_FLAG_TRACE;
  19. #else
  20. static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
  21. #endif
  22. /**
  23. * Seeing a return statements within an inner block
  24. * can sometimes be mistaken for a return point of the enclosing method.
  25. * This makes inline blocks a bit easier to read.
  26. **/
  27. #define return_from_block return
  28. // Define the timeouts (in seconds) for retreiving various parts of the XML stream
  29. #define TIMEOUT_XMPP_WRITE -1
  30. #define TIMEOUT_XMPP_READ_START 10
  31. #define TIMEOUT_XMPP_READ_STREAM -1
  32. // Define the tags we'll use to differentiate what it is we're currently reading or writing
  33. #define TAG_XMPP_READ_START 100
  34. #define TAG_XMPP_READ_STREAM 101
  35. #define TAG_XMPP_WRITE_START 200
  36. #define TAG_XMPP_WRITE_STREAM 201
  37. #define TAG_XMPP_WRITE_RECEIPT 202
  38. // Define the timeouts (in seconds) for SRV
  39. #define TIMEOUT_SRV_RESOLUTION 30.0
  40. NSString *const XMPPStreamErrorDomain = @"XMPPStreamErrorDomain";
  41. NSString *const XMPPStreamDidChangeMyJIDNotification = @"XMPPStreamDidChangeMyJID";
  42. const NSTimeInterval XMPPStreamTimeoutNone = -1;
  43. enum XMPPStreamFlags
  44. {
  45. kP2PInitiator = 1 << 0, // If set, we are the P2P initializer
  46. kIsSecure = 1 << 1, // If set, connection has been secured via SSL/TLS
  47. kIsAuthenticated = 1 << 2, // If set, authentication has succeeded
  48. kDidStartNegotiation = 1 << 3, // If set, negotiation has started at least once
  49. };
  50. enum XMPPStreamConfig
  51. {
  52. kP2PMode = 1 << 0, // If set, the XMPPStream was initialized in P2P mode
  53. kResetByteCountPerConnection = 1 << 1, // If set, byte count should be reset per connection
  54. #if TARGET_OS_IPHONE
  55. kEnableBackgroundingOnSocket = 1 << 2, // If set, the VoIP flag should be set on the socket
  56. #endif
  57. };
  58. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  59. #pragma mark -
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  61. @interface XMPPStream ()
  62. {
  63. dispatch_queue_t xmppQueue;
  64. void *xmppQueueTag;
  65. dispatch_queue_t willSendIqQueue;
  66. dispatch_queue_t willSendMessageQueue;
  67. dispatch_queue_t willSendPresenceQueue;
  68. dispatch_queue_t willReceiveIqQueue;
  69. dispatch_queue_t willReceiveMessageQueue;
  70. dispatch_queue_t willReceivePresenceQueue;
  71. dispatch_queue_t didReceiveIqQueue;
  72. dispatch_source_t connectTimer;
  73. GCDMulticastDelegate <XMPPStreamDelegate> *multicastDelegate;
  74. int state;
  75. GCDAsyncSocket *asyncSocket;
  76. UInt64 numberOfBytesSent;
  77. UInt64 numberOfBytesReceived;
  78. XMPPParser *parser;
  79. NSError *parserError;
  80. Byte flags;
  81. Byte config;
  82. NSString *hostName;
  83. UInt16 hostPort;
  84. BOOL autoStartTLS;
  85. id <XMPPSASLAuthentication> auth;
  86. NSDate *authenticationDate;
  87. XMPPJID *myJID_setByClient;
  88. XMPPJID *myJID_setByServer;
  89. XMPPJID *remoteJID;
  90. XMPPPresence *myPresence;
  91. NSXMLElement *rootElement;
  92. NSTimeInterval keepAliveInterval;
  93. dispatch_source_t keepAliveTimer;
  94. NSTimeInterval lastSendReceiveTime;
  95. NSData *keepAliveData;
  96. NSMutableArray *registeredModules;
  97. NSMutableDictionary *autoDelegateDict;
  98. XMPPSRVResolver *srvResolver;
  99. NSArray *srvResults;
  100. NSUInteger srvResultsIndex;
  101. NSMutableArray *receipts;
  102. NSThread *xmppUtilityThread;
  103. NSRunLoop *xmppUtilityRunLoop;
  104. id userTag;
  105. }
  106. - (void)setIsSecure:(BOOL)flag;
  107. - (void)setIsAuthenticated:(BOOL)flag;
  108. - (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag;
  109. - (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag;
  110. - (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag;
  111. - (void)startNegotiation;
  112. - (void)sendOpeningNegotiation;
  113. - (void)continueStartTLS:(NSMutableDictionary *)settings;
  114. - (void)continueHandleBinding:(NSString *)alternativeResource;
  115. - (void)setupKeepAliveTimer;
  116. - (void)keepAlive;
  117. - (void)startConnectTimeout:(NSTimeInterval)timeout;
  118. - (void)endConnectTimeout;
  119. - (void)doConnectTimeout;
  120. - (void)continueReceiveMessage:(XMPPMessage *)message;
  121. - (void)continueReceiveIQ:(XMPPIQ *)iq;
  122. - (void)continueReceivePresence:(XMPPPresence *)presence;
  123. @end
  124. @interface XMPPElementReceipt (PrivateAPI)
  125. - (void)signalSuccess;
  126. - (void)signalFailure;
  127. @end
  128. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  129. #pragma mark -
  130. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  131. @implementation XMPPStream
  132. @synthesize tag = userTag;
  133. /**
  134. * Shared initialization between the various init methods.
  135. **/
  136. - (void)commonInit
  137. {
  138. xmppQueueTag = &xmppQueueTag;
  139. xmppQueue = dispatch_queue_create("xmpp", NULL);
  140. dispatch_queue_set_specific(xmppQueue, xmppQueueTag, xmppQueueTag, NULL);
  141. willSendIqQueue = dispatch_queue_create("xmpp.willSendIq", NULL);
  142. willSendMessageQueue = dispatch_queue_create("xmpp.willSendMessage", NULL);
  143. willSendPresenceQueue = dispatch_queue_create("xmpp.willSendPresence", NULL);
  144. willReceiveIqQueue = dispatch_queue_create("xmpp.willReceiveIq", NULL);
  145. willReceiveMessageQueue = dispatch_queue_create("xmpp.willReceiveMessage", NULL);
  146. willReceivePresenceQueue = dispatch_queue_create("xmpp.willReceivePresence", NULL);
  147. didReceiveIqQueue = dispatch_queue_create("xmpp.didReceiveIq", NULL);
  148. multicastDelegate = (GCDMulticastDelegate <XMPPStreamDelegate> *)[[GCDMulticastDelegate alloc] init];
  149. state = STATE_XMPP_DISCONNECTED;
  150. flags = 0;
  151. config = 0;
  152. numberOfBytesSent = 0;
  153. numberOfBytesReceived = 0;
  154. hostPort = 5222;
  155. keepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
  156. keepAliveData = [@" " dataUsingEncoding:NSUTF8StringEncoding];
  157. registeredModules = [[NSMutableArray alloc] init];
  158. autoDelegateDict = [[NSMutableDictionary alloc] init];
  159. receipts = [[NSMutableArray alloc] init];
  160. // Setup and start the utility thread.
  161. // We need to be careful to ensure the thread doesn't retain a reference to us longer than necessary.
  162. xmppUtilityThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(xmppThreadMain) object:nil];
  163. [[xmppUtilityThread threadDictionary] setObject:self forKey:@"XMPPStream"];
  164. [xmppUtilityThread start];
  165. }
  166. /**
  167. * Standard XMPP initialization.
  168. * The stream is a standard client to server connection.
  169. **/
  170. - (id)init
  171. {
  172. if ((self = [super init]))
  173. {
  174. // Common initialization
  175. [self commonInit];
  176. // Initialize socket
  177. asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:xmppQueue];
  178. }
  179. return self;
  180. }
  181. /**
  182. * Peer to Peer XMPP initialization.
  183. * The stream is a direct client to client connection as outlined in XEP-0174.
  184. **/
  185. - (id)initP2PFrom:(XMPPJID *)jid
  186. {
  187. if ((self = [super init]))
  188. {
  189. // Common initialization
  190. [self commonInit];
  191. // Store JID
  192. myJID_setByClient = jid;
  193. // We do not initialize the socket, since the connectP2PWithSocket: method might be used.
  194. // Initialize configuration
  195. config = kP2PMode;
  196. }
  197. return self;
  198. }
  199. /**
  200. * Standard deallocation method.
  201. * Every object variable declared in the header file should be released here.
  202. **/
  203. - (void)dealloc
  204. {
  205. #if !OS_OBJECT_USE_OBJC
  206. dispatch_release(xmppQueue);
  207. dispatch_release(willSendIqQueue);
  208. dispatch_release(willSendMessageQueue);
  209. dispatch_release(willSendPresenceQueue);
  210. dispatch_release(willReceiveIqQueue);
  211. dispatch_release(willReceiveMessageQueue);
  212. dispatch_release(willReceivePresenceQueue);
  213. dispatch_release(didReceiveIqQueue);
  214. #endif
  215. [asyncSocket setDelegate:nil delegateQueue:NULL];
  216. [asyncSocket disconnect];
  217. [parser setDelegate:nil delegateQueue:NULL];
  218. if (keepAliveTimer)
  219. {
  220. dispatch_source_cancel(keepAliveTimer);
  221. }
  222. for (XMPPElementReceipt *receipt in receipts)
  223. {
  224. [receipt signalFailure];
  225. }
  226. [[self class] performSelector:@selector(xmppThreadStop)
  227. onThread:xmppUtilityThread
  228. withObject:nil
  229. waitUntilDone:NO];
  230. }
  231. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  232. #pragma mark Properties
  233. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  234. @synthesize xmppQueue;
  235. @synthesize xmppQueueTag;
  236. - (XMPPStreamState)state
  237. {
  238. __block XMPPStreamState result = STATE_XMPP_DISCONNECTED;
  239. dispatch_block_t block = ^{
  240. result = (XMPPStreamState)state;
  241. };
  242. if (dispatch_get_specific(xmppQueueTag))
  243. block();
  244. else
  245. dispatch_sync(xmppQueue, block);
  246. return result;
  247. }
  248. - (NSString *)hostName
  249. {
  250. if (dispatch_get_specific(xmppQueueTag))
  251. {
  252. return hostName;
  253. }
  254. else
  255. {
  256. __block NSString *result;
  257. dispatch_sync(xmppQueue, ^{
  258. result = hostName;
  259. });
  260. return result;
  261. }
  262. }
  263. - (void)setHostName:(NSString *)newHostName
  264. {
  265. if (dispatch_get_specific(xmppQueueTag))
  266. {
  267. if (hostName != newHostName)
  268. {
  269. hostName = [newHostName copy];
  270. }
  271. }
  272. else
  273. {
  274. NSString *newHostNameCopy = [newHostName copy];
  275. dispatch_async(xmppQueue, ^{
  276. hostName = newHostNameCopy;
  277. });
  278. }
  279. }
  280. - (UInt16)hostPort
  281. {
  282. if (dispatch_get_specific(xmppQueueTag))
  283. {
  284. return hostPort;
  285. }
  286. else
  287. {
  288. __block UInt16 result;
  289. dispatch_sync(xmppQueue, ^{
  290. result = hostPort;
  291. });
  292. return result;
  293. }
  294. }
  295. - (void)setHostPort:(UInt16)newHostPort
  296. {
  297. dispatch_block_t block = ^{
  298. hostPort = newHostPort;
  299. };
  300. if (dispatch_get_specific(xmppQueueTag))
  301. block();
  302. else
  303. dispatch_async(xmppQueue, block);
  304. }
  305. - (BOOL)autoStartTLS
  306. {
  307. __block BOOL result;
  308. dispatch_block_t block = ^{
  309. result = autoStartTLS;
  310. };
  311. if (dispatch_get_specific(xmppQueueTag))
  312. block();
  313. else
  314. dispatch_sync(xmppQueue, block);
  315. return result;
  316. }
  317. - (void)setAutoStartTLS:(BOOL)flag
  318. {
  319. dispatch_block_t block = ^{
  320. autoStartTLS = flag;
  321. };
  322. if (dispatch_get_specific(xmppQueueTag))
  323. block();
  324. else
  325. dispatch_async(xmppQueue, block);
  326. }
  327. - (XMPPJID *)myJID
  328. {
  329. __block XMPPJID *result = nil;
  330. dispatch_block_t block = ^{
  331. if (myJID_setByServer)
  332. result = myJID_setByServer;
  333. else
  334. result = myJID_setByClient;
  335. };
  336. if (dispatch_get_specific(xmppQueueTag))
  337. block();
  338. else
  339. dispatch_sync(xmppQueue, block);
  340. return result;
  341. }
  342. - (void)setMyJID_setByClient:(XMPPJID *)newMyJID
  343. {
  344. // XMPPJID is an immutable class (copy == retain)
  345. dispatch_block_t block = ^{
  346. if (![myJID_setByClient isEqualToJID:newMyJID])
  347. {
  348. myJID_setByClient = newMyJID;
  349. if (myJID_setByServer == nil)
  350. {
  351. [[NSNotificationCenter defaultCenter] postNotificationName:XMPPStreamDidChangeMyJIDNotification
  352. object:self];
  353. }
  354. }
  355. };
  356. if (dispatch_get_specific(xmppQueueTag))
  357. block();
  358. else
  359. dispatch_async(xmppQueue, block);
  360. }
  361. - (void)setMyJID_setByServer:(XMPPJID *)newMyJID
  362. {
  363. // XMPPJID is an immutable class (copy == retain)
  364. dispatch_block_t block = ^{
  365. if (![myJID_setByServer isEqualToJID:newMyJID])
  366. {
  367. XMPPJID *oldMyJID;
  368. if (myJID_setByServer)
  369. oldMyJID = myJID_setByServer;
  370. else
  371. oldMyJID = myJID_setByClient;
  372. myJID_setByServer = newMyJID;
  373. if (![oldMyJID isEqualToJID:newMyJID])
  374. {
  375. [[NSNotificationCenter defaultCenter] postNotificationName:XMPPStreamDidChangeMyJIDNotification
  376. object:self];
  377. }
  378. }
  379. };
  380. if (dispatch_get_specific(xmppQueueTag))
  381. block();
  382. else
  383. dispatch_async(xmppQueue, block);
  384. }
  385. - (void)setMyJID:(XMPPJID *)newMyJID
  386. {
  387. [self setMyJID_setByClient:newMyJID];
  388. }
  389. - (XMPPJID *)remoteJID
  390. {
  391. if (dispatch_get_specific(xmppQueueTag))
  392. {
  393. return remoteJID;
  394. }
  395. else
  396. {
  397. __block XMPPJID *result;
  398. dispatch_sync(xmppQueue, ^{
  399. result = remoteJID;
  400. });
  401. return result;
  402. }
  403. }
  404. - (XMPPPresence *)myPresence
  405. {
  406. if (dispatch_get_specific(xmppQueueTag))
  407. {
  408. return myPresence;
  409. }
  410. else
  411. {
  412. __block XMPPPresence *result;
  413. dispatch_sync(xmppQueue, ^{
  414. result = myPresence;
  415. });
  416. return result;
  417. }
  418. }
  419. - (NSTimeInterval)keepAliveInterval
  420. {
  421. __block NSTimeInterval result = 0.0;
  422. dispatch_block_t block = ^{
  423. result = keepAliveInterval;
  424. };
  425. if (dispatch_get_specific(xmppQueueTag))
  426. block();
  427. else
  428. dispatch_sync(xmppQueue, block);
  429. return result;
  430. }
  431. - (void)setKeepAliveInterval:(NSTimeInterval)interval
  432. {
  433. dispatch_block_t block = ^{
  434. if (keepAliveInterval != interval)
  435. {
  436. if (interval <= 0.0)
  437. keepAliveInterval = interval;
  438. else
  439. keepAliveInterval = MAX(interval, MIN_KEEPALIVE_INTERVAL);
  440. [self setupKeepAliveTimer];
  441. }
  442. };
  443. if (dispatch_get_specific(xmppQueueTag))
  444. block();
  445. else
  446. dispatch_async(xmppQueue, block);
  447. }
  448. - (char)keepAliveWhitespaceCharacter
  449. {
  450. __block char keepAliveChar = ' ';
  451. dispatch_block_t block = ^{
  452. NSString *keepAliveString = [[NSString alloc] initWithData:keepAliveData encoding:NSUTF8StringEncoding];
  453. if ([keepAliveString length] > 0)
  454. {
  455. keepAliveChar = (char)[keepAliveString characterAtIndex:0];
  456. }
  457. };
  458. if (dispatch_get_specific(xmppQueueTag))
  459. block();
  460. else
  461. dispatch_sync(xmppQueue, block);
  462. return keepAliveChar;
  463. }
  464. - (void)setKeepAliveWhitespaceCharacter:(char)keepAliveChar
  465. {
  466. dispatch_block_t block = ^{
  467. if (keepAliveChar == ' ' || keepAliveChar == '\n' || keepAliveChar == '\t')
  468. {
  469. keepAliveData = [[NSString stringWithFormat:@"%c", keepAliveChar] dataUsingEncoding:NSUTF8StringEncoding];
  470. }
  471. };
  472. if (dispatch_get_specific(xmppQueueTag))
  473. block();
  474. else
  475. dispatch_async(xmppQueue, block);
  476. }
  477. - (UInt64)numberOfBytesSent
  478. {
  479. if (dispatch_get_specific(xmppQueueTag))
  480. {
  481. return numberOfBytesSent;
  482. }
  483. else
  484. {
  485. __block UInt64 result;
  486. dispatch_sync(xmppQueue, ^{
  487. result = numberOfBytesSent;
  488. });
  489. return result;
  490. }
  491. }
  492. - (UInt64)numberOfBytesReceived
  493. {
  494. if (dispatch_get_specific(xmppQueueTag))
  495. {
  496. return numberOfBytesReceived;
  497. }
  498. else
  499. {
  500. __block UInt64 result;
  501. dispatch_sync(xmppQueue, ^{
  502. result = numberOfBytesReceived;
  503. });
  504. return result;
  505. }
  506. }
  507. - (BOOL)resetByteCountPerConnection
  508. {
  509. __block BOOL result = NO;
  510. dispatch_block_t block = ^{
  511. result = (config & kResetByteCountPerConnection) ? YES : NO;
  512. };
  513. if (dispatch_get_specific(xmppQueueTag))
  514. block();
  515. else
  516. dispatch_sync(xmppQueue, block);
  517. return result;
  518. }
  519. - (void)setResetByteCountPerConnection:(BOOL)flag
  520. {
  521. dispatch_block_t block = ^{
  522. if (flag)
  523. config |= kResetByteCountPerConnection;
  524. else
  525. config &= ~kResetByteCountPerConnection;
  526. };
  527. if (dispatch_get_specific(xmppQueueTag))
  528. block();
  529. else
  530. dispatch_async(xmppQueue, block);
  531. }
  532. #if TARGET_OS_IPHONE
  533. - (BOOL)enableBackgroundingOnSocket
  534. {
  535. __block BOOL result = NO;
  536. dispatch_block_t block = ^{
  537. result = (config & kEnableBackgroundingOnSocket) ? YES : NO;
  538. };
  539. if (dispatch_get_specific(xmppQueueTag))
  540. block();
  541. else
  542. dispatch_sync(xmppQueue, block);
  543. return result;
  544. }
  545. - (void)setEnableBackgroundingOnSocket:(BOOL)flag
  546. {
  547. dispatch_block_t block = ^{
  548. if (flag)
  549. config |= kEnableBackgroundingOnSocket;
  550. else
  551. config &= ~kEnableBackgroundingOnSocket;
  552. };
  553. if (dispatch_get_specific(xmppQueueTag))
  554. block();
  555. else
  556. dispatch_async(xmppQueue, block);
  557. }
  558. #endif
  559. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  560. #pragma mark Configuration
  561. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  562. - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
  563. {
  564. // Asynchronous operation (if outside xmppQueue)
  565. dispatch_block_t block = ^{
  566. [multicastDelegate addDelegate:delegate delegateQueue:delegateQueue];
  567. };
  568. if (dispatch_get_specific(xmppQueueTag))
  569. block();
  570. else
  571. dispatch_async(xmppQueue, block);
  572. }
  573. - (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
  574. {
  575. // Synchronous operation
  576. dispatch_block_t block = ^{
  577. [multicastDelegate removeDelegate:delegate delegateQueue:delegateQueue];
  578. };
  579. if (dispatch_get_specific(xmppQueueTag))
  580. block();
  581. else
  582. dispatch_sync(xmppQueue, block);
  583. }
  584. - (void)removeDelegate:(id)delegate
  585. {
  586. // Synchronous operation
  587. dispatch_block_t block = ^{
  588. [multicastDelegate removeDelegate:delegate];
  589. };
  590. if (dispatch_get_specific(xmppQueueTag))
  591. block();
  592. else
  593. dispatch_sync(xmppQueue, block);
  594. }
  595. /**
  596. * Returns YES if the stream was opened in P2P mode.
  597. * In other words, the stream was created via initP2PFrom: to use XEP-0174.
  598. **/
  599. - (BOOL)isP2P
  600. {
  601. if (dispatch_get_specific(xmppQueueTag))
  602. {
  603. return (config & kP2PMode) ? YES : NO;
  604. }
  605. else
  606. {
  607. __block BOOL result;
  608. dispatch_sync(xmppQueue, ^{
  609. result = (config & kP2PMode) ? YES : NO;
  610. });
  611. return result;
  612. }
  613. }
  614. - (BOOL)isP2PInitiator
  615. {
  616. if (dispatch_get_specific(xmppQueueTag))
  617. {
  618. return ((config & kP2PMode) && (flags & kP2PInitiator));
  619. }
  620. else
  621. {
  622. __block BOOL result;
  623. dispatch_sync(xmppQueue, ^{
  624. result = ((config & kP2PMode) && (flags & kP2PInitiator));
  625. });
  626. return result;
  627. }
  628. }
  629. - (BOOL)isP2PRecipient
  630. {
  631. if (dispatch_get_specific(xmppQueueTag))
  632. {
  633. return ((config & kP2PMode) && !(flags & kP2PInitiator));
  634. }
  635. else
  636. {
  637. __block BOOL result;
  638. dispatch_sync(xmppQueue, ^{
  639. result = ((config & kP2PMode) && !(flags & kP2PInitiator));
  640. });
  641. return result;
  642. }
  643. }
  644. - (BOOL)didStartNegotiation
  645. {
  646. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  647. return (flags & kDidStartNegotiation) ? YES : NO;
  648. }
  649. - (void)setDidStartNegotiation:(BOOL)flag
  650. {
  651. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  652. if (flag)
  653. flags |= kDidStartNegotiation;
  654. else
  655. flags &= ~kDidStartNegotiation;
  656. }
  657. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  658. #pragma mark Connection State
  659. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  660. /**
  661. * Returns YES if the connection is closed, and thus no stream is open.
  662. * If the stream is neither disconnected, nor connected, then a connection is currently being established.
  663. **/
  664. - (BOOL)isDisconnected
  665. {
  666. __block BOOL result = NO;
  667. dispatch_block_t block = ^{
  668. result = (state == STATE_XMPP_DISCONNECTED);
  669. };
  670. if (dispatch_get_specific(xmppQueueTag))
  671. block();
  672. else
  673. dispatch_sync(xmppQueue, block);
  674. return result;
  675. }
  676. /**
  677. * Returns YES is the connection is currently connecting
  678. **/
  679. - (BOOL)isConnecting
  680. {
  681. XMPPLogTrace();
  682. __block BOOL result = NO;
  683. dispatch_block_t block = ^{ @autoreleasepool {
  684. result = (state == STATE_XMPP_CONNECTING);
  685. }};
  686. if (dispatch_get_specific(xmppQueueTag))
  687. block();
  688. else
  689. dispatch_sync(xmppQueue, block);
  690. return result;
  691. }
  692. /**
  693. * Returns YES if the connection is open, and the stream has been properly established.
  694. * If the stream is neither disconnected, nor connected, then a connection is currently being established.
  695. **/
  696. - (BOOL)isConnected
  697. {
  698. __block BOOL result = NO;
  699. dispatch_block_t block = ^{
  700. result = (state == STATE_XMPP_CONNECTED);
  701. };
  702. if (dispatch_get_specific(xmppQueueTag))
  703. block();
  704. else
  705. dispatch_sync(xmppQueue, block);
  706. return result;
  707. }
  708. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  709. #pragma mark Connect Timeout
  710. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  711. /**
  712. * Start Connect Timeout
  713. **/
  714. - (void)startConnectTimeout:(NSTimeInterval)timeout
  715. {
  716. XMPPLogTrace();
  717. if (timeout >= 0.0 && !connectTimer)
  718. {
  719. connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, xmppQueue);
  720. dispatch_source_set_event_handler(connectTimer, ^{ @autoreleasepool {
  721. [self doConnectTimeout];
  722. }});
  723. #if !OS_OBJECT_USE_OBJC
  724. dispatch_source_t theConnectTimer = connectTimer;
  725. dispatch_source_set_cancel_handler(connectTimer, ^{
  726. XMPPLogVerbose(@"%@: dispatch_release(connectTimer)", THIS_FILE);
  727. dispatch_release(theConnectTimer);
  728. });
  729. #endif
  730. dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (timeout * NSEC_PER_SEC));
  731. dispatch_source_set_timer(connectTimer, tt, DISPATCH_TIME_FOREVER, 0);
  732. dispatch_resume(connectTimer);
  733. }
  734. }
  735. /**
  736. * End Connect Timeout
  737. **/
  738. - (void)endConnectTimeout
  739. {
  740. XMPPLogTrace();
  741. if (connectTimer)
  742. {
  743. dispatch_source_cancel(connectTimer);
  744. connectTimer = NULL;
  745. }
  746. }
  747. /**
  748. * Connect has timed out, so inform the delegates and close the connection
  749. **/
  750. - (void)doConnectTimeout
  751. {
  752. XMPPLogTrace();
  753. [self endConnectTimeout];
  754. if (state != STATE_XMPP_DISCONNECTED)
  755. {
  756. [multicastDelegate xmppStreamConnectDidTimeout:self];
  757. if (state == STATE_XMPP_RESOLVING_SRV)
  758. {
  759. [srvResolver stop];
  760. srvResolver = nil;
  761. state = STATE_XMPP_DISCONNECTED;
  762. }
  763. else
  764. {
  765. [asyncSocket disconnect];
  766. // Everthing will be handled in socketDidDisconnect:withError:
  767. }
  768. }
  769. }
  770. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  771. #pragma mark C2S Connection
  772. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  773. - (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
  774. {
  775. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  776. XMPPLogTrace();
  777. BOOL result = [asyncSocket connectToHost:host onPort:port error:errPtr];
  778. if (result && [self resetByteCountPerConnection])
  779. {
  780. numberOfBytesSent = 0;
  781. numberOfBytesReceived = 0;
  782. }
  783. if(result)
  784. {
  785. [self startConnectTimeout:timeout];
  786. }
  787. return result;
  788. }
  789. - (BOOL)connectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
  790. {
  791. XMPPLogTrace();
  792. __block BOOL result = NO;
  793. __block NSError *err = nil;
  794. dispatch_block_t block = ^{ @autoreleasepool {
  795. if (state != STATE_XMPP_DISCONNECTED)
  796. {
  797. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  798. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  799. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  800. result = NO;
  801. return_from_block;
  802. }
  803. if ([self isP2P])
  804. {
  805. NSString *errMsg = @"P2P streams must use either connectTo:withAddress: or connectP2PWithSocket:.";
  806. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  807. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  808. result = NO;
  809. return_from_block;
  810. }
  811. if (myJID_setByClient == nil)
  812. {
  813. // Note: If you wish to use anonymous authentication, you should still set myJID prior to calling connect.
  814. // You can simply set it to something like "anonymous@<domain>", where "<domain>" is the proper domain.
  815. // After the authentication process, you can query the myJID property to see what your assigned JID is.
  816. //
  817. // Setting myJID allows the framework to follow the xmpp protocol properly,
  818. // and it allows the framework to connect to servers without a DNS entry.
  819. //
  820. // For example, one may setup a private xmpp server for internal testing on their local network.
  821. // The xmpp domain of the server may be something like "testing.mycompany.com",
  822. // but since the server is internal, an IP (192.168.1.22) is used as the hostname to connect.
  823. //
  824. // Proper connection requires a TCP connection to the IP (192.168.1.22),
  825. // but the xmpp handshake requires the xmpp domain (testing.mycompany.com).
  826. NSString *errMsg = @"You must set myJID before calling connect.";
  827. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  828. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  829. result = NO;
  830. return_from_block;
  831. }
  832. // Notify delegates
  833. [multicastDelegate xmppStreamWillConnect:self];
  834. if ([hostName length] == 0)
  835. {
  836. // Resolve the hostName via myJID SRV resolution
  837. state = STATE_XMPP_RESOLVING_SRV;
  838. srvResolver = [[XMPPSRVResolver alloc] initWithdDelegate:self delegateQueue:xmppQueue resolverQueue:NULL];
  839. srvResults = nil;
  840. srvResultsIndex = 0;
  841. NSString *srvName = [XMPPSRVResolver srvNameFromXMPPDomain:[myJID_setByClient domain]];
  842. [srvResolver startWithSRVName:srvName timeout:TIMEOUT_SRV_RESOLUTION];
  843. result = YES;
  844. }
  845. else
  846. {
  847. // Open TCP connection to the configured hostName.
  848. state = STATE_XMPP_CONNECTING;
  849. NSError *connectErr = nil;
  850. result = [self connectToHost:hostName onPort:hostPort withTimeout:XMPPStreamTimeoutNone error:&connectErr];
  851. if (!result)
  852. {
  853. err = connectErr;
  854. state = STATE_XMPP_DISCONNECTED;
  855. }
  856. }
  857. if(result)
  858. {
  859. [self startConnectTimeout:timeout];
  860. }
  861. }};
  862. if (dispatch_get_specific(xmppQueueTag))
  863. block();
  864. else
  865. dispatch_sync(xmppQueue, block);
  866. if (errPtr)
  867. *errPtr = err;
  868. return result;
  869. }
  870. - (BOOL)oldSchoolSecureConnectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
  871. {
  872. XMPPLogTrace();
  873. __block BOOL result = NO;
  874. __block NSError *err = nil;
  875. dispatch_block_t block = ^{ @autoreleasepool {
  876. // Go through the regular connect routine
  877. NSError *connectErr = nil;
  878. result = [self connectWithTimeout:timeout error:&connectErr];
  879. if (result)
  880. {
  881. // Mark the secure flag.
  882. // We will check the flag in socket:didConnectToHost:port:
  883. [self setIsSecure:YES];
  884. }
  885. else
  886. {
  887. err = connectErr;
  888. }
  889. }};
  890. if (dispatch_get_specific(xmppQueueTag))
  891. block();
  892. else
  893. dispatch_sync(xmppQueue, block);
  894. if (errPtr)
  895. *errPtr = err;
  896. return result;
  897. }
  898. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  899. #pragma mark P2P Connection
  900. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  901. /**
  902. * Starts a P2P connection to the given user and given address.
  903. * This method only works with XMPPStream objects created using the initP2P method.
  904. *
  905. * The given address is specified as a sockaddr structure wrapped in a NSData object.
  906. * For example, a NSData object returned from NSNetservice's addresses method.
  907. **/
  908. - (BOOL)connectTo:(XMPPJID *)jid withAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
  909. {
  910. XMPPLogTrace();
  911. __block BOOL result = YES;
  912. __block NSError *err = nil;
  913. dispatch_block_t block = ^{ @autoreleasepool {
  914. if (state != STATE_XMPP_DISCONNECTED)
  915. {
  916. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  917. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  918. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  919. result = NO;
  920. return_from_block;
  921. }
  922. if (![self isP2P])
  923. {
  924. NSString *errMsg = @"Non P2P streams must use the connect: method";
  925. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  926. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  927. result = NO;
  928. return_from_block;
  929. }
  930. // Turn on P2P initiator flag
  931. flags |= kP2PInitiator;
  932. // Store remoteJID
  933. remoteJID = [jid copy];
  934. NSAssert((asyncSocket == nil), @"Forgot to release the previous asyncSocket instance.");
  935. // Notify delegates
  936. [multicastDelegate xmppStreamWillConnect:self];
  937. // Update state
  938. state = STATE_XMPP_CONNECTING;
  939. // Initailize socket
  940. asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:xmppQueue];
  941. NSError *connectErr = nil;
  942. result = [asyncSocket connectToAddress:remoteAddr error:&connectErr];
  943. if (result == NO)
  944. {
  945. err = connectErr;
  946. state = STATE_XMPP_DISCONNECTED;
  947. }
  948. else if ([self resetByteCountPerConnection])
  949. {
  950. numberOfBytesSent = 0;
  951. numberOfBytesReceived = 0;
  952. }
  953. if(result)
  954. {
  955. [self startConnectTimeout:timeout];
  956. }
  957. }};
  958. if (dispatch_get_specific(xmppQueueTag))
  959. block();
  960. else
  961. dispatch_sync(xmppQueue, block);
  962. if (errPtr)
  963. *errPtr = err;
  964. return result;
  965. }
  966. /**
  967. * Starts a P2P connection with the given accepted socket.
  968. * This method only works with XMPPStream objects created using the initP2P method.
  969. *
  970. * The given socket should be a socket that has already been accepted.
  971. * The remoteJID will be extracted from the opening stream negotiation.
  972. **/
  973. - (BOOL)connectP2PWithSocket:(GCDAsyncSocket *)acceptedSocket error:(NSError **)errPtr
  974. {
  975. XMPPLogTrace();
  976. __block BOOL result = YES;
  977. __block NSError *err = nil;
  978. dispatch_block_t block = ^{ @autoreleasepool {
  979. if (state != STATE_XMPP_DISCONNECTED)
  980. {
  981. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  982. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  983. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  984. result = NO;
  985. return_from_block;
  986. }
  987. if (![self isP2P])
  988. {
  989. NSString *errMsg = @"Non P2P streams must use the connect: method";
  990. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  991. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  992. result = NO;
  993. return_from_block;
  994. }
  995. if (acceptedSocket == nil)
  996. {
  997. NSString *errMsg = @"Parameter acceptedSocket is nil.";
  998. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  999. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidParameter userInfo:info];
  1000. result = NO;
  1001. return_from_block;
  1002. }
  1003. // Turn off P2P initiator flag
  1004. flags &= ~kP2PInitiator;
  1005. NSAssert((asyncSocket == nil), @"Forgot to release the previous asyncSocket instance.");
  1006. // Store and configure socket
  1007. asyncSocket = acceptedSocket;
  1008. [asyncSocket setDelegate:self delegateQueue:xmppQueue];
  1009. // Notify delegates
  1010. [multicastDelegate xmppStream:self socketDidConnect:asyncSocket];
  1011. // Update state
  1012. state = STATE_XMPP_CONNECTING;
  1013. if ([self resetByteCountPerConnection])
  1014. {
  1015. numberOfBytesSent = 0;
  1016. numberOfBytesReceived = 0;
  1017. }
  1018. // Start the XML stream
  1019. [self startNegotiation];
  1020. }};
  1021. if (dispatch_get_specific(xmppQueueTag))
  1022. block();
  1023. else
  1024. dispatch_sync(xmppQueue, block);
  1025. if (errPtr)
  1026. *errPtr = err;
  1027. return result;
  1028. }
  1029. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1030. #pragma mark Disconnect
  1031. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1032. /**
  1033. * Closes the connection to the remote host.
  1034. **/
  1035. - (void)disconnect
  1036. {
  1037. XMPPLogTrace();
  1038. dispatch_block_t block = ^{ @autoreleasepool {
  1039. if (state != STATE_XMPP_DISCONNECTED)
  1040. {
  1041. [multicastDelegate xmppStreamWasToldToDisconnect:self];
  1042. if (state == STATE_XMPP_RESOLVING_SRV)
  1043. {
  1044. [srvResolver stop];
  1045. srvResolver = nil;
  1046. state = STATE_XMPP_DISCONNECTED;
  1047. [multicastDelegate xmppStreamDidDisconnect:self withError:nil];
  1048. }
  1049. else
  1050. {
  1051. [asyncSocket disconnect];
  1052. // Everthing will be handled in socketDidDisconnect:withError:
  1053. }
  1054. }
  1055. }};
  1056. if (dispatch_get_specific(xmppQueueTag))
  1057. block();
  1058. else
  1059. dispatch_sync(xmppQueue, block);
  1060. }
  1061. - (void)disconnectAfterSending
  1062. {
  1063. XMPPLogTrace();
  1064. dispatch_block_t block = ^{ @autoreleasepool {
  1065. if (state != STATE_XMPP_DISCONNECTED)
  1066. {
  1067. [multicastDelegate xmppStreamWasToldToDisconnect:self];
  1068. if (state == STATE_XMPP_RESOLVING_SRV)
  1069. {
  1070. [srvResolver stop];
  1071. srvResolver = nil;
  1072. state = STATE_XMPP_DISCONNECTED;
  1073. [multicastDelegate xmppStreamDidDisconnect:self withError:nil];
  1074. }
  1075. else
  1076. {
  1077. NSString *termStr = @"</stream:stream>";
  1078. NSData *termData = [termStr dataUsingEncoding:NSUTF8StringEncoding];
  1079. XMPPLogSend(@"SEND: %@", termStr);
  1080. numberOfBytesSent += [termData length];
  1081. [asyncSocket writeData:termData withTimeout:TIMEOUT_XMPP_WRITE tag:TAG_XMPP_WRITE_STREAM];
  1082. [asyncSocket disconnectAfterWriting];
  1083. // Everthing will be handled in socketDidDisconnect:withError:
  1084. }
  1085. }
  1086. }};
  1087. if (dispatch_get_specific(xmppQueueTag))
  1088. block();
  1089. else
  1090. dispatch_async(xmppQueue, block);
  1091. }
  1092. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1093. #pragma mark Security
  1094. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1095. /**
  1096. * Returns YES if SSL/TLS has been used to secure the connection.
  1097. **/
  1098. - (BOOL)isSecure
  1099. {
  1100. if (dispatch_get_specific(xmppQueueTag))
  1101. {
  1102. return (flags & kIsSecure) ? YES : NO;
  1103. }
  1104. else
  1105. {
  1106. __block BOOL result;
  1107. dispatch_sync(xmppQueue, ^{
  1108. result = (flags & kIsSecure) ? YES : NO;
  1109. });
  1110. return result;
  1111. }
  1112. }
  1113. - (void)setIsSecure:(BOOL)flag
  1114. {
  1115. dispatch_block_t block = ^{
  1116. if(flag)
  1117. flags |= kIsSecure;
  1118. else
  1119. flags &= ~kIsSecure;
  1120. };
  1121. if (dispatch_get_specific(xmppQueueTag))
  1122. block();
  1123. else
  1124. dispatch_async(xmppQueue, block);
  1125. }
  1126. - (BOOL)supportsStartTLS
  1127. {
  1128. __block BOOL result = NO;
  1129. dispatch_block_t block = ^{ @autoreleasepool {
  1130. // The root element can be properly queried for authentication mechanisms anytime after the
  1131. // stream:features are received, and TLS has been setup (if required)
  1132. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1133. {
  1134. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1135. NSXMLElement *starttls = [features elementForName:@"starttls" xmlns:@"urn:ietf:params:xml:ns:xmpp-tls"];
  1136. result = (starttls != nil);
  1137. }
  1138. }};
  1139. if (dispatch_get_specific(xmppQueueTag))
  1140. block();
  1141. else
  1142. dispatch_sync(xmppQueue, block);
  1143. return result;
  1144. }
  1145. - (void)sendStartTLSRequest
  1146. {
  1147. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1148. XMPPLogTrace();
  1149. NSString *starttls = @"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
  1150. NSData *outgoingData = [starttls dataUsingEncoding:NSUTF8StringEncoding];
  1151. XMPPLogSend(@"SEND: %@", starttls);
  1152. numberOfBytesSent += [outgoingData length];
  1153. [asyncSocket writeData:outgoingData
  1154. withTimeout:TIMEOUT_XMPP_WRITE
  1155. tag:TAG_XMPP_WRITE_STREAM];
  1156. }
  1157. - (BOOL)secureConnection:(NSError **)errPtr
  1158. {
  1159. XMPPLogTrace();
  1160. __block BOOL result = YES;
  1161. __block NSError *err = nil;
  1162. dispatch_block_t block = ^{ @autoreleasepool {
  1163. if (state != STATE_XMPP_CONNECTED)
  1164. {
  1165. NSString *errMsg = @"Please wait until the stream is connected.";
  1166. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1167. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1168. result = NO;
  1169. return_from_block;
  1170. }
  1171. if ([self isSecure])
  1172. {
  1173. NSString *errMsg = @"The connection is already secure.";
  1174. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1175. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1176. result = NO;
  1177. return_from_block;
  1178. }
  1179. if (![self supportsStartTLS])
  1180. {
  1181. NSString *errMsg = @"The server does not support startTLS.";
  1182. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1183. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1184. result = NO;
  1185. return_from_block;
  1186. }
  1187. // Update state
  1188. state = STATE_XMPP_STARTTLS_1;
  1189. // Send the startTLS XML request
  1190. [self sendStartTLSRequest];
  1191. // We do not mark the stream as secure yet.
  1192. // We're waiting to receive the <proceed/> response from the
  1193. // server before we actually start the TLS handshake.
  1194. }};
  1195. if (dispatch_get_specific(xmppQueueTag))
  1196. block();
  1197. else
  1198. dispatch_sync(xmppQueue, block);
  1199. if (errPtr)
  1200. *errPtr = err;
  1201. return result;
  1202. }
  1203. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1204. #pragma mark Registration
  1205. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1206. /**
  1207. * This method checks the stream features of the connected server to determine if in-band registartion is supported.
  1208. * If we are not connected to a server, this method simply returns NO.
  1209. **/
  1210. - (BOOL)supportsInBandRegistration
  1211. {
  1212. __block BOOL result = NO;
  1213. dispatch_block_t block = ^{ @autoreleasepool {
  1214. // The root element can be properly queried for authentication mechanisms anytime after the
  1215. // stream:features are received, and TLS has been setup (if required)
  1216. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1217. {
  1218. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1219. NSXMLElement *reg = [features elementForName:@"register" xmlns:@"http://jabber.org/features/iq-register"];
  1220. result = (reg != nil);
  1221. }
  1222. }};
  1223. if (dispatch_get_specific(xmppQueueTag))
  1224. block();
  1225. else
  1226. dispatch_sync(xmppQueue, block);
  1227. return result;
  1228. }
  1229. /**
  1230. * This method attempts to register a new user on the server using the given elements.
  1231. * The result of this action will be returned via the delegate methods.
  1232. *
  1233. * If the XMPPStream is not connected, or the server doesn't support in-band registration, this method does nothing.
  1234. **/
  1235. - (BOOL)registerWithElements:(NSArray *)elements error:(NSError **)errPtr
  1236. {
  1237. XMPPLogTrace();
  1238. __block BOOL result = YES;
  1239. __block NSError *err = nil;
  1240. dispatch_block_t block = ^{ @autoreleasepool {
  1241. if (state != STATE_XMPP_CONNECTED)
  1242. {
  1243. NSString *errMsg = @"Please wait until the stream is connected.";
  1244. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1245. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1246. result = NO;
  1247. return_from_block;
  1248. }
  1249. if (![self supportsInBandRegistration])
  1250. {
  1251. NSString *errMsg = @"The server does not support in band registration.";
  1252. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1253. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1254. result = NO;
  1255. return_from_block;
  1256. }
  1257. NSXMLElement *queryElement = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"];
  1258. for(NSXMLElement *element in elements)
  1259. {
  1260. [queryElement addChild:element];
  1261. }
  1262. NSXMLElement *iqElement = [NSXMLElement elementWithName:@"iq"];
  1263. [iqElement addAttributeWithName:@"type" stringValue:@"set"];
  1264. [iqElement addChild:queryElement];
  1265. NSString *outgoingStr = [iqElement compactXMLString];
  1266. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1267. XMPPLogSend(@"SEND: %@", outgoingStr);
  1268. numberOfBytesSent += [outgoingData length];
  1269. [asyncSocket writeData:outgoingData
  1270. withTimeout:TIMEOUT_XMPP_WRITE
  1271. tag:TAG_XMPP_WRITE_STREAM];
  1272. // Update state
  1273. state = STATE_XMPP_REGISTERING;
  1274. }};
  1275. if (dispatch_get_specific(xmppQueueTag))
  1276. block();
  1277. else
  1278. dispatch_sync(xmppQueue, block);
  1279. if (errPtr)
  1280. *errPtr = err;
  1281. return result;
  1282. }
  1283. /**
  1284. * This method attempts to register a new user on the server using the given username and password.
  1285. * The result of this action will be returned via the delegate methods.
  1286. *
  1287. * If the XMPPStream is not connected, or the server doesn't support in-band registration, this method does nothing.
  1288. **/
  1289. - (BOOL)registerWithPassword:(NSString *)password error:(NSError **)errPtr
  1290. {
  1291. XMPPLogTrace();
  1292. __block BOOL result = YES;
  1293. __block NSError *err = nil;
  1294. dispatch_block_t block = ^{ @autoreleasepool {
  1295. if (myJID_setByClient == nil)
  1296. {
  1297. NSString *errMsg = @"You must set myJID before calling registerWithPassword:error:.";
  1298. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1299. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1300. result = NO;
  1301. return_from_block;
  1302. }
  1303. NSString *username = [myJID_setByClient user];
  1304. NSMutableArray *elements = [NSMutableArray array];
  1305. [elements addObject:[NSXMLElement elementWithName:@"username" stringValue:username]];
  1306. [elements addObject:[NSXMLElement elementWithName:@"password" stringValue:password]];
  1307. [self registerWithElements:elements error:errPtr];
  1308. }};
  1309. if (dispatch_get_specific(xmppQueueTag))
  1310. block();
  1311. else
  1312. dispatch_sync(xmppQueue, block);
  1313. if (errPtr)
  1314. *errPtr = err;
  1315. return result;
  1316. }
  1317. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1318. #pragma mark Authentication
  1319. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1320. - (NSArray *)supportedAuthenticationMechanisms
  1321. {
  1322. __block NSMutableArray *result = [[NSMutableArray alloc] init];
  1323. dispatch_block_t block = ^{ @autoreleasepool {
  1324. // The root element can be properly queried for authentication mechanisms anytime after the
  1325. // stream:features are received, and TLS has been setup (if required).
  1326. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1327. {
  1328. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1329. NSXMLElement *mech = [features elementForName:@"mechanisms" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
  1330. NSArray *mechanisms = [mech elementsForName:@"mechanism"];
  1331. for (NSXMLElement *mechanism in mechanisms)
  1332. {
  1333. [result addObject:[mechanism stringValue]];
  1334. }
  1335. }
  1336. }};
  1337. if (dispatch_get_specific(xmppQueueTag))
  1338. block();
  1339. else
  1340. dispatch_sync(xmppQueue, block);
  1341. return result;
  1342. }
  1343. /**
  1344. * This method checks the stream features of the connected server to determine
  1345. * if the given authentication mechanism is supported.
  1346. *
  1347. * If we are not connected to a server, this method simply returns NO.
  1348. **/
  1349. - (BOOL)supportsAuthenticationMechanism:(NSString *)mechanismType
  1350. {
  1351. __block BOOL result = NO;
  1352. dispatch_block_t block = ^{ @autoreleasepool {
  1353. // The root element can be properly queried for authentication mechanisms anytime after the
  1354. // stream:features are received, and TLS has been setup (if required).
  1355. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1356. {
  1357. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1358. NSXMLElement *mech = [features elementForName:@"mechanisms" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
  1359. NSArray *mechanisms = [mech elementsForName:@"mechanism"];
  1360. for (NSXMLElement *mechanism in mechanisms)
  1361. {
  1362. if ([[mechanism stringValue] isEqualToString:mechanismType])
  1363. {
  1364. result = YES;
  1365. break;
  1366. }
  1367. }
  1368. }
  1369. }};
  1370. if (dispatch_get_specific(xmppQueueTag))
  1371. block();
  1372. else
  1373. dispatch_sync(xmppQueue, block);
  1374. return result;
  1375. }
  1376. - (BOOL)authenticate:(id <XMPPSASLAuthentication>)inAuth error:(NSError **)errPtr
  1377. {
  1378. XMPPLogTrace();
  1379. __block BOOL result = NO;
  1380. __block NSError *err = nil;
  1381. dispatch_block_t block = ^{ @autoreleasepool {
  1382. if (state != STATE_XMPP_CONNECTED)
  1383. {
  1384. NSString *errMsg = @"Please wait until the stream is connected.";
  1385. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1386. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1387. result = NO;
  1388. return_from_block;
  1389. }
  1390. if (myJID_setByClient == nil)
  1391. {
  1392. NSString *errMsg = @"You must set myJID before calling authenticate:error:.";
  1393. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1394. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1395. result = NO;
  1396. return_from_block;
  1397. }
  1398. // Change state.
  1399. // We do this now because when we invoke the start method below,
  1400. // it may in turn invoke our sendAuthElement method, which expects us to be in STATE_XMPP_AUTH.
  1401. state = STATE_XMPP_AUTH;
  1402. if ([inAuth start:&err])
  1403. {
  1404. auth = inAuth;
  1405. result = YES;
  1406. }
  1407. else
  1408. {
  1409. // Unable to start authentication for some reason.
  1410. // Revert back to connected state.
  1411. state = STATE_XMPP_CONNECTED;
  1412. }
  1413. }};
  1414. if (dispatch_get_specific(xmppQueueTag))
  1415. block();
  1416. else
  1417. dispatch_sync(xmppQueue, block);
  1418. if (errPtr)
  1419. *errPtr = err;
  1420. return result;
  1421. }
  1422. /**
  1423. * This method applies to standard password authentication schemes only.
  1424. * This is NOT the primary authentication method.
  1425. *
  1426. * @see authenticate:error:
  1427. *
  1428. * This method exists for backwards compatibility, and may disappear in future versions.
  1429. **/
  1430. - (BOOL)authenticateWithPassword:(NSString *)inPassword error:(NSError **)errPtr
  1431. {
  1432. XMPPLogTrace();
  1433. // The given password parameter could be mutable
  1434. NSString *password = [inPassword copy];
  1435. __block BOOL result = YES;
  1436. __block NSError *err = nil;
  1437. dispatch_block_t block = ^{ @autoreleasepool {
  1438. if (state != STATE_XMPP_CONNECTED)
  1439. {
  1440. NSString *errMsg = @"Please wait until the stream is connected.";
  1441. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1442. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1443. result = NO;
  1444. return_from_block;
  1445. }
  1446. if (myJID_setByClient == nil)
  1447. {
  1448. NSString *errMsg = @"You must set myJID before calling authenticate:error:.";
  1449. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1450. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1451. result = NO;
  1452. return_from_block;
  1453. }
  1454. // Choose the best authentication method.
  1455. //
  1456. // P.S. - This method is deprecated.
  1457. id <XMPPSASLAuthentication> someAuth = nil;
  1458. if ([self supportsDigestMD5Authentication])
  1459. {
  1460. someAuth = [[XMPPDigestMD5Authentication alloc] initWithStream:self password:password];
  1461. result = [self authenticate:someAuth error:&err];
  1462. }
  1463. else if ([self supportsPlainAuthentication])
  1464. {
  1465. someAuth = [[XMPPPlainAuthentication alloc] initWithStream:self password:password];
  1466. result = [self authenticate:someAuth error:&err];
  1467. }
  1468. else if ([self supportsDeprecatedDigestAuthentication])
  1469. {
  1470. someAuth = [[XMPPDeprecatedDigestAuthentication alloc] initWithStream:self password:password];
  1471. result = [self authenticate:someAuth error:&err];
  1472. }
  1473. else if ([self supportsDeprecatedPlainAuthentication])
  1474. {
  1475. someAuth = [[XMPPDeprecatedDigestAuthentication alloc] initWithStream:self password:password];
  1476. result = [self authenticate:someAuth error:&err];
  1477. }
  1478. else
  1479. {
  1480. NSString *errMsg = @"No suitable authentication method found";
  1481. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1482. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1483. result = NO;
  1484. }
  1485. }};
  1486. if (dispatch_get_specific(xmppQueueTag))
  1487. block();
  1488. else
  1489. dispatch_sync(xmppQueue, block);
  1490. if (errPtr)
  1491. *errPtr = err;
  1492. return result;
  1493. }
  1494. - (BOOL)isAuthenticating{
  1495. XMPPLogTrace();
  1496. __block BOOL result = NO;
  1497. dispatch_block_t block = ^{ @autoreleasepool {
  1498. result = (state == STATE_XMPP_AUTH);
  1499. }};
  1500. if (dispatch_get_specific(xmppQueueTag))
  1501. block();
  1502. else
  1503. dispatch_sync(xmppQueue, block);
  1504. return result;
  1505. }
  1506. - (BOOL)isAuthenticated
  1507. {
  1508. __block BOOL result = NO;
  1509. dispatch_block_t block = ^{
  1510. result = (flags & kIsAuthenticated) ? YES : NO;
  1511. };
  1512. if (dispatch_get_specific(xmppQueueTag))
  1513. block();
  1514. else
  1515. dispatch_sync(xmppQueue, block);
  1516. return result;
  1517. }
  1518. - (void)setIsAuthenticated:(BOOL)flag
  1519. {
  1520. dispatch_block_t block = ^{
  1521. if(flag)
  1522. {
  1523. flags |= kIsAuthenticated;
  1524. authenticationDate = [NSDate date];
  1525. }
  1526. else
  1527. {
  1528. flags &= ~kIsAuthenticated;
  1529. authenticationDate = nil;
  1530. }
  1531. };
  1532. if (dispatch_get_specific(xmppQueueTag))
  1533. block();
  1534. else
  1535. dispatch_async(xmppQueue, block);
  1536. }
  1537. - (NSDate *)authenticationDate{
  1538. __block NSDate *result = nil;
  1539. dispatch_block_t block = ^{
  1540. if(flags & kIsAuthenticated)
  1541. {
  1542. result = authenticationDate;
  1543. }
  1544. };
  1545. if (dispatch_get_specific(xmppQueueTag))
  1546. block();
  1547. else
  1548. dispatch_sync(xmppQueue, block);
  1549. return result;
  1550. }
  1551. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1552. #pragma mark Compression
  1553. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1554. - (NSArray *)supportedCompressionMethods
  1555. {
  1556. __block NSMutableArray *result = [[NSMutableArray alloc] init];
  1557. dispatch_block_t block = ^{ @autoreleasepool {
  1558. // The root element can be properly queried for compression methods anytime after the
  1559. // stream:features are received, and TLS has been setup (if required).
  1560. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1561. {
  1562. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1563. NSXMLElement *compression = [features elementForName:@"compression" xmlns:@"http://jabber.org/features/compress"];
  1564. NSArray *methods = [compression elementsForName:@"method"];
  1565. for (NSXMLElement *method in methods)
  1566. {
  1567. [result addObject:[method stringValue]];
  1568. }
  1569. }
  1570. }};
  1571. if (dispatch_get_specific(xmppQueueTag))
  1572. block();
  1573. else
  1574. dispatch_sync(xmppQueue, block);
  1575. return result;
  1576. }
  1577. /**
  1578. * This method checks the stream features of the connected server to determine
  1579. * if the given compression method is supported.
  1580. *
  1581. * If we are not connected to a server, this method simply returns NO.
  1582. **/
  1583. - (BOOL)supportsCompressionMethod:(NSString *)compressionMethod
  1584. {
  1585. __block BOOL result = NO;
  1586. dispatch_block_t block = ^{ @autoreleasepool {
  1587. // The root element can be properly queried for compression methods anytime after the
  1588. // stream:features are received, and TLS has been setup (if required).
  1589. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1590. {
  1591. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1592. NSXMLElement *compression = [features elementForName:@"compression" xmlns:@"http://jabber.org/features/compress"];
  1593. NSArray *methods = [compression elementsForName:@"method"];
  1594. for (NSXMLElement *method in methods)
  1595. {
  1596. if ([[method stringValue] isEqualToString:compressionMethod])
  1597. {
  1598. result = YES;
  1599. break;
  1600. }
  1601. }
  1602. }
  1603. }};
  1604. if (dispatch_get_specific(xmppQueueTag))
  1605. block();
  1606. else
  1607. dispatch_sync(xmppQueue, block);
  1608. return result;
  1609. }
  1610. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1611. #pragma mark General Methods
  1612. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1613. /**
  1614. * This method will return the root element of the document.
  1615. * This element contains the opening <stream:stream/> and <stream:features/> tags received from the server
  1616. * when the XML stream was opened.
  1617. *
  1618. * Note: The rootElement is empty, and does not contain all the XML elements the stream has received during it's
  1619. * connection. This is done for performance reasons and for the obvious benefit of being more memory efficient.
  1620. **/
  1621. - (NSXMLElement *)rootElement
  1622. {
  1623. if (dispatch_get_specific(xmppQueueTag))
  1624. {
  1625. return rootElement;
  1626. }
  1627. else
  1628. {
  1629. __block NSXMLElement *result = nil;
  1630. dispatch_sync(xmppQueue, ^{
  1631. result = [rootElement copy];
  1632. });
  1633. return result;
  1634. }
  1635. }
  1636. /**
  1637. * Returns the version attribute from the servers's <stream:stream/> element.
  1638. * This should be at least 1.0 to be RFC 3920 compliant.
  1639. * If no version number was set, the server is not RFC compliant, and 0 is returned.
  1640. **/
  1641. - (float)serverXmppStreamVersionNumber
  1642. {
  1643. if (dispatch_get_specific(xmppQueueTag))
  1644. {
  1645. return [rootElement attributeFloatValueForName:@"version" withDefaultValue:0.0F];
  1646. }
  1647. else
  1648. {
  1649. __block float result;
  1650. dispatch_sync(xmppQueue, ^{
  1651. result = [rootElement attributeFloatValueForName:@"version" withDefaultValue:0.0F];
  1652. });
  1653. return result;
  1654. }
  1655. }
  1656. - (void)sendIQ:(XMPPIQ *)iq withTag:(long)tag
  1657. {
  1658. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1659. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1660. // We're getting ready to send an IQ.
  1661. // Notify delegates to allow them to optionally alter/filter the outgoing IQ.
  1662. SEL selector = @selector(xmppStream:willSendIQ:);
  1663. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  1664. {
  1665. // None of the delegates implement the method.
  1666. // Use a shortcut.
  1667. [self continueSendIQ:iq withTag:tag];
  1668. }
  1669. else
  1670. {
  1671. // Notify all interested delegates.
  1672. // This must be done serially to allow them to alter the element in a thread-safe manner.
  1673. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  1674. dispatch_async(willSendIqQueue, ^{ @autoreleasepool {
  1675. // Allow delegates to modify and/or filter outgoing element
  1676. __block XMPPIQ *modifiedIQ = iq;
  1677. id del;
  1678. dispatch_queue_t dq;
  1679. while (modifiedIQ && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  1680. {
  1681. #if DEBUG
  1682. {
  1683. char methodReturnType[32];
  1684. Method method = class_getInstanceMethod([del class], selector);
  1685. method_getReturnType(method, methodReturnType, sizeof(methodReturnType));
  1686. if (strcmp(methodReturnType, @encode(XMPPIQ*)) != 0)
  1687. {
  1688. NSAssert(NO, @"Method xmppStream:willSendIQ: is no longer void (see XMPPStream.h). "
  1689. @"Culprit = %@", NSStringFromClass([del class]));
  1690. }
  1691. }
  1692. #endif
  1693. dispatch_sync(dq, ^{ @autoreleasepool {
  1694. modifiedIQ = [del xmppStream:self willSendIQ:modifiedIQ];
  1695. }});
  1696. }
  1697. if (modifiedIQ)
  1698. {
  1699. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  1700. if (state == STATE_XMPP_CONNECTED) {
  1701. [self continueSendIQ:modifiedIQ withTag:tag];
  1702. }
  1703. }});
  1704. }
  1705. }});
  1706. }
  1707. }
  1708. - (void)sendMessage:(XMPPMessage *)message withTag:(long)tag
  1709. {
  1710. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1711. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1712. // We're getting ready to send a message.
  1713. // Notify delegates to allow them to optionally alter/filter the outgoing message.
  1714. SEL selector = @selector(xmppStream:willSendMessage:);
  1715. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  1716. {
  1717. // None of the delegates implement the method.
  1718. // Use a shortcut.
  1719. [self continueSendMessage:message withTag:tag];
  1720. }
  1721. else
  1722. {
  1723. // Notify all interested delegates.
  1724. // This must be done serially to allow them to alter the element in a thread-safe manner.
  1725. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  1726. dispatch_async(willSendMessageQueue, ^{ @autoreleasepool {
  1727. // Allow delegates to modify outgoing element
  1728. __block XMPPMessage *modifiedMessage = message;
  1729. id del;
  1730. dispatch_queue_t dq;
  1731. while (modifiedMessage && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  1732. {
  1733. #if DEBUG
  1734. {
  1735. char methodReturnType[32];
  1736. Method method = class_getInstanceMethod([del class], selector);
  1737. method_getReturnType(method, methodReturnType, sizeof(methodReturnType));
  1738. if (strcmp(methodReturnType, @encode(XMPPMessage*)) != 0)
  1739. {
  1740. NSAssert(NO, @"Method xmppStream:willSendMessage: is no longer void (see XMPPStream.h). "
  1741. @"Culprit = %@", NSStringFromClass([del class]));
  1742. }
  1743. }
  1744. #endif
  1745. dispatch_sync(dq, ^{ @autoreleasepool {
  1746. modifiedMessage = [del xmppStream:self willSendMessage:modifiedMessage];
  1747. }});
  1748. }
  1749. if (modifiedMessage)
  1750. {
  1751. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  1752. if (state == STATE_XMPP_CONNECTED) {
  1753. [self continueSendMessage:modifiedMessage withTag:tag];
  1754. }
  1755. }});
  1756. }
  1757. }});
  1758. }
  1759. }
  1760. - (void)sendPresence:(XMPPPresence *)presence withTag:(long)tag
  1761. {
  1762. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1763. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1764. // We're getting ready to send a presence element.
  1765. // Notify delegates to allow them to optionally alter/filter the outgoing presence.
  1766. SEL selector = @selector(xmppStream:willSendPresence:);
  1767. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  1768. {
  1769. // None of the delegates implement the method.
  1770. // Use a shortcut.
  1771. [self continueSendPresence:presence withTag:tag];
  1772. }
  1773. else
  1774. {
  1775. // Notify all interested delegates.
  1776. // This must be done serially to allow them to alter the element in a thread-safe manner.
  1777. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  1778. dispatch_async(willSendPresenceQueue, ^{ @autoreleasepool {
  1779. // Allow delegates to modify outgoing element
  1780. __block XMPPPresence *modifiedPresence = presence;
  1781. id del;
  1782. dispatch_queue_t dq;
  1783. while (modifiedPresence && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  1784. {
  1785. #if DEBUG
  1786. {
  1787. char methodReturnType[32];
  1788. Method method = class_getInstanceMethod([del class], selector);
  1789. method_getReturnType(method, methodReturnType, sizeof(methodReturnType));
  1790. if (strcmp(methodReturnType, @encode(XMPPPresence*)) != 0)
  1791. {
  1792. NSAssert(NO, @"Method xmppStream:willSendPresence: is no longer void (see XMPPStream.h). "
  1793. @"Culprit = %@", NSStringFromClass([del class]));
  1794. }
  1795. }
  1796. #endif
  1797. dispatch_sync(dq, ^{ @autoreleasepool {
  1798. modifiedPresence = [del xmppStream:self willSendPresence:modifiedPresence];
  1799. }});
  1800. }
  1801. if (modifiedPresence)
  1802. {
  1803. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  1804. if (state == STATE_XMPP_CONNECTED) {
  1805. [self continueSendPresence:presence withTag:tag];
  1806. }
  1807. }});
  1808. }
  1809. }});
  1810. }
  1811. }
  1812. - (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag
  1813. {
  1814. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1815. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1816. NSString *outgoingStr = [iq compactXMLString];
  1817. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1818. XMPPLogSend(@"SEND: %@", outgoingStr);
  1819. numberOfBytesSent += [outgoingData length];
  1820. [asyncSocket writeData:outgoingData
  1821. withTimeout:TIMEOUT_XMPP_WRITE
  1822. tag:tag];
  1823. [multicastDelegate xmppStream:self didSendIQ:iq];
  1824. }
  1825. - (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag
  1826. {
  1827. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1828. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1829. NSString *outgoingStr = [message compactXMLString];
  1830. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1831. XMPPLogSend(@"SEND: %@", outgoingStr);
  1832. numberOfBytesSent += [outgoingData length];
  1833. [asyncSocket writeData:outgoingData
  1834. withTimeout:TIMEOUT_XMPP_WRITE
  1835. tag:tag];
  1836. [multicastDelegate xmppStream:self didSendMessage:message];
  1837. }
  1838. - (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag
  1839. {
  1840. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1841. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1842. NSString *outgoingStr = [presence compactXMLString];
  1843. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1844. XMPPLogSend(@"SEND: %@", outgoingStr);
  1845. numberOfBytesSent += [outgoingData length];
  1846. [asyncSocket writeData:outgoingData
  1847. withTimeout:TIMEOUT_XMPP_WRITE
  1848. tag:tag];
  1849. // Update myPresence if this is a normal presence element.
  1850. // In other words, ignore presence subscription stuff, MUC room stuff, etc.
  1851. //
  1852. // We use the built-in [presence type] which guarantees lowercase strings,
  1853. // and will return @"available" if there was no set type (as available is implicit).
  1854. NSString *type = [presence type];
  1855. if ([type isEqualToString:@"available"] || [type isEqualToString:@"unavailable"])
  1856. {
  1857. if ([presence toStr] == nil && myPresence != presence)
  1858. {
  1859. myPresence = presence;
  1860. }
  1861. }
  1862. [multicastDelegate xmppStream:self didSendPresence:presence];
  1863. }
  1864. - (void)continueSendElement:(NSXMLElement *)element withTag:(long)tag
  1865. {
  1866. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1867. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1868. NSString *outgoingStr = [element compactXMLString];
  1869. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1870. XMPPLogSend(@"SEND: %@", outgoingStr);
  1871. numberOfBytesSent += [outgoingData length];
  1872. [asyncSocket writeData:outgoingData
  1873. withTimeout:TIMEOUT_XMPP_WRITE
  1874. tag:tag];
  1875. }
  1876. /**
  1877. * Private method.
  1878. * Presencts a common method for the various public sendElement methods.
  1879. **/
  1880. - (void)sendElement:(NSXMLElement *)element withTag:(long)tag
  1881. {
  1882. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1883. if ([element isKindOfClass:[XMPPIQ class]])
  1884. {
  1885. [self sendIQ:(XMPPIQ *)element withTag:tag];
  1886. }
  1887. else if ([element isKindOfClass:[XMPPMessage class]])
  1888. {
  1889. [self sendMessage:(XMPPMessage *)element withTag:tag];
  1890. }
  1891. else if ([element isKindOfClass:[XMPPPresence class]])
  1892. {
  1893. [self sendPresence:(XMPPPresence *)element withTag:tag];
  1894. }
  1895. else
  1896. {
  1897. NSString *elementName = [element name];
  1898. if ([elementName isEqualToString:@"iq"])
  1899. {
  1900. [self sendIQ:[XMPPIQ iqFromElement:element] withTag:tag];
  1901. }
  1902. else if ([elementName isEqualToString:@"message"])
  1903. {
  1904. [self sendMessage:[XMPPMessage messageFromElement:element] withTag:tag];
  1905. }
  1906. else if ([elementName isEqualToString:@"presence"])
  1907. {
  1908. [self sendPresence:[XMPPPresence presenceFromElement:element] withTag:tag];
  1909. }
  1910. else
  1911. {
  1912. [self continueSendElement:element withTag:tag];
  1913. }
  1914. }
  1915. }
  1916. /**
  1917. * This methods handles sending an XML stanza.
  1918. * If the XMPPStream is not connected, this method does nothing.
  1919. **/
  1920. - (void)sendElement:(NSXMLElement *)element
  1921. {
  1922. if (element == nil) return;
  1923. dispatch_block_t block = ^{ @autoreleasepool {
  1924. if (state == STATE_XMPP_CONNECTED)
  1925. {
  1926. [self sendElement:element withTag:TAG_XMPP_WRITE_STREAM];
  1927. }
  1928. else
  1929. {
  1930. NSError *error = [NSError errorWithDomain:XMPPStreamErrorDomain
  1931. code:XMPPStreamInvalidState
  1932. userInfo:nil];
  1933. [self failToSendElement:element error:error];
  1934. }
  1935. }};
  1936. if (dispatch_get_specific(xmppQueueTag))
  1937. block();
  1938. else
  1939. dispatch_async(xmppQueue, block);
  1940. }
  1941. /**
  1942. * This method handles sending an XML stanza.
  1943. * If the XMPPStream is not connected, this method does nothing.
  1944. *
  1945. * After the element has been successfully sent,
  1946. * the xmppStream:didSendElementWithTag: delegate method is called.
  1947. **/
  1948. - (void)sendElement:(NSXMLElement *)element andGetReceipt:(XMPPElementReceipt **)receiptPtr
  1949. {
  1950. if (element == nil) return;
  1951. if (receiptPtr == nil)
  1952. {
  1953. [self sendElement:element];
  1954. }
  1955. else
  1956. {
  1957. __block XMPPElementReceipt *receipt = nil;
  1958. dispatch_block_t block = ^{ @autoreleasepool {
  1959. if (state == STATE_XMPP_CONNECTED)
  1960. {
  1961. receipt = [[XMPPElementReceipt alloc] init];
  1962. [receipts addObject:receipt];
  1963. [self sendElement:element withTag:TAG_XMPP_WRITE_RECEIPT];
  1964. }
  1965. else
  1966. {
  1967. NSError *error = [NSError errorWithDomain:XMPPStreamErrorDomain
  1968. code:XMPPStreamInvalidState
  1969. userInfo:nil];
  1970. [self failToSendElement:element error:error];
  1971. }
  1972. }};
  1973. if (dispatch_get_specific(xmppQueueTag))
  1974. block();
  1975. else
  1976. dispatch_sync(xmppQueue, block);
  1977. *receiptPtr = receipt;
  1978. }
  1979. }
  1980. - (void)failToSendElement:(NSXMLElement *)element error:(NSError *)error
  1981. {
  1982. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  1983. if ([element isKindOfClass:[XMPPIQ class]])
  1984. {
  1985. [self failToSendIQ:(XMPPIQ *)element error:error];
  1986. }
  1987. else if ([element isKindOfClass:[XMPPMessage class]])
  1988. {
  1989. [self failToSendMessage:(XMPPMessage *)element error:error];
  1990. }
  1991. else if ([element isKindOfClass:[XMPPPresence class]])
  1992. {
  1993. [self failToSendPresence:(XMPPPresence *)element error:error];
  1994. }
  1995. else
  1996. {
  1997. NSString *elementName = [element name];
  1998. if ([elementName isEqualToString:@"iq"])
  1999. {
  2000. [self failToSendIQ:[XMPPIQ iqFromElement:element] error:error];
  2001. }
  2002. else if ([elementName isEqualToString:@"message"])
  2003. {
  2004. [self failToSendMessage:[XMPPMessage messageFromElement:element] error:error];
  2005. }
  2006. else if ([elementName isEqualToString:@"presence"])
  2007. {
  2008. [self failToSendPresence:[XMPPPresence presenceFromElement:element] error:error];
  2009. }
  2010. }
  2011. }
  2012. - (void)failToSendIQ:(XMPPIQ *)iq error:(NSError *)error
  2013. {
  2014. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2015. [multicastDelegate xmppStream:self didFailToSendIQ:iq error:error];
  2016. }
  2017. - (void)failToSendMessage:(XMPPMessage *)message error:(NSError *)error
  2018. {
  2019. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2020. [multicastDelegate xmppStream:self didFailToSendMessage:message error:error];
  2021. }
  2022. - (void)failToSendPresence:(XMPPPresence *)presence error:(NSError *)error
  2023. {
  2024. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2025. [multicastDelegate xmppStream:self didFailToSendPresence:presence error:error];
  2026. }
  2027. /**
  2028. * Retrieves the current presence and resends it in once atomic operation.
  2029. * Useful for various components that need to update injected information in the presence stanza.
  2030. **/
  2031. - (void)resendMyPresence
  2032. {
  2033. dispatch_block_t block = ^{ @autoreleasepool {
  2034. if (myPresence && [[myPresence type] isEqualToString:@"available"])
  2035. {
  2036. [self sendElement:myPresence];
  2037. }
  2038. }};
  2039. if (dispatch_get_specific(xmppQueueTag))
  2040. block();
  2041. else
  2042. dispatch_async(xmppQueue, block);
  2043. }
  2044. /**
  2045. * This method is for use by xmpp authentication mechanism classes.
  2046. * They should send elements using this method instead of the public sendElement classes,
  2047. * as those methods don't send the elements while authentication is in progress.
  2048. **/
  2049. - (void)sendAuthElement:(NSXMLElement *)element
  2050. {
  2051. dispatch_block_t block = ^{ @autoreleasepool {
  2052. if (state == STATE_XMPP_AUTH)
  2053. {
  2054. NSString *outgoingStr = [element compactXMLString];
  2055. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2056. XMPPLogSend(@"SEND: %@", outgoingStr);
  2057. numberOfBytesSent += [outgoingData length];
  2058. [asyncSocket writeData:outgoingData
  2059. withTimeout:TIMEOUT_XMPP_WRITE
  2060. tag:TAG_XMPP_WRITE_STREAM];
  2061. }
  2062. else
  2063. {
  2064. XMPPLogWarn(@"Unable to send element while not in STATE_XMPP_AUTH: %@", [element compactXMLString]);
  2065. }
  2066. }};
  2067. if (dispatch_get_specific(xmppQueueTag))
  2068. block();
  2069. else
  2070. dispatch_async(xmppQueue, block);
  2071. }
  2072. - (void)receiveIQ:(XMPPIQ *)iq
  2073. {
  2074. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2075. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2076. // We're getting ready to receive an IQ.
  2077. // Notify delegates to allow them to optionally alter/filter the incoming IQ element.
  2078. SEL selector = @selector(xmppStream:willReceiveIQ:);
  2079. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2080. {
  2081. // None of the delegates implement the method.
  2082. // Use a shortcut.
  2083. [self continueReceiveIQ:iq];
  2084. }
  2085. else
  2086. {
  2087. // Notify all interested delegates.
  2088. // This must be done serially to allow them to alter the element in a thread-safe manner.
  2089. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2090. dispatch_async(willReceiveIqQueue, ^{ @autoreleasepool {
  2091. // Allow delegates to modify and/or filter incoming element
  2092. __block XMPPIQ *modifiedIQ = iq;
  2093. id del;
  2094. dispatch_queue_t dq;
  2095. while (modifiedIQ && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2096. {
  2097. dispatch_sync(dq, ^{ @autoreleasepool {
  2098. modifiedIQ = [del xmppStream:self willReceiveIQ:modifiedIQ];
  2099. }});
  2100. }
  2101. if (modifiedIQ)
  2102. {
  2103. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2104. if (state == STATE_XMPP_CONNECTED) {
  2105. [self continueReceiveIQ:modifiedIQ];
  2106. }
  2107. }});
  2108. }
  2109. }});
  2110. }
  2111. }
  2112. - (void)receiveMessage:(XMPPMessage *)message
  2113. {
  2114. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2115. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2116. // We're getting ready to receive a message.
  2117. // Notify delegates to allow them to optionally alter/filter the incoming message.
  2118. SEL selector = @selector(xmppStream:willReceiveMessage:);
  2119. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2120. {
  2121. // None of the delegates implement the method.
  2122. // Use a shortcut.
  2123. [self continueReceiveMessage:message];
  2124. }
  2125. else
  2126. {
  2127. // Notify all interested delegates.
  2128. // This must be done serially to allow them to alter the element in a thread-safe manner.
  2129. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2130. dispatch_async(willReceiveMessageQueue, ^{ @autoreleasepool {
  2131. // Allow delegates to modify incoming element
  2132. __block XMPPMessage *modifiedMessage = message;
  2133. id del;
  2134. dispatch_queue_t dq;
  2135. while (modifiedMessage && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2136. {
  2137. dispatch_sync(dq, ^{ @autoreleasepool {
  2138. modifiedMessage = [del xmppStream:self willReceiveMessage:modifiedMessage];
  2139. }});
  2140. }
  2141. if (modifiedMessage)
  2142. {
  2143. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2144. if (state == STATE_XMPP_CONNECTED) {
  2145. [self continueReceiveMessage:modifiedMessage];
  2146. }
  2147. }});
  2148. }
  2149. }});
  2150. }
  2151. }
  2152. - (void)receivePresence:(XMPPPresence *)presence
  2153. {
  2154. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2155. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  2156. // We're getting ready to receive a presence element.
  2157. // Notify delegates to allow them to optionally alter/filter the incoming presence.
  2158. SEL selector = @selector(xmppStream:willReceivePresence:);
  2159. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2160. {
  2161. // None of the delegates implement the method.
  2162. // Use a shortcut.
  2163. [self continueReceivePresence:presence];
  2164. }
  2165. else
  2166. {
  2167. // Notify all interested delegates.
  2168. // This must be done serially to allow them to alter the element in a thread-safe manner.
  2169. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2170. dispatch_async(willSendPresenceQueue, ^{ @autoreleasepool {
  2171. // Allow delegates to modify outgoing element
  2172. __block XMPPPresence *modifiedPresence = presence;
  2173. id del;
  2174. dispatch_queue_t dq;
  2175. while (modifiedPresence && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2176. {
  2177. dispatch_sync(dq, ^{ @autoreleasepool {
  2178. modifiedPresence = [del xmppStream:self willReceivePresence:modifiedPresence];
  2179. }});
  2180. }
  2181. if (modifiedPresence)
  2182. {
  2183. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2184. if (state == STATE_XMPP_CONNECTED) {
  2185. [self continueReceivePresence:presence];
  2186. }
  2187. }});
  2188. }
  2189. }});
  2190. }
  2191. }
  2192. - (void)continueReceiveIQ:(XMPPIQ *)iq
  2193. {
  2194. if ([iq requiresResponse])
  2195. {
  2196. // As per the XMPP specificiation, if the IQ requires a response,
  2197. // and we don't have any delegates or modules that can properly respond to the IQ,
  2198. // we MUST send back and error IQ.
  2199. //
  2200. // So we notifiy all interested delegates and modules about the received IQ,
  2201. // keeping track of whether or not any of them have handled it.
  2202. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2203. id del;
  2204. dispatch_queue_t dq;
  2205. SEL selector = @selector(xmppStream:didReceiveIQ:);
  2206. dispatch_semaphore_t delSemaphore = dispatch_semaphore_create(0);
  2207. dispatch_group_t delGroup = dispatch_group_create();
  2208. while ([delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  2209. {
  2210. dispatch_group_async(delGroup, dq, ^{ @autoreleasepool {
  2211. if ([del xmppStream:self didReceiveIQ:iq])
  2212. {
  2213. dispatch_semaphore_signal(delSemaphore);
  2214. }
  2215. }});
  2216. }
  2217. dispatch_async(didReceiveIqQueue, ^{ @autoreleasepool {
  2218. dispatch_group_wait(delGroup, DISPATCH_TIME_FOREVER);
  2219. // Did any of the delegates handle the IQ? (handle == will response)
  2220. BOOL handled = (dispatch_semaphore_wait(delSemaphore, DISPATCH_TIME_NOW) == 0);
  2221. // An entity that receives an IQ request of type "get" or "set" MUST reply
  2222. // with an IQ response of type "result" or "error".
  2223. //
  2224. // The response MUST preserve the 'id' attribute of the request.
  2225. if (!handled)
  2226. {
  2227. // Return error message:
  2228. //
  2229. // <iq to="jid" type="error" id="id">
  2230. // <query xmlns="ns"/>
  2231. // <error type="cancel" code="501">
  2232. // <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
  2233. // </error>
  2234. // </iq>
  2235. NSXMLElement *reason = [NSXMLElement elementWithName:@"feature-not-implemented"
  2236. xmlns:@"urn:ietf:params:xml:ns:xmpp-stanzas"];
  2237. NSXMLElement *error = [NSXMLElement elementWithName:@"error"];
  2238. [error addAttributeWithName:@"type" stringValue:@"cancel"];
  2239. [error addAttributeWithName:@"code" stringValue:@"501"];
  2240. [error addChild:reason];
  2241. XMPPIQ *iqResponse = [XMPPIQ iqWithType:@"error"
  2242. to:[iq from]
  2243. elementID:[iq elementID]
  2244. child:error];
  2245. NSXMLElement *iqChild = [iq childElement];
  2246. if (iqChild)
  2247. {
  2248. NSXMLNode *iqChildCopy = [iqChild copy];
  2249. [iqResponse insertChild:iqChildCopy atIndex:0];
  2250. }
  2251. // Purposefully go through the sendElement: method
  2252. // so that it gets dispatched onto the xmppQueue,
  2253. // and so that modules may get notified of the outgoing error message.
  2254. [self sendElement:iqResponse];
  2255. }
  2256. #if !OS_OBJECT_USE_OBJC
  2257. dispatch_release(delSemaphore);
  2258. dispatch_release(delGroup);
  2259. #endif
  2260. }});
  2261. }
  2262. else
  2263. {
  2264. // The IQ doesn't require a response.
  2265. // So we can just fire the delegate method and ignore the responses.
  2266. [multicastDelegate xmppStream:self didReceiveIQ:iq];
  2267. }
  2268. }
  2269. - (void)continueReceiveMessage:(XMPPMessage *)message
  2270. {
  2271. [multicastDelegate xmppStream:self didReceiveMessage:message];
  2272. }
  2273. - (void)continueReceivePresence:(XMPPPresence *)presence
  2274. {
  2275. [multicastDelegate xmppStream:self didReceivePresence:presence];
  2276. }
  2277. /**
  2278. * This method allows you to inject an element into the stream as if it was received on the socket.
  2279. * This is an advanced technique, but makes for some interesting possibilities.
  2280. **/
  2281. - (void)injectElement:(NSXMLElement *)element
  2282. {
  2283. if (element == nil) return;
  2284. dispatch_block_t block = ^{ @autoreleasepool {
  2285. if (state != STATE_XMPP_CONNECTED)
  2286. {
  2287. return_from_block;
  2288. }
  2289. if ([element isKindOfClass:[XMPPIQ class]])
  2290. {
  2291. [self receiveIQ:(XMPPIQ *)element];
  2292. }
  2293. else if ([element isKindOfClass:[XMPPMessage class]])
  2294. {
  2295. [self receiveMessage:(XMPPMessage *)element];
  2296. }
  2297. else if ([element isKindOfClass:[XMPPPresence class]])
  2298. {
  2299. [self receivePresence:(XMPPPresence *)element];
  2300. }
  2301. else
  2302. {
  2303. NSString *elementName = [element name];
  2304. if ([elementName isEqualToString:@"iq"])
  2305. {
  2306. [self receiveIQ:[XMPPIQ iqFromElement:element]];
  2307. }
  2308. else if ([elementName isEqualToString:@"message"])
  2309. {
  2310. [self receiveMessage:[XMPPMessage messageFromElement:element]];
  2311. }
  2312. else if ([elementName isEqualToString:@"presence"])
  2313. {
  2314. [self receivePresence:[XMPPPresence presenceFromElement:element]];
  2315. }
  2316. else
  2317. {
  2318. [multicastDelegate xmppStream:self didReceiveError:element];
  2319. }
  2320. }
  2321. }};
  2322. if (dispatch_get_specific(xmppQueueTag))
  2323. block();
  2324. else
  2325. dispatch_async(xmppQueue, block);
  2326. }
  2327. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2328. #pragma mark Stream Negotiation
  2329. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2330. /**
  2331. * This method is called to start the initial negotiation process.
  2332. **/
  2333. - (void)startNegotiation
  2334. {
  2335. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2336. NSAssert(![self didStartNegotiation], @"Invoked after initial negotiation has started");
  2337. XMPPLogTrace();
  2338. // Initialize the XML stream
  2339. [self sendOpeningNegotiation];
  2340. // Inform delegate that the TCP connection is open, and the stream handshake has begun
  2341. [multicastDelegate xmppStreamDidStartNegotiation:self];
  2342. // And start reading in the server's XML stream
  2343. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
  2344. }
  2345. /**
  2346. * This method handles sending the opening <stream:stream ...> element which is needed in several situations.
  2347. **/
  2348. - (void)sendOpeningNegotiation
  2349. {
  2350. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2351. XMPPLogTrace();
  2352. if (![self didStartNegotiation])
  2353. {
  2354. // TCP connection was just opened - We need to include the opening XML stanza
  2355. NSString *s1 = @"<?xml version='1.0'?>";
  2356. NSData *outgoingData = [s1 dataUsingEncoding:NSUTF8StringEncoding];
  2357. XMPPLogSend(@"SEND: %@", s1);
  2358. numberOfBytesSent += [outgoingData length];
  2359. [asyncSocket writeData:outgoingData
  2360. withTimeout:TIMEOUT_XMPP_WRITE
  2361. tag:TAG_XMPP_WRITE_START];
  2362. [self setDidStartNegotiation:YES];
  2363. }
  2364. if (parser == nil)
  2365. {
  2366. XMPPLogVerbose(@"%@: Initializing parser...", THIS_FILE);
  2367. // Need to create the parser.
  2368. parser = [[XMPPParser alloc] initWithDelegate:self delegateQueue:xmppQueue];
  2369. }
  2370. else
  2371. {
  2372. XMPPLogVerbose(@"%@: Resetting parser...", THIS_FILE);
  2373. // We're restarting our negotiation, so we need to reset the parser.
  2374. parser = [[XMPPParser alloc] initWithDelegate:self delegateQueue:xmppQueue];
  2375. }
  2376. NSString *xmlns = @"jabber:client";
  2377. NSString *xmlns_stream = @"http://etherx.jabber.org/streams";
  2378. NSString *temp, *s2;
  2379. if ([self isP2P])
  2380. {
  2381. if (myJID_setByClient && remoteJID)
  2382. {
  2383. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' from='%@' to='%@'>";
  2384. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, [myJID_setByClient bare], [remoteJID bare]];
  2385. }
  2386. else if (myJID_setByClient)
  2387. {
  2388. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' from='%@'>";
  2389. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, [myJID_setByClient bare]];
  2390. }
  2391. else if (remoteJID)
  2392. {
  2393. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' to='%@'>";
  2394. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, [remoteJID bare]];
  2395. }
  2396. else
  2397. {
  2398. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0'>";
  2399. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream];
  2400. }
  2401. }
  2402. else
  2403. {
  2404. if (myJID_setByClient)
  2405. {
  2406. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' to='%@'>";
  2407. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, [myJID_setByClient domain]];
  2408. }
  2409. else if ([hostName length] > 0)
  2410. {
  2411. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0' to='%@'>";
  2412. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream, hostName];
  2413. }
  2414. else
  2415. {
  2416. temp = @"<stream:stream xmlns='%@' xmlns:stream='%@' version='1.0'>";
  2417. s2 = [NSString stringWithFormat:temp, xmlns, xmlns_stream];
  2418. }
  2419. }
  2420. NSData *outgoingData = [s2 dataUsingEncoding:NSUTF8StringEncoding];
  2421. XMPPLogSend(@"SEND: %@", s2);
  2422. numberOfBytesSent += [outgoingData length];
  2423. [asyncSocket writeData:outgoingData
  2424. withTimeout:TIMEOUT_XMPP_WRITE
  2425. tag:TAG_XMPP_WRITE_START];
  2426. // Update status
  2427. state = STATE_XMPP_OPENING;
  2428. }
  2429. /**
  2430. * This method handles starting TLS negotiation on the socket, using the proper settings.
  2431. **/
  2432. - (void)startTLS
  2433. {
  2434. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2435. XMPPLogTrace();
  2436. // Update state (part 2 - prompting delegates)
  2437. state = STATE_XMPP_STARTTLS_2;
  2438. // Create a mutable dictionary for security settings
  2439. NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:5];
  2440. SEL selector = @selector(xmppStream:willSecureWithSettings:);
  2441. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2442. {
  2443. // None of the delegates implement the method.
  2444. // Use a shortcut.
  2445. [self continueStartTLS:settings];
  2446. }
  2447. else
  2448. {
  2449. // Query all interested delegates.
  2450. // This must be done serially to maintain thread safety.
  2451. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2452. dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  2453. dispatch_async(concurrentQueue, ^{ @autoreleasepool {
  2454. // Prompt the delegate(s) to populate the security settings
  2455. id delegate;
  2456. dispatch_queue_t delegateQueue;
  2457. while ([delegateEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue forSelector:selector])
  2458. {
  2459. dispatch_sync(delegateQueue, ^{ @autoreleasepool {
  2460. [delegate xmppStream:self willSecureWithSettings:settings];
  2461. }});
  2462. }
  2463. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2464. [self continueStartTLS:settings];
  2465. }});
  2466. }});
  2467. }
  2468. }
  2469. - (void)continueStartTLS:(NSMutableDictionary *)settings
  2470. {
  2471. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2472. XMPPLogTrace2(@"%@: %@ %@", THIS_FILE, THIS_METHOD, settings);
  2473. if (state == STATE_XMPP_STARTTLS_2)
  2474. {
  2475. // If the delegates didn't respond
  2476. if ([settings count] == 0)
  2477. {
  2478. // Use the default settings, and set the peer name
  2479. NSString *expectedCertName = hostName;
  2480. if (expectedCertName == nil)
  2481. {
  2482. expectedCertName = [myJID_setByClient domain];
  2483. }
  2484. if ([expectedCertName length] > 0)
  2485. {
  2486. [settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
  2487. }
  2488. }
  2489. [asyncSocket startTLS:settings];
  2490. [self setIsSecure:YES];
  2491. // Note: We don't need to wait for asyncSocket to complete TLS negotiation.
  2492. // We can just continue reading/writing to the socket, and it will handle queueing everything for us!
  2493. if ([self didStartNegotiation])
  2494. {
  2495. // Now we start our negotiation over again...
  2496. [self sendOpeningNegotiation];
  2497. // We paused reading from the socket.
  2498. // We're ready to continue now.
  2499. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
  2500. }
  2501. else
  2502. {
  2503. // First time starting negotiation
  2504. [self startNegotiation];
  2505. }
  2506. }
  2507. }
  2508. /**
  2509. * This method is called anytime we receive the server's stream features.
  2510. * This method looks at the stream features, and handles any requirements so communication can continue.
  2511. **/
  2512. - (void)handleStreamFeatures
  2513. {
  2514. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2515. XMPPLogTrace();
  2516. // Extract the stream features
  2517. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  2518. // Check to see if TLS is required
  2519. // Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
  2520. NSXMLElement *f_starttls = [features elementForName:@"starttls" xmlns:@"urn:ietf:params:xml:ns:xmpp-tls"];
  2521. if (f_starttls)
  2522. {
  2523. if ([f_starttls elementForName:@"required"] || [self autoStartTLS])
  2524. {
  2525. // TLS is required for this connection
  2526. // Update state
  2527. state = STATE_XMPP_STARTTLS_1;
  2528. // Send the startTLS XML request
  2529. [self sendStartTLSRequest];
  2530. // We do not mark the stream as secure yet.
  2531. // We're waiting to receive the <proceed/> response from the
  2532. // server before we actually start the TLS handshake.
  2533. // We're already listening for the response...
  2534. return;
  2535. }
  2536. }
  2537. // Check to see if resource binding is required
  2538. // Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
  2539. NSXMLElement *f_bind = [features elementForName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2540. if (f_bind)
  2541. {
  2542. // Binding is required for this connection
  2543. state = STATE_XMPP_BINDING;
  2544. NSString *requestedResource = [myJID_setByClient resource];
  2545. if ([requestedResource length] > 0)
  2546. {
  2547. // Ask the server to bind the user specified resource
  2548. NSXMLElement *resource = [NSXMLElement elementWithName:@"resource"];
  2549. [resource setStringValue:requestedResource];
  2550. NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2551. [bind addChild:resource];
  2552. NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
  2553. [iq addAttributeWithName:@"type" stringValue:@"set"];
  2554. [iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
  2555. [iq addChild:bind];
  2556. NSString *outgoingStr = [iq compactXMLString];
  2557. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2558. XMPPLogSend(@"SEND: %@", outgoingStr);
  2559. numberOfBytesSent += [outgoingData length];
  2560. [asyncSocket writeData:outgoingData
  2561. withTimeout:TIMEOUT_XMPP_WRITE
  2562. tag:TAG_XMPP_WRITE_STREAM];
  2563. }
  2564. else
  2565. {
  2566. // The user didn't specify a resource, so we ask the server to bind one for us
  2567. NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2568. NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
  2569. [iq addAttributeWithName:@"type" stringValue:@"set"];
  2570. [iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
  2571. [iq addChild:bind];
  2572. NSString *outgoingStr = [iq compactXMLString];
  2573. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2574. XMPPLogSend(@"SEND: %@", outgoingStr);
  2575. numberOfBytesSent += [outgoingData length];
  2576. [asyncSocket writeData:outgoingData
  2577. withTimeout:TIMEOUT_XMPP_WRITE
  2578. tag:TAG_XMPP_WRITE_STREAM];
  2579. }
  2580. // We're already listening for the response...
  2581. return;
  2582. }
  2583. // It looks like all has gone well, and the connection should be ready to use now
  2584. state = STATE_XMPP_CONNECTED;
  2585. if (![self isAuthenticated])
  2586. {
  2587. [self setupKeepAliveTimer];
  2588. // Notify delegates
  2589. [multicastDelegate xmppStreamDidConnect:self];
  2590. }
  2591. }
  2592. - (void)handleStartTLSResponse:(NSXMLElement *)response
  2593. {
  2594. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2595. XMPPLogTrace();
  2596. // We're expecting a proceed response
  2597. // If we get anything else we can safely assume it's the equivalent of a failure response
  2598. if ( ![[response name] isEqualToString:@"proceed"])
  2599. {
  2600. // We can close our TCP connection now
  2601. [self disconnect];
  2602. // The socketDidDisconnect:withError: method will handle everything else
  2603. return;
  2604. }
  2605. // Start TLS negotiation
  2606. [self startTLS];
  2607. }
  2608. /**
  2609. * After the registerUser:withPassword: method is invoked, a registration message is sent to the server.
  2610. * We're waiting for the result from this registration request.
  2611. **/
  2612. - (void)handleRegistration:(NSXMLElement *)response
  2613. {
  2614. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2615. XMPPLogTrace();
  2616. if ([[response attributeStringValueForName:@"type"] isEqualToString:@"error"])
  2617. {
  2618. // Revert back to connected state (from authenticating state)
  2619. state = STATE_XMPP_CONNECTED;
  2620. [multicastDelegate xmppStream:self didNotRegister:response];
  2621. }
  2622. else
  2623. {
  2624. // Revert back to connected state (from authenticating state)
  2625. state = STATE_XMPP_CONNECTED;
  2626. [multicastDelegate xmppStreamDidRegister:self];
  2627. }
  2628. }
  2629. /**
  2630. * After the authenticate:error: or authenticateWithPassword:error: methods are invoked, some kind of
  2631. * authentication message is sent to the server.
  2632. * This method forwards the response to the authentication module, and handles the resulting authentication state.
  2633. **/
  2634. - (void)handleAuth:(NSXMLElement *)authResponse
  2635. {
  2636. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2637. XMPPLogTrace();
  2638. XMPPHandleAuthResponse result = [auth handleAuth:authResponse];
  2639. if (result == XMPP_AUTH_SUCCESS)
  2640. {
  2641. // We are successfully authenticated (via sasl:digest-md5)
  2642. [self setIsAuthenticated:YES];
  2643. BOOL shouldRenegotiate = YES;
  2644. if ([auth respondsToSelector:@selector(shouldResendOpeningNegotiationAfterSuccessfulAuthentication)])
  2645. {
  2646. shouldRenegotiate = [auth shouldResendOpeningNegotiationAfterSuccessfulAuthentication];
  2647. }
  2648. if (shouldRenegotiate)
  2649. {
  2650. // Now we start our negotiation over again...
  2651. [self sendOpeningNegotiation];
  2652. if (![self isSecure])
  2653. {
  2654. // Normally we requeue our read operation in xmppParserDidParseData:.
  2655. // But we just reset the parser, so that code path isn't going to happen.
  2656. // So start read request here.
  2657. // The state is STATE_XMPP_OPENING, set via sendOpeningNegotiation method.
  2658. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
  2659. }
  2660. }
  2661. else
  2662. {
  2663. // Revert back to connected state (from authenticating state)
  2664. state = STATE_XMPP_CONNECTED;
  2665. [multicastDelegate xmppStreamDidAuthenticate:self];
  2666. }
  2667. // Done with auth
  2668. auth = nil;
  2669. }
  2670. else if (result == XMPP_AUTH_FAIL)
  2671. {
  2672. // Revert back to connected state (from authenticating state)
  2673. state = STATE_XMPP_CONNECTED;
  2674. // Notify delegate
  2675. [multicastDelegate xmppStream:self didNotAuthenticate:authResponse];
  2676. // Done with auth
  2677. auth = nil;
  2678. }
  2679. else if (result == XMPP_AUTH_CONTINUE)
  2680. {
  2681. // Authentication continues.
  2682. // State doesn't change.
  2683. }
  2684. else
  2685. {
  2686. XMPPLogError(@"Authentication class (%@) returned invalid response code (%i)",
  2687. NSStringFromClass([auth class]), (int)result);
  2688. NSAssert(NO, @"Authentication class (%@) returned invalid response code (%i)",
  2689. NSStringFromClass([auth class]), (int)result);
  2690. }
  2691. }
  2692. - (void)handleBinding:(NSXMLElement *)response
  2693. {
  2694. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2695. XMPPLogTrace();
  2696. NSXMLElement *r_bind = [response elementForName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2697. NSXMLElement *r_jid = [r_bind elementForName:@"jid"];
  2698. if (r_jid)
  2699. {
  2700. // We're properly binded to a resource now
  2701. // Extract and save our resource (it may not be what we originally requested)
  2702. NSString *fullJIDStr = [r_jid stringValue];
  2703. [self setMyJID_setByServer:[XMPPJID jidWithString:fullJIDStr]];
  2704. // And we may now have to do one last thing before we're ready - start an IM session
  2705. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  2706. // Check to see if a session is required
  2707. // Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
  2708. NSXMLElement *f_session = [features elementForName:@"session" xmlns:@"urn:ietf:params:xml:ns:xmpp-session"];
  2709. if (f_session)
  2710. {
  2711. NSXMLElement *session = [NSXMLElement elementWithName:@"session"];
  2712. [session setXmlns:@"urn:ietf:params:xml:ns:xmpp-session"];
  2713. NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
  2714. [iq addAttributeWithName:@"type" stringValue:@"set"];
  2715. [iq addAttributeWithName:@"id" stringValue:[self generateUUID]];
  2716. [iq addChild:session];
  2717. NSString *outgoingStr = [iq compactXMLString];
  2718. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2719. XMPPLogSend(@"SEND: %@", outgoingStr);
  2720. numberOfBytesSent += [outgoingData length];
  2721. [asyncSocket writeData:outgoingData
  2722. withTimeout:TIMEOUT_XMPP_WRITE
  2723. tag:TAG_XMPP_WRITE_STREAM];
  2724. // Update state
  2725. state = STATE_XMPP_START_SESSION;
  2726. }
  2727. else
  2728. {
  2729. // Revert back to connected state (from binding state)
  2730. state = STATE_XMPP_CONNECTED;
  2731. [multicastDelegate xmppStreamDidAuthenticate:self];
  2732. }
  2733. }
  2734. else
  2735. {
  2736. // It appears the server didn't allow our resource choice
  2737. // First check if we want to try an alternative resource
  2738. NSXMLElement *r_error = [response elementForName:@"error"];
  2739. NSXMLElement *r_conflict = [r_error elementForName:@"conflict" xmlns:@"urn:ietf:params:xml:ns:xmpp-stanzas"];
  2740. if (r_conflict)
  2741. {
  2742. SEL selector = @selector(xmppStream:alternativeResourceForConflictingResource:);
  2743. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  2744. {
  2745. // None of the delegates implement the method.
  2746. // Use a shortcut.
  2747. [self continueHandleBinding:nil];
  2748. }
  2749. else
  2750. {
  2751. // Query all interested delegates.
  2752. // This must be done serially to maintain thread safety.
  2753. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  2754. dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  2755. dispatch_async(concurrentQueue, ^{ @autoreleasepool {
  2756. // Query delegates for alternative resource
  2757. NSString *currentResource = [[self myJID] resource];
  2758. __block NSString *alternativeResource = nil;
  2759. id delegate;
  2760. dispatch_queue_t dq;
  2761. while ([delegateEnumerator getNextDelegate:&delegate delegateQueue:&dq forSelector:selector])
  2762. {
  2763. dispatch_sync(dq, ^{ @autoreleasepool {
  2764. NSString *delegateAlternativeResource =
  2765. [delegate xmppStream:self alternativeResourceForConflictingResource:currentResource];
  2766. if (delegateAlternativeResource)
  2767. {
  2768. alternativeResource = delegateAlternativeResource;
  2769. }
  2770. }});
  2771. }
  2772. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  2773. [self continueHandleBinding:alternativeResource];
  2774. }});
  2775. }});
  2776. }
  2777. }
  2778. else
  2779. {
  2780. // Appears to be a conflicting resource, but server didn't specify conflict
  2781. [self continueHandleBinding:nil];
  2782. }
  2783. }
  2784. }
  2785. - (void)continueHandleBinding:(NSString *)alternativeResource
  2786. {
  2787. if ([alternativeResource length] > 0)
  2788. {
  2789. // Update myJID
  2790. [self setMyJID_setByClient:[myJID_setByClient jidWithNewResource:alternativeResource]];
  2791. NSXMLElement *resource = [NSXMLElement elementWithName:@"resource"];
  2792. [resource setStringValue:alternativeResource];
  2793. NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2794. [bind addChild:resource];
  2795. XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
  2796. [iq addChild:bind];
  2797. NSString *outgoingStr = [iq compactXMLString];
  2798. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2799. XMPPLogSend(@"SEND: %@", outgoingStr);
  2800. numberOfBytesSent += [outgoingData length];
  2801. [asyncSocket writeData:outgoingData
  2802. withTimeout:TIMEOUT_XMPP_WRITE
  2803. tag:TAG_XMPP_WRITE_STREAM];
  2804. // The state remains in STATE_XMPP_BINDING
  2805. }
  2806. else
  2807. {
  2808. // We'll simply let the server choose then
  2809. NSXMLElement *bind = [NSXMLElement elementWithName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
  2810. XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
  2811. [iq addChild:bind];
  2812. NSString *outgoingStr = [iq compactXMLString];
  2813. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  2814. XMPPLogSend(@"SEND: %@", outgoingStr);
  2815. numberOfBytesSent += [outgoingData length];
  2816. [asyncSocket writeData:outgoingData
  2817. withTimeout:TIMEOUT_XMPP_WRITE
  2818. tag:TAG_XMPP_WRITE_STREAM];
  2819. // The state remains in STATE_XMPP_BINDING
  2820. }
  2821. }
  2822. - (void)handleStartSessionResponse:(NSXMLElement *)response
  2823. {
  2824. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2825. XMPPLogTrace();
  2826. if ([[response attributeStringValueForName:@"type"] isEqualToString:@"result"])
  2827. {
  2828. // Revert back to connected state (from start session state)
  2829. state = STATE_XMPP_CONNECTED;
  2830. [multicastDelegate xmppStreamDidAuthenticate:self];
  2831. }
  2832. else
  2833. {
  2834. // Revert back to connected state (from start session state)
  2835. state = STATE_XMPP_CONNECTED;
  2836. [multicastDelegate xmppStream:self didNotAuthenticate:response];
  2837. }
  2838. }
  2839. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2840. #pragma mark XMPPSRVResolver Delegate
  2841. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2842. - (void)tryNextSrvResult
  2843. {
  2844. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2845. XMPPLogTrace();
  2846. NSError *connectError = nil;
  2847. BOOL success = NO;
  2848. while (srvResultsIndex < [srvResults count])
  2849. {
  2850. XMPPSRVRecord *srvRecord = [srvResults objectAtIndex:srvResultsIndex];
  2851. NSString *srvHost = srvRecord.target;
  2852. UInt16 srvPort = srvRecord.port;
  2853. success = [self connectToHost:srvHost onPort:srvPort withTimeout:XMPPStreamTimeoutNone error:&connectError];
  2854. if (success)
  2855. {
  2856. break;
  2857. }
  2858. else
  2859. {
  2860. srvResultsIndex++;
  2861. }
  2862. }
  2863. if (!success)
  2864. {
  2865. // SRV resolution of the JID domain failed.
  2866. // As per the RFC:
  2867. //
  2868. // "If the SRV lookup fails, the fallback is a normal IPv4/IPv6 address record resolution
  2869. // to determine the IP address, using the "xmpp-client" port 5222, registered with the IANA."
  2870. //
  2871. // In other words, just try connecting to the domain specified in the JID.
  2872. success = [self connectToHost:[myJID_setByClient domain] onPort:5222 withTimeout:XMPPStreamTimeoutNone error:&connectError];
  2873. }
  2874. if (!success)
  2875. {
  2876. [self endConnectTimeout];
  2877. state = STATE_XMPP_DISCONNECTED;
  2878. [multicastDelegate xmppStreamDidDisconnect:self withError:connectError];
  2879. }
  2880. }
  2881. - (void)srvResolver:(XMPPSRVResolver *)sender didResolveRecords:(NSArray *)records
  2882. {
  2883. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2884. if (sender != srvResolver) return;
  2885. XMPPLogTrace();
  2886. srvResults = [records copy];
  2887. srvResultsIndex = 0;
  2888. state = STATE_XMPP_CONNECTING;
  2889. [self tryNextSrvResult];
  2890. }
  2891. - (void)srvResolver:(XMPPSRVResolver *)sender didNotResolveDueToError:(NSError *)error
  2892. {
  2893. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  2894. if (sender != srvResolver) return;
  2895. XMPPLogTrace();
  2896. state = STATE_XMPP_CONNECTING;
  2897. [self tryNextSrvResult];
  2898. }
  2899. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2900. #pragma mark AsyncSocket Delegate
  2901. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2902. /**
  2903. * Called when a socket connects and is ready for reading and writing. "host" will be an IP address, not a DNS name.
  2904. **/
  2905. - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
  2906. {
  2907. // This method is invoked on the xmppQueue.
  2908. //
  2909. // The TCP connection is now established.
  2910. XMPPLogTrace();
  2911. [self endConnectTimeout];
  2912. #if TARGET_OS_IPHONE
  2913. {
  2914. if (self.enableBackgroundingOnSocket)
  2915. {
  2916. __block BOOL result;
  2917. [asyncSocket performBlock:^{
  2918. result = [asyncSocket enableBackgroundingOnSocket];
  2919. }];
  2920. if (result)
  2921. XMPPLogVerbose(@"%@: Enabled backgrounding on socket", THIS_FILE);
  2922. else
  2923. XMPPLogError(@"%@: Error enabling backgrounding on socket!", THIS_FILE);
  2924. }
  2925. }
  2926. #endif
  2927. [multicastDelegate xmppStream:self socketDidConnect:sock];
  2928. srvResolver = nil;
  2929. srvResults = nil;
  2930. // Are we using old-style SSL? (Not the upgrade to TLS technique specified in the XMPP RFC)
  2931. if ([self isSecure])
  2932. {
  2933. // The connection must be secured immediately (just like with HTTPS)
  2934. [self startTLS];
  2935. }
  2936. else
  2937. {
  2938. [self startNegotiation];
  2939. }
  2940. }
  2941. - (void)socketDidSecure:(GCDAsyncSocket *)sock
  2942. {
  2943. // This method is invoked on the xmppQueue.
  2944. XMPPLogTrace();
  2945. [multicastDelegate xmppStreamDidSecure:self];
  2946. }
  2947. /**
  2948. * Called when a socket has completed reading the requested data. Not called if there is an error.
  2949. **/
  2950. - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
  2951. {
  2952. // This method is invoked on the xmppQueue.
  2953. XMPPLogTrace();
  2954. lastSendReceiveTime = [NSDate timeIntervalSinceReferenceDate];
  2955. numberOfBytesReceived += [data length];
  2956. XMPPLogRecvPre(@"RECV: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
  2957. // Asynchronously parse the xml data
  2958. [parser parseData:data];
  2959. if ([self isSecure])
  2960. {
  2961. // Continue reading for XML elements
  2962. if (state == STATE_XMPP_OPENING)
  2963. {
  2964. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
  2965. }
  2966. else
  2967. {
  2968. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
  2969. }
  2970. }
  2971. else
  2972. {
  2973. // Don't queue up a read on the socket as we may need to upgrade to TLS.
  2974. // We'll read more data after we've parsed the current chunk of data.
  2975. }
  2976. }
  2977. /**
  2978. * Called after data with the given tag has been successfully sent.
  2979. **/
  2980. - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
  2981. {
  2982. // This method is invoked on the xmppQueue.
  2983. XMPPLogTrace();
  2984. lastSendReceiveTime = [NSDate timeIntervalSinceReferenceDate];
  2985. if (tag == TAG_XMPP_WRITE_RECEIPT)
  2986. {
  2987. if ([receipts count] == 0)
  2988. {
  2989. XMPPLogWarn(@"%@: Found TAG_XMPP_WRITE_RECEIPT with no pending receipts!", THIS_FILE);
  2990. return;
  2991. }
  2992. XMPPElementReceipt *receipt = [receipts objectAtIndex:0];
  2993. [receipt signalSuccess];
  2994. [receipts removeObjectAtIndex:0];
  2995. }
  2996. }
  2997. /**
  2998. * Called when a socket disconnects with or without error.
  2999. **/
  3000. - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
  3001. {
  3002. // This method is invoked on the xmppQueue.
  3003. XMPPLogTrace();
  3004. [self endConnectTimeout];
  3005. if (srvResults && (++srvResultsIndex < [srvResults count]))
  3006. {
  3007. [self tryNextSrvResult];
  3008. }
  3009. else
  3010. {
  3011. // Update state
  3012. state = STATE_XMPP_DISCONNECTED;
  3013. // Release the parser (to free underlying resources)
  3014. [parser setDelegate:nil delegateQueue:NULL];
  3015. parser = nil;
  3016. // Clear any saved authentication information
  3017. auth = nil;
  3018. authenticationDate = nil;
  3019. // Clear stored elements
  3020. myJID_setByServer = nil;
  3021. myPresence = nil;
  3022. rootElement = nil;
  3023. // Stop the keep alive timer
  3024. if (keepAliveTimer)
  3025. {
  3026. dispatch_source_cancel(keepAliveTimer);
  3027. keepAliveTimer = NULL;
  3028. }
  3029. // Clear srv results
  3030. srvResolver = nil;
  3031. srvResults = nil;
  3032. // Clear any pending receipts
  3033. for (XMPPElementReceipt *receipt in receipts)
  3034. {
  3035. [receipt signalFailure];
  3036. }
  3037. [receipts removeAllObjects];
  3038. // Clear flags
  3039. flags = 0;
  3040. // Notify delegate
  3041. if (parserError)
  3042. {
  3043. [multicastDelegate xmppStreamDidDisconnect:self withError:parserError];
  3044. parserError = nil;
  3045. }
  3046. else
  3047. {
  3048. [multicastDelegate xmppStreamDidDisconnect:self withError:err];
  3049. }
  3050. }
  3051. }
  3052. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3053. #pragma mark XMPPParser Delegate
  3054. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3055. /**
  3056. * Called when the xmpp parser has read in the entire root element.
  3057. **/
  3058. - (void)xmppParser:(XMPPParser *)sender didReadRoot:(NSXMLElement *)root
  3059. {
  3060. // This method is invoked on the xmppQueue.
  3061. if (sender != parser) return;
  3062. XMPPLogTrace();
  3063. XMPPLogRecvPost(@"RECV: %@", [root compactXMLString]);
  3064. // At this point we've sent our XML stream header, and we've received the response XML stream header.
  3065. // We save the root element of our stream for future reference.
  3066. rootElement = root;
  3067. if ([self isP2P])
  3068. {
  3069. // XEP-0174 specifies that <stream:features/> SHOULD be sent by the receiver.
  3070. // In other words, if we're the recipient we will now send our features.
  3071. // But if we're the initiator, we can't depend on receiving their features.
  3072. // Either way, we're connected at this point.
  3073. state = STATE_XMPP_CONNECTED;
  3074. if ([self isP2PRecipient])
  3075. {
  3076. // Extract the remoteJID:
  3077. //
  3078. // <stream:stream ... from='<remoteJID>' to='<myJID>'>
  3079. NSString *from = [[rootElement attributeForName:@"from"] stringValue];
  3080. remoteJID = [XMPPJID jidWithString:from];
  3081. // Send our stream features.
  3082. // To do so we need to ask the delegate to fill it out for us.
  3083. NSXMLElement *streamFeatures = [NSXMLElement elementWithName:@"stream:features"];
  3084. [multicastDelegate xmppStream:self willSendP2PFeatures:streamFeatures];
  3085. NSString *outgoingStr = [streamFeatures compactXMLString];
  3086. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  3087. XMPPLogSend(@"SEND: %@", outgoingStr);
  3088. numberOfBytesSent += [outgoingData length];
  3089. [asyncSocket writeData:outgoingData
  3090. withTimeout:TIMEOUT_XMPP_WRITE
  3091. tag:TAG_XMPP_WRITE_STREAM];
  3092. }
  3093. // Make sure the delegate didn't disconnect us in the xmppStream:willSendP2PFeatures: method.
  3094. if ([self isConnected])
  3095. {
  3096. [multicastDelegate xmppStreamDidConnect:self];
  3097. }
  3098. }
  3099. else
  3100. {
  3101. // Check for RFC compliance
  3102. if ([self serverXmppStreamVersionNumber] >= 1.0)
  3103. {
  3104. // Update state - we're now onto stream negotiations
  3105. state = STATE_XMPP_NEGOTIATING;
  3106. // Note: We're waiting for the <stream:features> now
  3107. }
  3108. else
  3109. {
  3110. // The server isn't RFC comliant, and won't be sending any stream features.
  3111. // We would still like to know what authentication features it supports though,
  3112. // so we'll use the jabber:iq:auth namespace, which was used prior to the RFC spec.
  3113. // Update state - we're onto psuedo negotiation
  3114. state = STATE_XMPP_NEGOTIATING;
  3115. NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:auth"];
  3116. NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
  3117. [iq addAttributeWithName:@"type" stringValue:@"get"];
  3118. [iq addChild:query];
  3119. NSString *outgoingStr = [iq compactXMLString];
  3120. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  3121. XMPPLogSend(@"SEND: %@", outgoingStr);
  3122. numberOfBytesSent += [outgoingData length];
  3123. [asyncSocket writeData:outgoingData
  3124. withTimeout:TIMEOUT_XMPP_WRITE
  3125. tag:TAG_XMPP_WRITE_STREAM];
  3126. // Now wait for the response IQ
  3127. }
  3128. }
  3129. }
  3130. - (void)xmppParser:(XMPPParser *)sender didReadElement:(NSXMLElement *)element
  3131. {
  3132. // This method is invoked on the xmppQueue.
  3133. if (sender != parser) return;
  3134. XMPPLogTrace();
  3135. XMPPLogRecvPost(@"RECV: %@", [element compactXMLString]);
  3136. NSString *elementName = [element name];
  3137. if ([elementName isEqualToString:@"stream:error"] || [elementName isEqualToString:@"error"])
  3138. {
  3139. [multicastDelegate xmppStream:self didReceiveError:element];
  3140. return;
  3141. }
  3142. if (state == STATE_XMPP_NEGOTIATING)
  3143. {
  3144. // We've just read in the stream features
  3145. // We consider this part of the root element, so we'll add it (replacing any previously sent features)
  3146. [rootElement setChildren:[NSArray arrayWithObject:element]];
  3147. // Call a method to handle any requirements set forth in the features
  3148. [self handleStreamFeatures];
  3149. }
  3150. else if (state == STATE_XMPP_STARTTLS_1)
  3151. {
  3152. // The response from our starttls message
  3153. [self handleStartTLSResponse:element];
  3154. }
  3155. else if (state == STATE_XMPP_REGISTERING)
  3156. {
  3157. // The iq response from our registration request
  3158. [self handleRegistration:element];
  3159. }
  3160. else if (state == STATE_XMPP_AUTH)
  3161. {
  3162. // Some response to the authentication process
  3163. [self handleAuth:element];
  3164. }
  3165. else if (state == STATE_XMPP_BINDING)
  3166. {
  3167. // The response from our binding request
  3168. [self handleBinding:element];
  3169. }
  3170. else if (state == STATE_XMPP_START_SESSION)
  3171. {
  3172. // The response from our start session request
  3173. [self handleStartSessionResponse:element];
  3174. }
  3175. else
  3176. {
  3177. if ([elementName isEqualToString:@"iq"])
  3178. {
  3179. [self receiveIQ:[XMPPIQ iqFromElement:element]];
  3180. }
  3181. else if ([elementName isEqualToString:@"message"])
  3182. {
  3183. [self receiveMessage:[XMPPMessage messageFromElement:element]];
  3184. }
  3185. else if ([elementName isEqualToString:@"presence"])
  3186. {
  3187. [self receivePresence:[XMPPPresence presenceFromElement:element]];
  3188. }
  3189. else if ([self isP2P] &&
  3190. ([elementName isEqualToString:@"stream:features"] || [elementName isEqualToString:@"features"]))
  3191. {
  3192. [multicastDelegate xmppStream:self didReceiveP2PFeatures:element];
  3193. }
  3194. else
  3195. {
  3196. [multicastDelegate xmppStream:self didReceiveError:element];
  3197. }
  3198. }
  3199. }
  3200. - (void)xmppParserDidParseData:(XMPPParser *)sender
  3201. {
  3202. // This method is invoked on the xmppQueue.
  3203. if (sender != parser) return;
  3204. XMPPLogTrace();
  3205. if (![self isSecure])
  3206. {
  3207. // Continue reading for XML elements
  3208. if (state == STATE_XMPP_OPENING)
  3209. {
  3210. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
  3211. }
  3212. else if (state != STATE_XMPP_STARTTLS_2) // Don't queue read operation prior to [asyncSocket startTLS:]
  3213. {
  3214. [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
  3215. }
  3216. }
  3217. }
  3218. - (void)xmppParserDidEnd:(XMPPParser *)sender
  3219. {
  3220. // This method is invoked on the xmppQueue.
  3221. if (sender != parser) return;
  3222. XMPPLogTrace();
  3223. [asyncSocket disconnect];
  3224. }
  3225. - (void)xmppParser:(XMPPParser *)sender didFail:(NSError *)error
  3226. {
  3227. // This method is invoked on the xmppQueue.
  3228. if (sender != parser) return;
  3229. XMPPLogTrace();
  3230. parserError = error;
  3231. [asyncSocket disconnect];
  3232. }
  3233. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3234. #pragma mark Keep Alive
  3235. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3236. - (void)setupKeepAliveTimer
  3237. {
  3238. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  3239. XMPPLogTrace();
  3240. if (keepAliveTimer)
  3241. {
  3242. dispatch_source_cancel(keepAliveTimer);
  3243. keepAliveTimer = NULL;
  3244. }
  3245. if (state == STATE_XMPP_CONNECTED)
  3246. {
  3247. if (keepAliveInterval > 0)
  3248. {
  3249. keepAliveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, xmppQueue);
  3250. dispatch_source_set_event_handler(keepAliveTimer, ^{ @autoreleasepool {
  3251. [self keepAlive];
  3252. }});
  3253. #if !OS_OBJECT_USE_OBJC
  3254. dispatch_source_t theKeepAliveTimer = keepAliveTimer;
  3255. dispatch_source_set_cancel_handler(keepAliveTimer, ^{
  3256. XMPPLogVerbose(@"dispatch_release(keepAliveTimer)");
  3257. dispatch_release(theKeepAliveTimer);
  3258. });
  3259. #endif
  3260. // Everytime we send or receive data, we update our lastSendReceiveTime.
  3261. // We set our timer to fire several times per keepAliveInterval.
  3262. // This allows us to maintain a single timer,
  3263. // and an acceptable timer resolution (assuming larger keepAliveIntervals).
  3264. uint64_t interval = ((keepAliveInterval / 4.0) * NSEC_PER_SEC);
  3265. dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, interval);
  3266. dispatch_source_set_timer(keepAliveTimer, tt, interval, 1.0);
  3267. dispatch_resume(keepAliveTimer);
  3268. }
  3269. }
  3270. }
  3271. - (void)keepAlive
  3272. {
  3273. NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");
  3274. if (state == STATE_XMPP_CONNECTED)
  3275. {
  3276. NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
  3277. NSTimeInterval elapsed = (now - lastSendReceiveTime);
  3278. if (elapsed < 0 || elapsed >= keepAliveInterval)
  3279. {
  3280. numberOfBytesSent += [keepAliveData length];
  3281. [asyncSocket writeData:keepAliveData
  3282. withTimeout:TIMEOUT_XMPP_WRITE
  3283. tag:TAG_XMPP_WRITE_STREAM];
  3284. // Force update the lastSendReceiveTime here just to be safe.
  3285. //
  3286. // In case the TCP socket comes to a crawl with a giant element in the queue,
  3287. // which would prevent the socket:didWriteDataWithTag: method from being called for some time.
  3288. lastSendReceiveTime = [NSDate timeIntervalSinceReferenceDate];
  3289. }
  3290. }
  3291. }
  3292. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3293. #pragma mark Module Plug-In System
  3294. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3295. - (void)registerModule:(XMPPModule *)module
  3296. {
  3297. if (module == nil) return;
  3298. // Asynchronous operation
  3299. dispatch_block_t block = ^{ @autoreleasepool {
  3300. // Register module
  3301. [registeredModules addObject:module];
  3302. // Add auto delegates (if there are any)
  3303. NSString *className = NSStringFromClass([module class]);
  3304. GCDMulticastDelegate *autoDelegates = [autoDelegateDict objectForKey:className];
  3305. GCDMulticastDelegateEnumerator *autoDelegatesEnumerator = [autoDelegates delegateEnumerator];
  3306. id delegate;
  3307. dispatch_queue_t delegateQueue;
  3308. while ([autoDelegatesEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue])
  3309. {
  3310. [module addDelegate:delegate delegateQueue:delegateQueue];
  3311. }
  3312. // Notify our own delegate(s)
  3313. [multicastDelegate xmppStream:self didRegisterModule:module];
  3314. }};
  3315. // Asynchronous operation
  3316. if (dispatch_get_specific(xmppQueueTag))
  3317. block();
  3318. else
  3319. dispatch_async(xmppQueue, block);
  3320. }
  3321. - (void)unregisterModule:(XMPPModule *)module
  3322. {
  3323. if (module == nil) return;
  3324. // Synchronous operation
  3325. dispatch_block_t block = ^{ @autoreleasepool {
  3326. // Notify our own delegate(s)
  3327. [multicastDelegate xmppStream:self willUnregisterModule:module];
  3328. // Remove auto delegates (if there are any)
  3329. NSString *className = NSStringFromClass([module class]);
  3330. GCDMulticastDelegate *autoDelegates = [autoDelegateDict objectForKey:className];
  3331. GCDMulticastDelegateEnumerator *autoDelegatesEnumerator = [autoDelegates delegateEnumerator];
  3332. id delegate;
  3333. dispatch_queue_t delegateQueue;
  3334. while ([autoDelegatesEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue])
  3335. {
  3336. // The module itself has dispatch_sync'd in order to invoke its deactivate method,
  3337. // which has in turn invoked this method. If we call back into the module,
  3338. // and have it dispatch_sync again, we're going to get a deadlock.
  3339. // So we must remove the delegate(s) asynchronously.
  3340. [module removeDelegate:delegate delegateQueue:delegateQueue synchronously:NO];
  3341. }
  3342. // Unregister modules
  3343. [registeredModules removeObject:module];
  3344. }};
  3345. // Synchronous operation
  3346. if (dispatch_get_specific(xmppQueueTag))
  3347. block();
  3348. else
  3349. dispatch_sync(xmppQueue, block);
  3350. }
  3351. - (void)autoAddDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue toModulesOfClass:(Class)aClass
  3352. {
  3353. if (delegate == nil) return;
  3354. if (aClass == nil) return;
  3355. // Asynchronous operation
  3356. dispatch_block_t block = ^{ @autoreleasepool {
  3357. NSString *className = NSStringFromClass(aClass);
  3358. // Add the delegate to all currently registered modules of the given class.
  3359. for (XMPPModule *module in registeredModules)
  3360. {
  3361. if ([module isKindOfClass:aClass])
  3362. {
  3363. [module addDelegate:delegate delegateQueue:delegateQueue];
  3364. }
  3365. }
  3366. // Add the delegate to list of auto delegates for the given class.
  3367. // It will be added as a delegate to future registered modules of the given class.
  3368. id delegates = [autoDelegateDict objectForKey:className];
  3369. if (delegates == nil)
  3370. {
  3371. delegates = [[GCDMulticastDelegate alloc] init];
  3372. [autoDelegateDict setObject:delegates forKey:className];
  3373. }
  3374. [delegates addDelegate:delegate delegateQueue:delegateQueue];
  3375. }};
  3376. // Asynchronous operation
  3377. if (dispatch_get_specific(xmppQueueTag))
  3378. block();
  3379. else
  3380. dispatch_async(xmppQueue, block);
  3381. }
  3382. - (void)removeAutoDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue fromModulesOfClass:(Class)aClass
  3383. {
  3384. if (delegate == nil) return;
  3385. // delegateQueue may be NULL
  3386. // aClass may be NULL
  3387. // Synchronous operation
  3388. dispatch_block_t block = ^{ @autoreleasepool {
  3389. if (aClass == NULL)
  3390. {
  3391. // Remove the delegate from all currently registered modules of ANY class.
  3392. for (XMPPModule *module in registeredModules)
  3393. {
  3394. [module removeDelegate:delegate delegateQueue:delegateQueue];
  3395. }
  3396. // Remove the delegate from list of auto delegates for all classes,
  3397. // so that it will not be auto added as a delegate to future registered modules.
  3398. for (GCDMulticastDelegate *delegates in [autoDelegateDict objectEnumerator])
  3399. {
  3400. [delegates removeDelegate:delegate delegateQueue:delegateQueue];
  3401. }
  3402. }
  3403. else
  3404. {
  3405. NSString *className = NSStringFromClass(aClass);
  3406. // Remove the delegate from all currently registered modules of the given class.
  3407. for (XMPPModule *module in registeredModules)
  3408. {
  3409. if ([module isKindOfClass:aClass])
  3410. {
  3411. [module removeDelegate:delegate delegateQueue:delegateQueue];
  3412. }
  3413. }
  3414. // Remove the delegate from list of auto delegates for the given class,
  3415. // so that it will not be added as a delegate to future registered modules of the given class.
  3416. GCDMulticastDelegate *delegates = [autoDelegateDict objectForKey:className];
  3417. [delegates removeDelegate:delegate delegateQueue:delegateQueue];
  3418. if ([delegates count] == 0)
  3419. {
  3420. [autoDelegateDict removeObjectForKey:className];
  3421. }
  3422. }
  3423. }};
  3424. // Synchronous operation
  3425. if (dispatch_get_specific(xmppQueueTag))
  3426. block();
  3427. else
  3428. dispatch_sync(xmppQueue, block);
  3429. }
  3430. - (void)enumerateModulesWithBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))enumBlock
  3431. {
  3432. if (enumBlock == NULL) return;
  3433. dispatch_block_t block = ^{ @autoreleasepool {
  3434. NSUInteger i = 0;
  3435. BOOL stop = NO;
  3436. for (XMPPModule *module in registeredModules)
  3437. {
  3438. enumBlock(module, i, &stop);
  3439. if (stop)
  3440. break;
  3441. else
  3442. i++;
  3443. }
  3444. }};
  3445. // Synchronous operation
  3446. if (dispatch_get_specific(xmppQueueTag))
  3447. block();
  3448. else
  3449. dispatch_sync(xmppQueue, block);
  3450. }
  3451. - (void)enumerateModulesOfClass:(Class)aClass withBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))block
  3452. {
  3453. [self enumerateModulesWithBlock:^(XMPPModule *module, NSUInteger idx, BOOL *stop)
  3454. {
  3455. if([module isKindOfClass:aClass])
  3456. {
  3457. block(module,idx,stop);
  3458. }
  3459. }];
  3460. }
  3461. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3462. #pragma mark Utilities
  3463. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3464. + (NSString *)generateUUID
  3465. {
  3466. NSString *result = nil;
  3467. CFUUIDRef uuid = CFUUIDCreate(NULL);
  3468. if (uuid)
  3469. {
  3470. result = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
  3471. CFRelease(uuid);
  3472. }
  3473. return result;
  3474. }
  3475. - (NSString *)generateUUID
  3476. {
  3477. return [[self class] generateUUID];
  3478. }
  3479. - (NSThread *)xmppUtilityThread
  3480. {
  3481. // This is a read-only variable, set in the init method and never altered.
  3482. // Thus we supply direct access to it in this method.
  3483. return xmppUtilityThread;
  3484. }
  3485. - (NSRunLoop *)xmppUtilityRunLoop
  3486. {
  3487. __block NSRunLoop *result = nil;
  3488. dispatch_block_t block = ^{
  3489. result = xmppUtilityRunLoop;
  3490. };
  3491. if (dispatch_get_specific(xmppQueueTag))
  3492. block();
  3493. else
  3494. dispatch_sync(xmppQueue, block);
  3495. return result;
  3496. }
  3497. - (void)setXmppUtilityRunLoop:(NSRunLoop *)runLoop
  3498. {
  3499. dispatch_async(xmppQueue, ^{
  3500. if (xmppUtilityRunLoop == nil)
  3501. {
  3502. xmppUtilityRunLoop = runLoop;
  3503. }
  3504. });
  3505. }
  3506. + (void)xmppThreadMain
  3507. {
  3508. // This is the xmppUtilityThread.
  3509. // It is designed to be used only if absolutely necessary.
  3510. // If there is a GCD alternative, it should be used instead.
  3511. @autoreleasepool {
  3512. [[NSThread currentThread] setName:@"XMPPUtilityThread"];
  3513. // Set XMPPStream's xmppUtilityRunLoop variable.
  3514. //
  3515. // And when done, remove the xmppStream reference from the dictionary so it's no longer retained.
  3516. XMPPStream *creator = [[[NSThread currentThread] threadDictionary] objectForKey:@"XMPPStream"];
  3517. [creator setXmppUtilityRunLoop:[NSRunLoop currentRunLoop]];
  3518. [[[NSThread currentThread] threadDictionary] removeObjectForKey:@"XMPPStream"];
  3519. // We can't iteratively run the run loop unless it has at least one source or timer.
  3520. // So we'll create a timer that will probably never fire.
  3521. [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
  3522. target:self
  3523. selector:@selector(xmppThreadIgnore:)
  3524. userInfo:nil
  3525. repeats:YES];
  3526. BOOL isCancelled = NO;
  3527. BOOL hasRunLoopSources = YES;
  3528. while (!isCancelled && hasRunLoopSources)
  3529. {
  3530. @autoreleasepool {
  3531. hasRunLoopSources = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
  3532. beforeDate:[NSDate distantFuture]];
  3533. isCancelled = [[NSThread currentThread] isCancelled];
  3534. }
  3535. }
  3536. }
  3537. }
  3538. + (void)xmppThreadStop
  3539. {
  3540. [[NSThread currentThread] cancel];
  3541. }
  3542. + (void)xmppThreadIgnore:(NSTimer *)aTimer
  3543. {
  3544. // Ignore
  3545. }
  3546. @end
  3547. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3548. #pragma mark -
  3549. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3550. @implementation XMPPElementReceipt
  3551. static const uint32_t receipt_unknown = 0 << 0;
  3552. static const uint32_t receipt_failure = 1 << 0;
  3553. static const uint32_t receipt_success = 1 << 1;
  3554. - (id)init
  3555. {
  3556. if ((self = [super init]))
  3557. {
  3558. atomicFlags = receipt_unknown;
  3559. semaphore = dispatch_semaphore_create(0);
  3560. }
  3561. return self;
  3562. }
  3563. - (void)signalSuccess
  3564. {
  3565. uint32_t mask = receipt_success;
  3566. OSAtomicOr32Barrier(mask, &atomicFlags);
  3567. dispatch_semaphore_signal(semaphore);
  3568. }
  3569. - (void)signalFailure
  3570. {
  3571. uint32_t mask = receipt_failure;
  3572. OSAtomicOr32Barrier(mask, &atomicFlags);
  3573. dispatch_semaphore_signal(semaphore);
  3574. }
  3575. - (BOOL)wait:(NSTimeInterval)timeout_seconds
  3576. {
  3577. uint32_t mask = 0;
  3578. uint32_t flags = OSAtomicOr32Barrier(mask, &atomicFlags);
  3579. if (flags != receipt_unknown) return (flags == receipt_success);
  3580. dispatch_time_t timeout_nanos;
  3581. if (isless(timeout_seconds, 0.0))
  3582. timeout_nanos = DISPATCH_TIME_FOREVER;
  3583. else
  3584. timeout_nanos = dispatch_time(DISPATCH_TIME_NOW, (timeout_seconds * NSEC_PER_SEC));
  3585. // dispatch_semaphore_wait
  3586. //
  3587. // Decrement the counting semaphore. If the resulting value is less than zero,
  3588. // this function waits in FIFO order for a signal to occur before returning.
  3589. //
  3590. // Returns zero on success, or non-zero if the timeout occurred.
  3591. //
  3592. // Note: If the timeout occurs, the semaphore value is incremented (without signaling).
  3593. long result = dispatch_semaphore_wait(semaphore, timeout_nanos);
  3594. if (result == 0)
  3595. {
  3596. flags = OSAtomicOr32Barrier(mask, &atomicFlags);
  3597. return (flags == receipt_success);
  3598. }
  3599. else
  3600. {
  3601. // Timed out waiting...
  3602. return NO;
  3603. }
  3604. }
  3605. - (void)dealloc
  3606. {
  3607. #if !OS_OBJECT_USE_OBJC
  3608. dispatch_release(semaphore);
  3609. #endif
  3610. }
  3611. @end