PageRenderTime 78ms CodeModel.GetById 17ms RepoModel.GetById 2ms app.codeStats 0ms

/XMPP_Demo/Core/XMPPStream.m

https://gitlab.com/praveenvelanati/ios-demo
Objective C | 1956 lines | 1307 code | 445 blank | 204 comment | 225 complexity | 8520742c9f4cfb1871c53159bbc259db 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. /**
  17. * Does ARC support support GCD objects?
  18. * It does if the minimum deployment target is iOS 6+ or Mac OS X 10.8+
  19. **/
  20. #if TARGET_OS_IPHONE
  21. // Compiling for iOS
  22. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
  23. #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  24. #else // iOS 5.X or earlier
  25. #define NEEDS_DISPATCH_RETAIN_RELEASE 1
  26. #endif
  27. #else
  28. // Compiling for Mac OS X
  29. #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
  30. #define NEEDS_DISPATCH_RETAIN_RELEASE 0
  31. #else
  32. #define NEEDS_DISPATCH_RETAIN_RELEASE 1 // Mac OS X 10.7 or earlier
  33. #endif
  34. #endif
  35. // Log levels: off, error, warn, info, verbose
  36. #if DEBUG
  37. static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO | XMPP_LOG_FLAG_SEND_RECV; // | XMPP_LOG_FLAG_TRACE;
  38. #else
  39. static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
  40. #endif
  41. /**
  42. * Seeing a return statements within an inner block
  43. * can sometimes be mistaken for a return point of the enclosing method.
  44. * This makes inline blocks a bit easier to read.
  45. **/
  46. #define return_from_block return
  47. // Define the timeouts (in seconds) for retreiving various parts of the XML stream
  48. #define TIMEOUT_XMPP_WRITE -1
  49. #define TIMEOUT_XMPP_READ_START 10
  50. #define TIMEOUT_XMPP_READ_STREAM -1
  51. // Define the tags we'll use to differentiate what it is we're currently reading or writing
  52. #define TAG_XMPP_READ_START 100
  53. #define TAG_XMPP_READ_STREAM 101
  54. #define TAG_XMPP_WRITE_START 200
  55. #define TAG_XMPP_WRITE_STREAM 201
  56. #define TAG_XMPP_WRITE_RECEIPT 202
  57. NSString *const XMPPStreamErrorDomain = @"XMPPStreamErrorDomain";
  58. NSString *const XMPPStreamDidChangeMyJIDNotification = @"XMPPStreamDidChangeMyJID";
  59. enum XMPPStreamFlags
  60. {
  61. kP2PInitiator = 1 << 0, // If set, we are the P2P initializer
  62. kIsSecure = 1 << 1, // If set, connection has been secured via SSL/TLS
  63. kIsAuthenticated = 1 << 2, // If set, authentication has succeeded
  64. kDidStartNegotiation = 1 << 3, // If set, negotiation has started at least once
  65. };
  66. enum XMPPStreamConfig
  67. {
  68. kP2PMode = 1 << 0, // If set, the XMPPStream was initialized in P2P mode
  69. kResetByteCountPerConnection = 1 << 1, // If set, byte count should be reset per connection
  70. #if TARGET_OS_IPHONE
  71. kEnableBackgroundingOnSocket = 1 << 2, // If set, the VoIP flag should be set on the socket
  72. #endif
  73. };
  74. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  75. #pragma mark -
  76. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  77. @interface XMPPStream ()
  78. {
  79. dispatch_queue_t xmppQueue;
  80. dispatch_queue_t willSendIqQueue;
  81. dispatch_queue_t willSendMessageQueue;
  82. dispatch_queue_t willSendPresenceQueue;
  83. dispatch_queue_t willReceiveIqQueue;
  84. dispatch_queue_t willReceiveMessageQueue;
  85. dispatch_queue_t willReceivePresenceQueue;
  86. dispatch_queue_t didReceiveIqQueue;
  87. GCDMulticastDelegate <XMPPStreamDelegate> *multicastDelegate;
  88. int state;
  89. GCDAsyncSocket *asyncSocket;
  90. UInt64 numberOfBytesSent;
  91. UInt64 numberOfBytesReceived;
  92. XMPPParser *parser;
  93. NSError *parserError;
  94. Byte flags;
  95. Byte config;
  96. NSString *hostName;
  97. UInt16 hostPort;
  98. id <XMPPSASLAuthentication> auth;
  99. XMPPJID *myJID_setByClient;
  100. XMPPJID *myJID_setByServer;
  101. XMPPJID *remoteJID;
  102. XMPPPresence *myPresence;
  103. NSXMLElement *rootElement;
  104. NSTimeInterval keepAliveInterval;
  105. dispatch_source_t keepAliveTimer;
  106. NSTimeInterval lastSendReceiveTime;
  107. NSData *keepAliveData;
  108. NSMutableArray *registeredModules;
  109. NSMutableDictionary *autoDelegateDict;
  110. XMPPSRVResolver *srvResolver;
  111. NSArray *srvResults;
  112. NSUInteger srvResultsIndex;
  113. NSMutableArray *receipts;
  114. NSThread *xmppUtilityThread;
  115. NSRunLoop *xmppUtilityRunLoop;
  116. id userTag;
  117. }
  118. - (void)setIsSecure:(BOOL)flag;
  119. - (void)setIsAuthenticated:(BOOL)flag;
  120. - (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag;
  121. - (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag;
  122. - (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag;
  123. - (void)startNegotiation;
  124. - (void)sendOpeningNegotiation;
  125. - (void)continueStartTLS:(NSMutableDictionary *)settings;
  126. - (void)continueHandleBinding:(NSString *)alternativeResource;
  127. - (void)setupKeepAliveTimer;
  128. - (void)keepAlive;
  129. - (void)continueReceiveMessage:(XMPPMessage *)message;
  130. - (void)continueReceiveIQ:(XMPPIQ *)iq;
  131. - (void)continueReceivePresence:(XMPPPresence *)presence;
  132. @end
  133. @interface XMPPElementReceipt (PrivateAPI)
  134. - (void)signalSuccess;
  135. - (void)signalFailure;
  136. @end
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. #pragma mark -
  139. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  140. @implementation XMPPStream
  141. @synthesize tag = userTag;
  142. /**
  143. * Shared initialization between the various init methods.
  144. **/
  145. - (void)commonInit
  146. {
  147. xmppQueue = dispatch_queue_create("xmpp", NULL);
  148. willSendIqQueue = dispatch_queue_create("xmpp.willSendIq", NULL);
  149. willSendMessageQueue = dispatch_queue_create("xmpp.willSendMessage", NULL);
  150. willSendPresenceQueue = dispatch_queue_create("xmpp.willSendPresence", NULL);
  151. willReceiveIqQueue = dispatch_queue_create("xmpp.willReceiveIq", NULL);
  152. willReceiveMessageQueue = dispatch_queue_create("xmpp.willReceiveMessage", NULL);
  153. willReceivePresenceQueue = dispatch_queue_create("xmpp.willReceivePresence", NULL);
  154. didReceiveIqQueue = dispatch_queue_create("xmpp.didReceiveIq", NULL);
  155. multicastDelegate = (GCDMulticastDelegate <XMPPStreamDelegate> *)[[GCDMulticastDelegate alloc] init];
  156. state = STATE_XMPP_DISCONNECTED;
  157. flags = 0;
  158. config = 0;
  159. numberOfBytesSent = 0;
  160. numberOfBytesReceived = 0;
  161. hostPort = 5222;
  162. keepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
  163. keepAliveData = [@" " dataUsingEncoding:NSUTF8StringEncoding];
  164. registeredModules = [[NSMutableArray alloc] init];
  165. autoDelegateDict = [[NSMutableDictionary alloc] init];
  166. receipts = [[NSMutableArray alloc] init];
  167. // Setup and start the utility thread.
  168. // We need to be careful to ensure the thread doesn't retain a reference to us longer than necessary.
  169. xmppUtilityThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(xmppThreadMain) object:nil];
  170. [[xmppUtilityThread threadDictionary] setObject:self forKey:@"XMPPStream"];
  171. [xmppUtilityThread start];
  172. }
  173. /**
  174. * Standard XMPP initialization.
  175. * The stream is a standard client to server connection.
  176. **/
  177. - (id)init
  178. {
  179. if ((self = [super init]))
  180. {
  181. // Common initialization
  182. [self commonInit];
  183. // Initialize socket
  184. asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:xmppQueue];
  185. }
  186. return self;
  187. }
  188. /**
  189. * Peer to Peer XMPP initialization.
  190. * The stream is a direct client to client connection as outlined in XEP-0174.
  191. **/
  192. - (id)initP2PFrom:(XMPPJID *)jid
  193. {
  194. if ((self = [super init]))
  195. {
  196. // Common initialization
  197. [self commonInit];
  198. // Store JID
  199. myJID_setByClient = jid;
  200. // We do not initialize the socket, since the connectP2PWithSocket: method might be used.
  201. // Initialize configuration
  202. config = kP2PMode;
  203. }
  204. return self;
  205. }
  206. /**
  207. * Standard deallocation method.
  208. * Every object variable declared in the header file should be released here.
  209. **/
  210. - (void)dealloc
  211. {
  212. #if NEEDS_DISPATCH_RETAIN_RELEASE
  213. dispatch_release(xmppQueue);
  214. dispatch_release(willSendIqQueue);
  215. dispatch_release(willSendMessageQueue);
  216. dispatch_release(willSendPresenceQueue);
  217. dispatch_release(willReceiveIqQueue);
  218. dispatch_release(willReceiveMessageQueue);
  219. dispatch_release(willReceivePresenceQueue);
  220. dispatch_release(didReceiveIqQueue);
  221. #endif
  222. [asyncSocket setDelegate:nil delegateQueue:NULL];
  223. [asyncSocket disconnect];
  224. [parser setDelegate:nil delegateQueue:NULL];
  225. if (keepAliveTimer)
  226. {
  227. dispatch_source_cancel(keepAliveTimer);
  228. }
  229. for (XMPPElementReceipt *receipt in receipts)
  230. {
  231. [receipt signalFailure];
  232. }
  233. [[self class] performSelector:@selector(xmppThreadStop)
  234. onThread:xmppUtilityThread
  235. withObject:nil
  236. waitUntilDone:NO];
  237. }
  238. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  239. #pragma mark Properties
  240. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  241. @synthesize xmppQueue;
  242. - (XMPPStreamState)state
  243. {
  244. __block XMPPStreamState result = STATE_XMPP_DISCONNECTED;
  245. dispatch_block_t block = ^{
  246. result = (XMPPStreamState)state;
  247. };
  248. if (dispatch_get_current_queue() == xmppQueue)
  249. block();
  250. else
  251. dispatch_sync(xmppQueue, block);
  252. return result;
  253. }
  254. - (NSString *)hostName
  255. {
  256. if (dispatch_get_current_queue() == xmppQueue)
  257. {
  258. return hostName;
  259. }
  260. else
  261. {
  262. __block NSString *result;
  263. dispatch_sync(xmppQueue, ^{
  264. result = hostName;
  265. });
  266. return result;
  267. }
  268. }
  269. - (void)setHostName:(NSString *)newHostName
  270. {
  271. if (dispatch_get_current_queue() == xmppQueue)
  272. {
  273. if (hostName != newHostName)
  274. {
  275. hostName = [newHostName copy];
  276. }
  277. }
  278. else
  279. {
  280. NSString *newHostNameCopy = [newHostName copy];
  281. dispatch_async(xmppQueue, ^{
  282. hostName = newHostNameCopy;
  283. });
  284. }
  285. }
  286. - (UInt16)hostPort
  287. {
  288. if (dispatch_get_current_queue() == xmppQueue)
  289. {
  290. return hostPort;
  291. }
  292. else
  293. {
  294. __block UInt16 result;
  295. dispatch_sync(xmppQueue, ^{
  296. result = hostPort;
  297. });
  298. return result;
  299. }
  300. }
  301. - (void)setHostPort:(UInt16)newHostPort
  302. {
  303. dispatch_block_t block = ^{
  304. hostPort = newHostPort;
  305. };
  306. if (dispatch_get_current_queue() == xmppQueue)
  307. block();
  308. else
  309. dispatch_async(xmppQueue, block);
  310. }
  311. - (XMPPJID *)myJID
  312. {
  313. __block XMPPJID *result = nil;
  314. dispatch_block_t block = ^{
  315. if (myJID_setByServer)
  316. result = myJID_setByServer;
  317. else
  318. result = myJID_setByClient;
  319. };
  320. if (dispatch_get_current_queue() == xmppQueue)
  321. block();
  322. else
  323. dispatch_sync(xmppQueue, block);
  324. return result;
  325. }
  326. - (void)setMyJID_setByClient:(XMPPJID *)newMyJID
  327. {
  328. // XMPPJID is an immutable class (copy == retain)
  329. dispatch_block_t block = ^{
  330. if (![myJID_setByClient isEqualToJID:newMyJID])
  331. {
  332. myJID_setByClient = newMyJID;
  333. if (myJID_setByServer == nil)
  334. {
  335. [[NSNotificationCenter defaultCenter] postNotificationName:XMPPStreamDidChangeMyJIDNotification
  336. object:self];
  337. }
  338. }
  339. };
  340. if (dispatch_get_current_queue() == xmppQueue)
  341. block();
  342. else
  343. dispatch_async(xmppQueue, block);
  344. }
  345. - (void)setMyJID_setByServer:(XMPPJID *)newMyJID
  346. {
  347. // XMPPJID is an immutable class (copy == retain)
  348. dispatch_block_t block = ^{
  349. if (![myJID_setByServer isEqualToJID:newMyJID])
  350. {
  351. XMPPJID *oldMyJID;
  352. if (myJID_setByServer)
  353. oldMyJID = myJID_setByServer;
  354. else
  355. oldMyJID = myJID_setByClient;
  356. myJID_setByServer = newMyJID;
  357. if (![oldMyJID isEqualToJID:newMyJID])
  358. {
  359. [[NSNotificationCenter defaultCenter] postNotificationName:XMPPStreamDidChangeMyJIDNotification
  360. object:self];
  361. }
  362. }
  363. };
  364. if (dispatch_get_current_queue() == xmppQueue)
  365. block();
  366. else
  367. dispatch_async(xmppQueue, block);
  368. }
  369. - (void)setMyJID:(XMPPJID *)newMyJID
  370. {
  371. [self setMyJID_setByClient:newMyJID];
  372. }
  373. - (XMPPJID *)remoteJID
  374. {
  375. if (dispatch_get_current_queue() == xmppQueue)
  376. {
  377. return remoteJID;
  378. }
  379. else
  380. {
  381. __block XMPPJID *result;
  382. dispatch_sync(xmppQueue, ^{
  383. result = remoteJID;
  384. });
  385. return result;
  386. }
  387. }
  388. - (XMPPPresence *)myPresence
  389. {
  390. if (dispatch_get_current_queue() == xmppQueue)
  391. {
  392. return myPresence;
  393. }
  394. else
  395. {
  396. __block XMPPPresence *result;
  397. dispatch_sync(xmppQueue, ^{
  398. result = myPresence;
  399. });
  400. return result;
  401. }
  402. }
  403. - (NSTimeInterval)keepAliveInterval
  404. {
  405. __block NSTimeInterval result = 0.0;
  406. dispatch_block_t block = ^{
  407. result = keepAliveInterval;
  408. };
  409. if (dispatch_get_current_queue() == xmppQueue)
  410. block();
  411. else
  412. dispatch_sync(xmppQueue, block);
  413. return result;
  414. }
  415. - (void)setKeepAliveInterval:(NSTimeInterval)interval
  416. {
  417. dispatch_block_t block = ^{
  418. if (keepAliveInterval != interval)
  419. {
  420. if (interval <= 0.0)
  421. keepAliveInterval = interval;
  422. else
  423. keepAliveInterval = MAX(interval, MIN_KEEPALIVE_INTERVAL);
  424. [self setupKeepAliveTimer];
  425. }
  426. };
  427. if (dispatch_get_current_queue() == xmppQueue)
  428. block();
  429. else
  430. dispatch_async(xmppQueue, block);
  431. }
  432. - (char)keepAliveWhitespaceCharacter
  433. {
  434. __block char keepAliveChar = ' ';
  435. dispatch_block_t block = ^{
  436. NSString *keepAliveString = [[NSString alloc] initWithData:keepAliveData encoding:NSUTF8StringEncoding];
  437. if ([keepAliveString length] > 0)
  438. {
  439. keepAliveChar = (char)[keepAliveString characterAtIndex:0];
  440. }
  441. };
  442. if (dispatch_get_current_queue() == xmppQueue)
  443. block();
  444. else
  445. dispatch_sync(xmppQueue, block);
  446. return keepAliveChar;
  447. }
  448. - (void)setKeepAliveWhitespaceCharacter:(char)keepAliveChar
  449. {
  450. dispatch_block_t block = ^{
  451. if (keepAliveChar == ' ' || keepAliveChar == '\n' || keepAliveChar == '\t')
  452. {
  453. keepAliveData = [[NSString stringWithFormat:@"%c", keepAliveChar] dataUsingEncoding:NSUTF8StringEncoding];
  454. }
  455. };
  456. if (dispatch_get_current_queue() == xmppQueue)
  457. block();
  458. else
  459. dispatch_async(xmppQueue, block);
  460. }
  461. - (UInt64)numberOfBytesSent
  462. {
  463. if (dispatch_get_current_queue() == xmppQueue)
  464. {
  465. return numberOfBytesSent;
  466. }
  467. else
  468. {
  469. __block UInt64 result;
  470. dispatch_sync(xmppQueue, ^{
  471. result = numberOfBytesSent;
  472. });
  473. return result;
  474. }
  475. }
  476. - (UInt64)numberOfBytesReceived
  477. {
  478. if (dispatch_get_current_queue() == xmppQueue)
  479. {
  480. return numberOfBytesReceived;
  481. }
  482. else
  483. {
  484. __block UInt64 result;
  485. dispatch_sync(xmppQueue, ^{
  486. result = numberOfBytesReceived;
  487. });
  488. return result;
  489. }
  490. }
  491. - (BOOL)resetByteCountPerConnection
  492. {
  493. __block BOOL result = NO;
  494. dispatch_block_t block = ^{
  495. result = (config & kResetByteCountPerConnection) ? YES : NO;
  496. };
  497. if (dispatch_get_current_queue() == xmppQueue)
  498. block();
  499. else
  500. dispatch_sync(xmppQueue, block);
  501. return result;
  502. }
  503. - (void)setResetByteCountPerConnection:(BOOL)flag
  504. {
  505. dispatch_block_t block = ^{
  506. if (flag)
  507. config |= kResetByteCountPerConnection;
  508. else
  509. config &= ~kResetByteCountPerConnection;
  510. };
  511. if (dispatch_get_current_queue() == xmppQueue)
  512. block();
  513. else
  514. dispatch_async(xmppQueue, block);
  515. }
  516. #if TARGET_OS_IPHONE
  517. - (BOOL)enableBackgroundingOnSocket
  518. {
  519. __block BOOL result = NO;
  520. dispatch_block_t block = ^{
  521. result = (config & kEnableBackgroundingOnSocket) ? YES : NO;
  522. };
  523. if (dispatch_get_current_queue() == xmppQueue)
  524. block();
  525. else
  526. dispatch_sync(xmppQueue, block);
  527. return result;
  528. }
  529. - (void)setEnableBackgroundingOnSocket:(BOOL)flag
  530. {
  531. dispatch_block_t block = ^{
  532. if (flag)
  533. config |= kEnableBackgroundingOnSocket;
  534. else
  535. config &= ~kEnableBackgroundingOnSocket;
  536. };
  537. if (dispatch_get_current_queue() == xmppQueue)
  538. block();
  539. else
  540. dispatch_async(xmppQueue, block);
  541. }
  542. #endif
  543. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  544. #pragma mark Configuration
  545. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  546. - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
  547. {
  548. // Asynchronous operation (if outside xmppQueue)
  549. dispatch_block_t block = ^{
  550. [multicastDelegate addDelegate:delegate delegateQueue:delegateQueue];
  551. };
  552. if (dispatch_get_current_queue() == xmppQueue)
  553. block();
  554. else
  555. dispatch_async(xmppQueue, block);
  556. }
  557. - (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
  558. {
  559. // Synchronous operation
  560. dispatch_block_t block = ^{
  561. [multicastDelegate removeDelegate:delegate delegateQueue:delegateQueue];
  562. };
  563. if (dispatch_get_current_queue() == xmppQueue)
  564. block();
  565. else
  566. dispatch_sync(xmppQueue, block);
  567. }
  568. - (void)removeDelegate:(id)delegate
  569. {
  570. // Synchronous operation
  571. dispatch_block_t block = ^{
  572. [multicastDelegate removeDelegate:delegate];
  573. };
  574. if (dispatch_get_current_queue() == xmppQueue)
  575. block();
  576. else
  577. dispatch_sync(xmppQueue, block);
  578. }
  579. /**
  580. * Returns YES if the stream was opened in P2P mode.
  581. * In other words, the stream was created via initP2PFrom: to use XEP-0174.
  582. **/
  583. - (BOOL)isP2P
  584. {
  585. if (dispatch_get_current_queue() == xmppQueue)
  586. {
  587. return (config & kP2PMode) ? YES : NO;
  588. }
  589. else
  590. {
  591. __block BOOL result;
  592. dispatch_sync(xmppQueue, ^{
  593. result = (config & kP2PMode) ? YES : NO;
  594. });
  595. return result;
  596. }
  597. }
  598. - (BOOL)isP2PInitiator
  599. {
  600. if (dispatch_get_current_queue() == xmppQueue)
  601. {
  602. return ((config & kP2PMode) && (flags & kP2PInitiator));
  603. }
  604. else
  605. {
  606. __block BOOL result;
  607. dispatch_sync(xmppQueue, ^{
  608. result = ((config & kP2PMode) && (flags & kP2PInitiator));
  609. });
  610. return result;
  611. }
  612. }
  613. - (BOOL)isP2PRecipient
  614. {
  615. if (dispatch_get_current_queue() == xmppQueue)
  616. {
  617. return ((config & kP2PMode) && !(flags & kP2PInitiator));
  618. }
  619. else
  620. {
  621. __block BOOL result;
  622. dispatch_sync(xmppQueue, ^{
  623. result = ((config & kP2PMode) && !(flags & kP2PInitiator));
  624. });
  625. return result;
  626. }
  627. }
  628. - (BOOL)didStartNegotiation
  629. {
  630. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  631. return (flags & kDidStartNegotiation) ? YES : NO;
  632. }
  633. - (void)setDidStartNegotiation:(BOOL)flag
  634. {
  635. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  636. if (flag)
  637. flags |= kDidStartNegotiation;
  638. else
  639. flags &= ~kDidStartNegotiation;
  640. }
  641. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  642. #pragma mark Connection State
  643. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  644. /**
  645. * Returns YES if the connection is closed, and thus no stream is open.
  646. * If the stream is neither disconnected, nor connected, then a connection is currently being established.
  647. **/
  648. - (BOOL)isDisconnected
  649. {
  650. __block BOOL result = NO;
  651. dispatch_block_t block = ^{
  652. result = (state == STATE_XMPP_DISCONNECTED);
  653. };
  654. if (dispatch_get_current_queue() == xmppQueue)
  655. block();
  656. else
  657. dispatch_sync(xmppQueue, block);
  658. return result;
  659. }
  660. /**
  661. * Returns YES if the connection is open, and the stream has been properly established.
  662. * If the stream is neither disconnected, nor connected, then a connection is currently being established.
  663. **/
  664. - (BOOL)isConnected
  665. {
  666. __block BOOL result = NO;
  667. dispatch_block_t block = ^{
  668. result = (state == STATE_XMPP_CONNECTED);
  669. };
  670. if (dispatch_get_current_queue() == xmppQueue)
  671. block();
  672. else
  673. dispatch_sync(xmppQueue, block);
  674. return result;
  675. }
  676. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  677. #pragma mark C2S Connection
  678. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  679. - (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr
  680. {
  681. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  682. XMPPLogTrace();
  683. BOOL result = [asyncSocket connectToHost:host onPort:port error:errPtr];
  684. if (result && [self resetByteCountPerConnection])
  685. {
  686. numberOfBytesSent = 0;
  687. numberOfBytesReceived = 0;
  688. }
  689. return result;
  690. }
  691. - (BOOL)connect:(NSError **)errPtr
  692. {
  693. XMPPLogTrace();
  694. __block BOOL result = NO;
  695. __block NSError *err = nil;
  696. dispatch_block_t block = ^{ @autoreleasepool {
  697. if (state != STATE_XMPP_DISCONNECTED)
  698. {
  699. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  700. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  701. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  702. result = NO;
  703. return_from_block;
  704. }
  705. if ([self isP2P])
  706. {
  707. NSString *errMsg = @"P2P streams must use either connectTo:withAddress: or connectP2PWithSocket:.";
  708. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  709. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  710. result = NO;
  711. return_from_block;
  712. }
  713. if (myJID_setByClient == nil)
  714. {
  715. // Note: If you wish to use anonymous authentication, you should still set myJID prior to calling connect.
  716. // You can simply set it to something like "anonymous@<domain>", where "<domain>" is the proper domain.
  717. // After the authentication process, you can query the myJID property to see what your assigned JID is.
  718. //
  719. // Setting myJID allows the framework to follow the xmpp protocol properly,
  720. // and it allows the framework to connect to servers without a DNS entry.
  721. //
  722. // For example, one may setup a private xmpp server for internal testing on their local network.
  723. // The xmpp domain of the server may be something like "testing.mycompany.com",
  724. // but since the server is internal, an IP (192.168.1.22) is used as the hostname to connect.
  725. //
  726. // Proper connection requires a TCP connection to the IP (192.168.1.22),
  727. // but the xmpp handshake requires the xmpp domain (testing.mycompany.com).
  728. NSString *errMsg = @"You must set myJID before calling connect.";
  729. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  730. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  731. result = NO;
  732. return_from_block;
  733. }
  734. // Notify delegates
  735. [multicastDelegate xmppStreamWillConnect:self];
  736. if ([hostName length] == 0)
  737. {
  738. // Resolve the hostName via myJID SRV resolution
  739. state = STATE_XMPP_RESOLVING_SRV;
  740. srvResolver = [[XMPPSRVResolver alloc] initWithdDelegate:self delegateQueue:xmppQueue resolverQueue:NULL];
  741. srvResults = nil;
  742. srvResultsIndex = 0;
  743. NSString *srvName = [XMPPSRVResolver srvNameFromXMPPDomain:[myJID_setByClient domain]];
  744. [srvResolver startWithSRVName:srvName timeout:30.0];
  745. result = YES;
  746. }
  747. else
  748. {
  749. // Open TCP connection to the configured hostName.
  750. state = STATE_XMPP_CONNECTING;
  751. NSError *connectErr = nil;
  752. result = [self connectToHost:hostName onPort:hostPort error:&connectErr];
  753. if (!result)
  754. {
  755. err = connectErr;
  756. state = STATE_XMPP_DISCONNECTED;
  757. }
  758. }
  759. }};
  760. if (dispatch_get_current_queue() == xmppQueue)
  761. block();
  762. else
  763. dispatch_sync(xmppQueue, block);
  764. if (errPtr)
  765. *errPtr = err;
  766. return result;
  767. }
  768. - (BOOL)oldSchoolSecureConnect:(NSError **)errPtr
  769. {
  770. XMPPLogTrace();
  771. __block BOOL result = NO;
  772. __block NSError *err = nil;
  773. dispatch_block_t block = ^{ @autoreleasepool {
  774. // Go through the regular connect routine
  775. NSError *connectErr = nil;
  776. result = [self connect:&connectErr];
  777. if (result)
  778. {
  779. // Mark the secure flag.
  780. // We will check the flag in socket:didConnectToHost:port:
  781. [self setIsSecure:YES];
  782. }
  783. else
  784. {
  785. err = connectErr;
  786. }
  787. }};
  788. if (dispatch_get_current_queue() == xmppQueue)
  789. block();
  790. else
  791. dispatch_sync(xmppQueue, block);
  792. if (errPtr)
  793. *errPtr = err;
  794. return result;
  795. }
  796. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  797. #pragma mark P2P Connection
  798. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  799. /**
  800. * Starts a P2P connection to the given user and given address.
  801. * This method only works with XMPPStream objects created using the initP2P method.
  802. *
  803. * The given address is specified as a sockaddr structure wrapped in a NSData object.
  804. * For example, a NSData object returned from NSNetservice's addresses method.
  805. **/
  806. - (BOOL)connectTo:(XMPPJID *)jid withAddress:(NSData *)remoteAddr error:(NSError **)errPtr
  807. {
  808. XMPPLogTrace();
  809. __block BOOL result = YES;
  810. __block NSError *err = nil;
  811. dispatch_block_t block = ^{ @autoreleasepool {
  812. if (state != STATE_XMPP_DISCONNECTED)
  813. {
  814. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  815. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  816. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  817. result = NO;
  818. return_from_block;
  819. }
  820. if (![self isP2P])
  821. {
  822. NSString *errMsg = @"Non P2P streams must use the connect: method";
  823. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  824. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  825. result = NO;
  826. return_from_block;
  827. }
  828. // Turn on P2P initiator flag
  829. flags |= kP2PInitiator;
  830. // Store remoteJID
  831. remoteJID = [jid copy];
  832. NSAssert((asyncSocket == nil), @"Forgot to release the previous asyncSocket instance.");
  833. // Notify delegates
  834. [multicastDelegate xmppStreamWillConnect:self];
  835. // Update state
  836. state = STATE_XMPP_CONNECTING;
  837. // Initailize socket
  838. asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:xmppQueue];
  839. NSError *connectErr = nil;
  840. result = [asyncSocket connectToAddress:remoteAddr error:&connectErr];
  841. if (result == NO)
  842. {
  843. err = connectErr;
  844. state = STATE_XMPP_DISCONNECTED;
  845. }
  846. else if ([self resetByteCountPerConnection])
  847. {
  848. numberOfBytesSent = 0;
  849. numberOfBytesReceived = 0;
  850. }
  851. }};
  852. if (dispatch_get_current_queue() == xmppQueue)
  853. block();
  854. else
  855. dispatch_sync(xmppQueue, block);
  856. if (errPtr)
  857. *errPtr = err;
  858. return result;
  859. }
  860. /**
  861. * Starts a P2P connection with the given accepted socket.
  862. * This method only works with XMPPStream objects created using the initP2P method.
  863. *
  864. * The given socket should be a socket that has already been accepted.
  865. * The remoteJID will be extracted from the opening stream negotiation.
  866. **/
  867. - (BOOL)connectP2PWithSocket:(GCDAsyncSocket *)acceptedSocket error:(NSError **)errPtr
  868. {
  869. XMPPLogTrace();
  870. __block BOOL result = YES;
  871. __block NSError *err = nil;
  872. dispatch_block_t block = ^{ @autoreleasepool {
  873. if (state != STATE_XMPP_DISCONNECTED)
  874. {
  875. NSString *errMsg = @"Attempting to connect while already connected or connecting.";
  876. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  877. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  878. result = NO;
  879. return_from_block;
  880. }
  881. if (![self isP2P])
  882. {
  883. NSString *errMsg = @"Non P2P streams must use the connect: method";
  884. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  885. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidType userInfo:info];
  886. result = NO;
  887. return_from_block;
  888. }
  889. if (acceptedSocket == nil)
  890. {
  891. NSString *errMsg = @"Parameter acceptedSocket is nil.";
  892. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  893. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidParameter userInfo:info];
  894. result = NO;
  895. return_from_block;
  896. }
  897. // Turn off P2P initiator flag
  898. flags &= ~kP2PInitiator;
  899. NSAssert((asyncSocket == nil), @"Forgot to release the previous asyncSocket instance.");
  900. // Store and configure socket
  901. asyncSocket = acceptedSocket;
  902. [asyncSocket setDelegate:self delegateQueue:xmppQueue];
  903. // Notify delegates
  904. [multicastDelegate xmppStream:self socketDidConnect:asyncSocket];
  905. // Update state
  906. state = STATE_XMPP_CONNECTING;
  907. if ([self resetByteCountPerConnection])
  908. {
  909. numberOfBytesSent = 0;
  910. numberOfBytesReceived = 0;
  911. }
  912. // Start the XML stream
  913. [self startNegotiation];
  914. }};
  915. if (dispatch_get_current_queue() == xmppQueue)
  916. block();
  917. else
  918. dispatch_sync(xmppQueue, block);
  919. if (errPtr)
  920. *errPtr = err;
  921. return result;
  922. }
  923. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  924. #pragma mark Disconnect
  925. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  926. /**
  927. * Closes the connection to the remote host.
  928. **/
  929. - (void)disconnect
  930. {
  931. XMPPLogTrace();
  932. dispatch_block_t block = ^{ @autoreleasepool {
  933. if (state != STATE_XMPP_DISCONNECTED)
  934. {
  935. [multicastDelegate xmppStreamWasToldToDisconnect:self];
  936. if (state == STATE_XMPP_RESOLVING_SRV)
  937. {
  938. [srvResolver stop];
  939. srvResolver = nil;
  940. state = STATE_XMPP_DISCONNECTED;
  941. [multicastDelegate xmppStreamDidDisconnect:self withError:nil];
  942. }
  943. else
  944. {
  945. [asyncSocket disconnect];
  946. // Everthing will be handled in socketDidDisconnect:withError:
  947. }
  948. }
  949. }};
  950. if (dispatch_get_current_queue() == xmppQueue)
  951. block();
  952. else
  953. dispatch_sync(xmppQueue, block);
  954. }
  955. - (void)disconnectAfterSending
  956. {
  957. XMPPLogTrace();
  958. dispatch_block_t block = ^{ @autoreleasepool {
  959. if (state != STATE_XMPP_DISCONNECTED)
  960. {
  961. [multicastDelegate xmppStreamWasToldToDisconnect:self];
  962. if (state == STATE_XMPP_RESOLVING_SRV)
  963. {
  964. [srvResolver stop];
  965. srvResolver = nil;
  966. state = STATE_XMPP_DISCONNECTED;
  967. [multicastDelegate xmppStreamDidDisconnect:self withError:nil];
  968. }
  969. else
  970. {
  971. NSString *termStr = @"</stream:stream>";
  972. NSData *termData = [termStr dataUsingEncoding:NSUTF8StringEncoding];
  973. XMPPLogSend(@"SEND: %@", termStr);
  974. numberOfBytesSent += [termData length];
  975. [asyncSocket writeData:termData withTimeout:TIMEOUT_XMPP_WRITE tag:TAG_XMPP_WRITE_STREAM];
  976. [asyncSocket disconnectAfterWriting];
  977. // Everthing will be handled in socketDidDisconnect:withError:
  978. }
  979. }
  980. }};
  981. if (dispatch_get_current_queue() == xmppQueue)
  982. block();
  983. else
  984. dispatch_async(xmppQueue, block);
  985. }
  986. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  987. #pragma mark Security
  988. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  989. /**
  990. * Returns YES if SSL/TLS has been used to secure the connection.
  991. **/
  992. - (BOOL)isSecure
  993. {
  994. if (dispatch_get_current_queue() == xmppQueue)
  995. {
  996. return (flags & kIsSecure) ? YES : NO;
  997. }
  998. else
  999. {
  1000. __block BOOL result;
  1001. dispatch_sync(xmppQueue, ^{
  1002. result = (flags & kIsSecure) ? YES : NO;
  1003. });
  1004. return result;
  1005. }
  1006. }
  1007. - (void)setIsSecure:(BOOL)flag
  1008. {
  1009. dispatch_block_t block = ^{
  1010. if(flag)
  1011. flags |= kIsSecure;
  1012. else
  1013. flags &= ~kIsSecure;
  1014. };
  1015. if (dispatch_get_current_queue() == xmppQueue)
  1016. block();
  1017. else
  1018. dispatch_async(xmppQueue, block);
  1019. }
  1020. - (BOOL)supportsStartTLS
  1021. {
  1022. __block BOOL result = NO;
  1023. dispatch_block_t block = ^{ @autoreleasepool {
  1024. // The root element can be properly queried for authentication mechanisms anytime after the
  1025. // stream:features are received, and TLS has been setup (if required)
  1026. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1027. {
  1028. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1029. NSXMLElement *starttls = [features elementForName:@"starttls" xmlns:@"urn:ietf:params:xml:ns:xmpp-tls"];
  1030. result = (starttls != nil);
  1031. }
  1032. }};
  1033. if (dispatch_get_current_queue() == xmppQueue)
  1034. block();
  1035. else
  1036. dispatch_sync(xmppQueue, block);
  1037. return result;
  1038. }
  1039. - (void)sendStartTLSRequest
  1040. {
  1041. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  1042. XMPPLogTrace();
  1043. NSString *starttls = @"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
  1044. NSData *outgoingData = [starttls dataUsingEncoding:NSUTF8StringEncoding];
  1045. XMPPLogSend(@"SEND: %@", starttls);
  1046. numberOfBytesSent += [outgoingData length];
  1047. [asyncSocket writeData:outgoingData
  1048. withTimeout:TIMEOUT_XMPP_WRITE
  1049. tag:TAG_XMPP_WRITE_STREAM];
  1050. }
  1051. - (BOOL)secureConnection:(NSError **)errPtr
  1052. {
  1053. XMPPLogTrace();
  1054. __block BOOL result = YES;
  1055. __block NSError *err = nil;
  1056. dispatch_block_t block = ^{ @autoreleasepool {
  1057. if (state != STATE_XMPP_CONNECTED)
  1058. {
  1059. NSString *errMsg = @"Please wait until the stream is connected.";
  1060. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1061. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1062. result = NO;
  1063. return_from_block;
  1064. }
  1065. if ([self isSecure])
  1066. {
  1067. NSString *errMsg = @"The connection is already secure.";
  1068. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1069. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1070. result = NO;
  1071. return_from_block;
  1072. }
  1073. if (![self supportsStartTLS])
  1074. {
  1075. NSString *errMsg = @"The server does not support startTLS.";
  1076. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1077. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1078. result = NO;
  1079. return_from_block;
  1080. }
  1081. // Update state
  1082. state = STATE_XMPP_STARTTLS_1;
  1083. // Send the startTLS XML request
  1084. [self sendStartTLSRequest];
  1085. // We do not mark the stream as secure yet.
  1086. // We're waiting to receive the <proceed/> response from the
  1087. // server before we actually start the TLS handshake.
  1088. }};
  1089. if (dispatch_get_current_queue() == xmppQueue)
  1090. block();
  1091. else
  1092. dispatch_sync(xmppQueue, block);
  1093. if (errPtr)
  1094. *errPtr = err;
  1095. return result;
  1096. }
  1097. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1098. #pragma mark Registration
  1099. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1100. /**
  1101. * This method checks the stream features of the connected server to determine if in-band registartion is supported.
  1102. * If we are not connected to a server, this method simply returns NO.
  1103. **/
  1104. - (BOOL)supportsInBandRegistration
  1105. {
  1106. __block BOOL result = NO;
  1107. dispatch_block_t block = ^{ @autoreleasepool {
  1108. // The root element can be properly queried for authentication mechanisms anytime after the
  1109. // stream:features are received, and TLS has been setup (if required)
  1110. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1111. {
  1112. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1113. NSXMLElement *reg = [features elementForName:@"register" xmlns:@"http://jabber.org/features/iq-register"];
  1114. result = (reg != nil);
  1115. }
  1116. }};
  1117. if (dispatch_get_current_queue() == xmppQueue)
  1118. block();
  1119. else
  1120. dispatch_sync(xmppQueue, block);
  1121. return result;
  1122. }
  1123. /**
  1124. * This method attempts to register a new user on the server using the given username and password.
  1125. * The result of this action will be returned via the delegate methods.
  1126. *
  1127. * If the XMPPStream is not connected, or the server doesn't support in-band registration, this method does nothing.
  1128. **/
  1129. - (BOOL)registerWithPassword:(NSString *)password error:(NSError **)errPtr
  1130. {
  1131. XMPPLogTrace();
  1132. __block BOOL result = YES;
  1133. __block NSError *err = nil;
  1134. dispatch_block_t block = ^{ @autoreleasepool {
  1135. if (state != STATE_XMPP_CONNECTED)
  1136. {
  1137. NSString *errMsg = @"Please wait until the stream is connected.";
  1138. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1139. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1140. result = NO;
  1141. return_from_block;
  1142. }
  1143. if (myJID_setByClient == nil)
  1144. {
  1145. NSString *errMsg = @"You must set myJID before calling registerWithPassword:error:.";
  1146. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1147. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1148. result = NO;
  1149. return_from_block;
  1150. }
  1151. if (![self supportsInBandRegistration])
  1152. {
  1153. NSString *errMsg = @"The server does not support in band registration.";
  1154. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1155. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1156. result = NO;
  1157. return_from_block;
  1158. }
  1159. NSString *username = [myJID_setByClient user];
  1160. NSXMLElement *queryElement = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"];
  1161. [queryElement addChild:[NSXMLElement elementWithName:@"username" stringValue:username]];
  1162. [queryElement addChild:[NSXMLElement elementWithName:@"password" stringValue:password]];
  1163. NSXMLElement *iqElement = [NSXMLElement elementWithName:@"iq"];
  1164. [iqElement addAttributeWithName:@"type" stringValue:@"set"];
  1165. [iqElement addChild:queryElement];
  1166. NSString *outgoingStr = [iqElement compactXMLString];
  1167. NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
  1168. XMPPLogSend(@"SEND: %@", outgoingStr);
  1169. numberOfBytesSent += [outgoingData length];
  1170. [asyncSocket writeData:outgoingData
  1171. withTimeout:TIMEOUT_XMPP_WRITE
  1172. tag:TAG_XMPP_WRITE_STREAM];
  1173. // Update state
  1174. state = STATE_XMPP_REGISTERING;
  1175. }};
  1176. if (dispatch_get_current_queue() == xmppQueue)
  1177. block();
  1178. else
  1179. dispatch_sync(xmppQueue, block);
  1180. if (errPtr)
  1181. *errPtr = err;
  1182. return result;
  1183. }
  1184. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1185. #pragma mark Authentication
  1186. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1187. - (NSArray *)supportedAuthenticationMechanisms
  1188. {
  1189. __block NSMutableArray *result = [[NSMutableArray alloc] init];
  1190. dispatch_block_t block = ^{ @autoreleasepool {
  1191. // The root element can be properly queried for authentication mechanisms anytime after the
  1192. // stream:features are received, and TLS has been setup (if required).
  1193. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1194. {
  1195. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1196. NSXMLElement *mech = [features elementForName:@"mechanisms" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
  1197. NSArray *mechanisms = [mech elementsForName:@"mechanism"];
  1198. for (NSXMLElement *mechanism in mechanisms)
  1199. {
  1200. [result addObject:[mechanism stringValue]];
  1201. }
  1202. }
  1203. }};
  1204. if (dispatch_get_current_queue() == xmppQueue)
  1205. block();
  1206. else
  1207. dispatch_sync(xmppQueue, block);
  1208. return result;
  1209. }
  1210. /**
  1211. * This method checks the stream features of the connected server to determine
  1212. * if the given authentication mechanism is supported.
  1213. *
  1214. * If we are not connected to a server, this method simply returns NO.
  1215. **/
  1216. - (BOOL)supportsAuthenticationMechanism:(NSString *)mechanismType
  1217. {
  1218. __block BOOL result = NO;
  1219. dispatch_block_t block = ^{ @autoreleasepool {
  1220. // The root element can be properly queried for authentication mechanisms anytime after the
  1221. // stream:features are received, and TLS has been setup (if required).
  1222. if (state >= STATE_XMPP_POST_NEGOTIATION)
  1223. {
  1224. NSXMLElement *features = [rootElement elementForName:@"stream:features"];
  1225. NSXMLElement *mech = [features elementForName:@"mechanisms" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
  1226. NSArray *mechanisms = [mech elementsForName:@"mechanism"];
  1227. for (NSXMLElement *mechanism in mechanisms)
  1228. {
  1229. if ([[mechanism stringValue] isEqualToString:mechanismType])
  1230. {
  1231. result = YES;
  1232. break;
  1233. }
  1234. }
  1235. }
  1236. }};
  1237. if (dispatch_get_current_queue() == xmppQueue)
  1238. block();
  1239. else
  1240. dispatch_sync(xmppQueue, block);
  1241. return result;
  1242. }
  1243. - (BOOL)authenticate:(id <XMPPSASLAuthentication>)inAuth error:(NSError **)errPtr
  1244. {
  1245. XMPPLogTrace();
  1246. __block BOOL result = NO;
  1247. __block NSError *err = nil;
  1248. dispatch_block_t block = ^{ @autoreleasepool {
  1249. if (state != STATE_XMPP_CONNECTED)
  1250. {
  1251. NSString *errMsg = @"Please wait until the stream is connected.";
  1252. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1253. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1254. result = NO;
  1255. return_from_block;
  1256. }
  1257. if (myJID_setByClient == nil)
  1258. {
  1259. NSString *errMsg = @"You must set myJID before calling authenticate:error:.";
  1260. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1261. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1262. result = NO;
  1263. return_from_block;
  1264. }
  1265. // Change state.
  1266. // We do this now because when we invoke the start method below,
  1267. // it may in turn invoke our sendAuthElement method, which expects us to be in STATE_XMPP_AUTH.
  1268. state = STATE_XMPP_AUTH;
  1269. if ([inAuth start:&err])
  1270. {
  1271. auth = inAuth;
  1272. result = YES;
  1273. }
  1274. else
  1275. {
  1276. // Unable to start authentication for some reason.
  1277. // Revert back to connected state.
  1278. state = STATE_XMPP_CONNECTED;
  1279. }
  1280. }};
  1281. if (dispatch_get_current_queue() == xmppQueue)
  1282. block();
  1283. else
  1284. dispatch_sync(xmppQueue, block);
  1285. if (errPtr)
  1286. *errPtr = err;
  1287. return result;
  1288. }
  1289. /**
  1290. * This method applies to standard password authentication schemes only.
  1291. * This is NOT the primary authentication method.
  1292. *
  1293. * @see authenticate:error:
  1294. *
  1295. * This method exists for backwards compatibility, and may disappear in future versions.
  1296. **/
  1297. - (BOOL)authenticateWithPassword:(NSString *)inPassword error:(NSError **)errPtr
  1298. {
  1299. XMPPLogTrace();
  1300. // The given password parameter could be mutable
  1301. NSString *password = [inPassword copy];
  1302. __block BOOL result = YES;
  1303. __block NSError *err = nil;
  1304. dispatch_block_t block = ^{ @autoreleasepool {
  1305. if (state != STATE_XMPP_CONNECTED)
  1306. {
  1307. NSString *errMsg = @"Please wait until the stream is connected.";
  1308. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1309. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
  1310. result = NO;
  1311. return_from_block;
  1312. }
  1313. if (myJID_setByClient == nil)
  1314. {
  1315. NSString *errMsg = @"You must set myJID before calling authenticate:error:.";
  1316. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1317. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidProperty userInfo:info];
  1318. result = NO;
  1319. return_from_block;
  1320. }
  1321. // Choose the best authentication method.
  1322. //
  1323. // P.S. - This method is deprecated.
  1324. id <XMPPSASLAuthentication> someAuth = nil;
  1325. if ([self supportsDigestMD5Authentication])
  1326. {
  1327. someAuth = [[XMPPDigestMD5Authentication alloc] initWithStream:self password:password];
  1328. result = [self authenticate:someAuth error:&err];
  1329. }
  1330. else if ([self supportsPlainAuthentication])
  1331. {
  1332. someAuth = [[XMPPPlainAuthentication alloc] initWithStream:self password:password];
  1333. result = [self authenticate:someAuth error:&err];
  1334. }
  1335. else if ([self supportsDeprecatedDigestAuthentication])
  1336. {
  1337. someAuth = [[XMPPDeprecatedDigestAuthentication alloc] initWithStream:self password:password];
  1338. result = [self authenticate:someAuth error:&err];
  1339. }
  1340. else if ([self supportsDeprecatedPlainAuthentication])
  1341. {
  1342. someAuth = [[XMPPDeprecatedDigestAuthentication alloc] initWithStream:self password:password];
  1343. result = [self authenticate:someAuth error:&err];
  1344. }
  1345. else
  1346. {
  1347. NSString *errMsg = @"No suitable authentication method found";
  1348. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1349. err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
  1350. result = NO;
  1351. }
  1352. }};
  1353. if (dispatch_get_current_queue() == xmppQueue)
  1354. block();
  1355. else
  1356. dispatch_sync(xmppQueue, block);
  1357. if (errPtr)
  1358. *errPtr = err;
  1359. return result;
  1360. }
  1361. - (BOOL)isAuthenticated
  1362. {
  1363. __block BOOL result = NO;
  1364. dispatch_block_t block = ^{
  1365. result = (flags & kIsAuthenticated) ? YES : NO;
  1366. };
  1367. if (dispatch_get_current_queue() == xmppQueue)
  1368. block();
  1369. else
  1370. dispatch_sync(xmppQueue, block);
  1371. return result;
  1372. }
  1373. - (void)setIsAuthenticated:(BOOL)flag
  1374. {
  1375. dispatch_block_t block = ^{
  1376. if(flag)
  1377. flags |= kIsAuthenticated;
  1378. else
  1379. flags &= ~kIsAuthenticated;
  1380. };
  1381. if (dispatch_get_current_queue() == xmppQueue)
  1382. block();
  1383. else
  1384. dispatch_async(xmppQueue, block);
  1385. }
  1386. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1387. #pragma mark General Methods
  1388. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1389. /**
  1390. * This method will return the root element of the document.
  1391. * This element contains the opening <stream:stream/> and <stream:features/> tags received from the server
  1392. * when the XML stream was opened.
  1393. *
  1394. * Note: The rootElement is empty, and does not contain all the XML elements the stream has received during it's
  1395. * connection. This is done for performance reasons and for the obvious benefit of being more memory efficient.
  1396. **/
  1397. - (NSXMLElement *)rootElement
  1398. {
  1399. if (dispatch_get_current_queue() == xmppQueue)
  1400. {
  1401. return rootElement;
  1402. }
  1403. else
  1404. {
  1405. __block NSXMLElement *result = nil;
  1406. dispatch_sync(xmppQueue, ^{
  1407. result = [rootElement copy];
  1408. });
  1409. return result;
  1410. }
  1411. }
  1412. /**
  1413. * Returns the version attribute from the servers's <stream:stream/> element.
  1414. * This should be at least 1.0 to be RFC 3920 compliant.
  1415. * If no version number was set, the server is not RFC compliant, and 0 is returned.
  1416. **/
  1417. - (float)serverXmppStreamVersionNumber
  1418. {
  1419. if (dispatch_get_current_queue() == xmppQueue)
  1420. {
  1421. return [rootElement attributeFloatValueForName:@"version" withDefaultValue:0.0F];
  1422. }
  1423. else
  1424. {
  1425. __block float result;
  1426. dispatch_sync(xmppQueue, ^{
  1427. result = [rootElement attributeFloatValueForName:@"version" withDefaultValue:0.0F];
  1428. });
  1429. return result;
  1430. }
  1431. }
  1432. - (void)sendIQ:(XMPPIQ *)iq withTag:(long)tag
  1433. {
  1434. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  1435. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1436. // We're getting ready to send an IQ.
  1437. // Notify delegates to allow them to optionally alter/filter the outgoing IQ.
  1438. SEL selector = @selector(xmppStream:willSendIQ:);
  1439. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  1440. {
  1441. // None of the delegates implement the method.
  1442. // Use a shortcut.
  1443. [self continueSendIQ:iq withTag:tag];
  1444. }
  1445. else
  1446. {
  1447. // Notify all interested delegates.
  1448. // This must be done serially to allow them to alter the element in a thread-safe manner.
  1449. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  1450. dispatch_async(willSendIqQueue, ^{ @autoreleasepool {
  1451. // Allow delegates to modify and/or filter outgoing element
  1452. __block XMPPIQ *modifiedIQ = iq;
  1453. id del;
  1454. dispatch_queue_t dq;
  1455. while (modifiedIQ && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  1456. {
  1457. #if DEBUG
  1458. {
  1459. char methodReturnType[32];
  1460. Method method = class_getInstanceMethod([del class], selector);
  1461. method_getReturnType(method, methodReturnType, sizeof(methodReturnType));
  1462. if (strcmp(methodReturnType, @encode(XMPPIQ*)) != 0)
  1463. {
  1464. NSAssert(NO, @"Method xmppStream:willSendIQ: is no longer void (see XMPPStream.h). "
  1465. @"Culprit = %@", NSStringFromClass([del class]));
  1466. }
  1467. }
  1468. #endif
  1469. dispatch_sync(dq, ^{ @autoreleasepool {
  1470. modifiedIQ = [del xmppStream:self willSendIQ:modifiedIQ];
  1471. }});
  1472. }
  1473. if (modifiedIQ)
  1474. {
  1475. dispatch_async(xmppQueue, ^{ @autoreleasepool {
  1476. if (state == STATE_XMPP_CONNECTED) {
  1477. [self continueSendIQ:modifiedIQ withTag:tag];
  1478. }
  1479. }});
  1480. }
  1481. }});
  1482. }
  1483. }
  1484. - (void)sendMessage:(XMPPMessage *)message withTag:(long)tag
  1485. {
  1486. NSAssert(dispatch_get_current_queue() == xmppQueue, @"Invoked on incorrect queue");
  1487. NSAssert(state == STATE_XMPP_CONNECTED, @"Invoked with incorrect state");
  1488. // We're getting ready to send a message.
  1489. // Notify delegates to allow them to optionally alter/filter the outgoing message.
  1490. SEL selector = @selector(xmppStream:willSendMessage:);
  1491. if (![multicastDelegate hasDelegateThatRespondsToSelector:selector])
  1492. {
  1493. // None of the delegates implement the method.
  1494. // Use a shortcut.
  1495. [self continueSendMessage:message withTag:tag];
  1496. }
  1497. else
  1498. {
  1499. // Notify all interested delegates.
  1500. // This must be done serially to allow them to alter the element in a thread-safe manner.
  1501. GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator];
  1502. dispatch_async(willSendMessageQueue, ^{ @autoreleasepool {
  1503. // Allow delegates to modify outgoing element
  1504. __block XMPPMessage *modifiedMessage = message;
  1505. id del;
  1506. dispatch_queue_t dq;
  1507. while (modifiedMessage && [delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector])
  1508. {
  1509. #if DEBUG
  1510. {
  1511. char metho