PageRenderTime 64ms CodeModel.GetById 24ms 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

Large files files are truncated, but you can click here to view the full 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 e…

Large files files are truncated, but you can click here to view the full file