#import #import "XMPPSASLAuthentication.h" #import "XMPPCustomBinding.h" #import "GCDMulticastDelegate.h" #import "CocoaAsyncSocket/GCDAsyncSocket.h" #if TARGET_OS_IPHONE #import "DDXML.h" #endif @class XMPPSRVResolver; @class XMPPParser; @class XMPPJID; @class XMPPIQ; @class XMPPMessage; @class XMPPPresence; @class XMPPModule; @class XMPPElement; @class XMPPElementReceipt; @protocol XMPPStreamDelegate; #if TARGET_OS_IPHONE #define MIN_KEEPALIVE_INTERVAL 20.0 // 20 Seconds #define DEFAULT_KEEPALIVE_INTERVAL 120.0 // 2 Minutes #else #define MIN_KEEPALIVE_INTERVAL 10.0 // 10 Seconds #define DEFAULT_KEEPALIVE_INTERVAL 300.0 // 5 Minutes #endif extern NSString *const XMPPStreamErrorDomain; typedef NS_ENUM(NSUInteger, XMPPStreamErrorCode) { XMPPStreamInvalidType, // Attempting to access P2P methods in a non-P2P stream, or vice-versa XMPPStreamInvalidState, // Invalid state for requested action, such as connect when already connected XMPPStreamInvalidProperty, // Missing a required property, such as myJID XMPPStreamInvalidParameter, // Invalid parameter, such as a nil JID XMPPStreamUnsupportedAction, // The server doesn't support the requested action }; typedef NS_ENUM(NSUInteger, XMPPStreamStartTLSPolicy) { XMPPStreamStartTLSPolicyAllowed, // TLS will be used if the server requires it XMPPStreamStartTLSPolicyPreferred, // TLS will be used if the server offers it XMPPStreamStartTLSPolicyRequired // TLS will be used if the server offers it, else the stream won't connect }; extern const NSTimeInterval XMPPStreamTimeoutNone; @interface XMPPStream : NSObject /** * Standard XMPP initialization. * The stream is a standard client to server connection. * * P2P streams using XEP-0174 are also supported. * See the P2P section below. **/ - (id)init; /** * Peer to Peer XMPP initialization. * The stream is a direct client to client connection as outlined in XEP-0174. **/ - (id)initP2PFrom:(XMPPJID *)myJID; /** * XMPPStream uses a multicast delegate. * This allows one to add multiple delegates to a single XMPPStream instance, * which makes it easier to separate various components and extensions. * * For example, if you were implementing two different custom extensions on top of XMPP, * you could put them in separate classes, and simply add each as a delegate. **/ - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; - (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; - (void)removeDelegate:(id)delegate; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Properties //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * The server's hostname that should be used to make the TCP connection. * This may be a domain name (e.g. "deusty.com") or an IP address (e.g. "70.85.193.226"). * * Note that this may be different from the virtual xmpp hostname. * Just as HTTP servers can support mulitple virtual hosts from a single server, so too can xmpp servers. * A prime example is google via google apps. * * For example, say you own the domain "mydomain.com". * If you go to mydomain.com in a web browser, * you are directed to your apache server running on your webserver somewhere in the cloud. * But you use google apps for your email and xmpp needs. * So if somebody sends you an email, it actually goes to google's servers, where you later access it from. * Similarly, you connect to google's servers to sign into xmpp. * * In the example above, your hostname is "talk.google.com" and your JID is "me@mydomain.com". * * This hostName property is optional. * If you do not set the hostName, then the framework will follow the xmpp specification using jid's domain. * That is, it first do an SRV lookup (as specified in the xmpp RFC). * If that fails, it will fall back to simply attempting to connect to the jid's domain. **/ @property (readwrite, copy) NSString *hostName; /** * The port the xmpp server is running on. * If you do not explicitly set the port, the default port will be used. * If you set the port to zero, the default port will be used. * * The default port is 5222. **/ @property (readwrite, assign) UInt16 hostPort; /** * The stream's policy on when to Start TLS. * * The default is XMPPStreamStartTLSPolicyAllowed. * * @see XMPPStreamStartTLSPolicy **/ @property (readwrite, assign) XMPPStreamStartTLSPolicy startTLSPolicy; /** * The JID of the user. * * This value is required, and is used in many parts of the underlying implementation. * When connecting, the domain of the JID is used to properly specify the correct xmpp virtual host. * It is used during registration to supply the username of the user to create an account for. * It is used during authentication to supply the username of the user to authenticate with. * And the resource may be used post-authentication during the required xmpp resource binding step. * * A proper JID is of the form user@domain/resource. * For example: robbiehanson@deusty.com/work * * The resource is optional, in the sense that if one is not supplied, * one will be automatically generated for you (either by us or by the server). * * Please note: * Resource collisions are handled in different ways depending on server configuration. * * For example: * You are signed in with user1@domain.com/home on your desktop. * Then you attempt to sign in with user1@domain.com/home on your laptop. * * The server could possibly: * - Reject the resource request for the laptop. * - Accept the resource request for the laptop, and immediately disconnect the desktop. * - Automatically assign the laptop another resource without a conflict. * * For this reason, you may wish to check the myJID variable after the stream has been connected, * just in case the resource was changed by the server. **/ @property (readwrite, copy) XMPPJID *myJID; /** * Only used in P2P streams. **/ @property (strong, readonly) XMPPJID *remoteJID; /** * Many routers will teardown a socket mapping if there is no activity on the socket. * For this reason, the xmpp stream supports sending keep-alive data. * This is simply whitespace, which is ignored by the xmpp protocol. * * Keep-alive data is only sent in the absence of any other data being sent/received. * * The default value is defined in DEFAULT_KEEPALIVE_INTERVAL. * The minimum value is defined in MIN_KEEPALIVE_INTERVAL. * * To disable keep-alive, set the interval to zero (or any non-positive number). * * The keep-alive timer (if enabled) fires every (keepAliveInterval / 4) seconds. * Upon firing it checks when data was last sent/received, * and sends keep-alive data if the elapsed time has exceeded the keepAliveInterval. * Thus the effective resolution of the keepalive timer is based on the interval. * * @see keepAliveWhitespaceCharacter **/ @property (readwrite, assign) NSTimeInterval keepAliveInterval; /** * The keep-alive mechanism sends whitespace which is ignored by the xmpp protocol. * The default whitespace character is a space (' '). * * This can be changed, for whatever reason, to another whitespace character. * Valid whitespace characters are space(' '), tab('\t') and newline('\n'). * * If you attempt to set the character to any non-whitespace character, the attempt is ignored. * * @see keepAliveInterval **/ @property (readwrite, assign) char keepAliveWhitespaceCharacter; /** * Represents the last sent presence element concerning the presence of myJID on the server. * In other words, it represents the presence as others see us. * * This excludes presence elements sent concerning subscriptions, MUC rooms, etc. * * @see resendMyPresence **/ @property (strong, readonly) XMPPPresence *myPresence; /** * Returns the total number of bytes bytes sent/received by the xmpp stream. * * By default this is the byte count since the xmpp stream object has been created. * If the stream has connected/disconnected/reconnected multiple times, * the count will be the summation of all connections. * * The functionality may optionaly be changed to count only the current socket connection. * @see resetByteCountPerConnection **/ @property (readonly) uint64_t numberOfBytesSent; @property (readonly) uint64_t numberOfBytesReceived; /** * Same as the individual properties, * but provides a way to fetch them in one atomic operation. **/ - (void)getNumberOfBytesSent:(uint64_t *)bytesSentPtr numberOfBytesReceived:(uint64_t *)bytesReceivedPtr; /** * Affects the funtionality of the byte counter. * * The default value is NO. * * If set to YES, the byte count will be reset just prior to a new connection (in the connect methods). **/ @property (readwrite, assign) BOOL resetByteCountPerConnection; /** * The tag property allows you to associate user defined information with the stream. * Tag values are not used internally, and should not be used by xmpp modules. **/ @property (readwrite, strong) id tag; /** * RFC 6121 states that starting a session is no longer required. * To skip this step set skipStartSession to YES. * * [RFC3921] specified one additional * precondition: formal establishment of an instant messaging and * presence session. Implementation and deployment experience has * shown that this additional step is unnecessary. However, for * backward compatibility an implementation MAY still offer that * feature. This enables older software to connect while letting * newer software save a round trip. * * The default value is NO. **/ @property (readwrite, assign) BOOL skipStartSession; /** * Validates that a response element is FROM the jid that the request element was sent TO. * Supports validating responses when request didn't specify a TO. * * @see isValidResponseElementFrom:forRequestElementTo: * @see isValidResponseElement:forRequestElement: * * The default value is NO. **/ @property (readwrite, assign) BOOL validatesResponses; #if TARGET_OS_IPHONE /** * If set, the kCFStreamNetworkServiceTypeVoIP flags will be set on the underlying CFRead/Write streams. * * The default value is NO. **/ @property (readwrite, assign) BOOL enableBackgroundingOnSocket; #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark State //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Returns YES if the connection is closed, and thus no stream is open. * If the stream is neither disconnected, nor connected, then a connection is currently being established. **/ - (BOOL)isDisconnected; /** * Returns YES is the connection is currently connecting **/ - (BOOL)isConnecting; /** * Returns YES if the connection is open, and the stream has been properly established. * If the stream is neither disconnected, nor connected, then a connection is currently being established. * * If this method returns YES, then it is ready for you to start sending and receiving elements. **/ - (BOOL)isConnected; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Connect & Disconnect //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Connects to the configured hostName on the configured hostPort. * The timeout is optional. To not time out use XMPPStreamTimeoutNone. * If the hostName or myJID are not set, this method will return NO and set the error parameter. **/ - (BOOL)connectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; /** * THIS IS DEPRECATED BY THE XMPP SPECIFICATION. * * The xmpp specification outlines the proper use of SSL/TLS by negotiating * the startTLS upgrade within the stream negotiation. * This method exists for those ancient servers that still require the connection to be secured prematurely. * The timeout is optional. To not time out use XMPPStreamTimeoutNone. * * Note: Such servers generally use port 5223 for this, which you will need to set. **/ - (BOOL)oldSchoolSecureConnectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; /** * Starts a P2P connection to the given user and given address. * The timeout is optional. To not time out use XMPPStreamTimeoutNone. * This method only works with XMPPStream objects created using the initP2P method. * * The given address is specified as a sockaddr structure wrapped in a NSData object. * For example, a NSData object returned from NSNetservice's addresses method. **/ - (BOOL)connectTo:(XMPPJID *)remoteJID withAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; /** * Starts a P2P connection with the given accepted socket. * This method only works with XMPPStream objects created using the initP2P method. * * The given socket should be a socket that has already been accepted. * The remoteJID will be extracted from the opening stream negotiation. **/ - (BOOL)connectP2PWithSocket:(GCDAsyncSocket *)acceptedSocket error:(NSError **)errPtr; /** * Disconnects from the remote host by closing the underlying TCP socket connection. * The terminating element is not sent to the server. * * This method is synchronous. * Meaning that the disconnect will happen immediately, even if there are pending elements yet to be sent. * * The xmppStreamDidDisconnect:withError: delegate method will immediately be dispatched onto the delegate queue. **/ - (void)disconnect; /** * Disconnects from the remote host by sending the terminating element, * and then closing the underlying TCP socket connection. * * This method is asynchronous. * The disconnect will happen after all pending elements have been sent. * Attempting to send elements after this method has been called will not work (the elements won't get sent). **/ - (void)disconnectAfterSending; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Security //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Returns YES if SSL/TLS was used to establish a connection to the server. * * Some servers may require an "upgrade to TLS" in order to start communication, * so even if the connection was not explicitly secured, an ugrade to TLS may have occured. * * See also the xmppStream:willSecureWithSettings: delegate method. **/ - (BOOL)isSecure; /** * Returns whether or not the server supports securing the connection via SSL/TLS. * * Some servers will actually require a secure connection, * in which case the stream will attempt to secure the connection during the opening process. * * If the connection has already been secured, this method may return NO. **/ - (BOOL)supportsStartTLS; /** * Attempts to secure the connection via SSL/TLS. * * This method is asynchronous. * The SSL/TLS handshake will occur in the background, and * the xmppStreamDidSecure: delegate method will be called after the TLS process has completed. * * This method returns immediately. * If the secure process was started, it will return YES. * If there was an issue while starting the security process, * this method will return NO and set the error parameter. * * The errPtr parameter is optional - you may pass nil. * * You may wish to configure the security settings via the xmppStream:willSecureWithSettings: delegate method. * * If the SSL/TLS handshake fails, the connection will be closed. * The reason for the error will be reported via the xmppStreamDidDisconnect:withError: delegate method. * The error parameter will be an NSError object, and may have an error domain of kCFStreamErrorDomainSSL. * The corresponding error code is documented in Apple's Security framework, in SecureTransport.h **/ - (BOOL)secureConnection:(NSError **)errPtr; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Registration //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * In Band Registration. * Creating a user account on the xmpp server within the xmpp protocol. * * The registerWithElements:error: method is asynchronous. * It will return immediately, and the delegate methods are used to determine success. * See the xmppStreamDidRegister: and xmppStream:didNotRegister: methods. * * If there is something immediately wrong, such as the stream is not connected, * this method will return NO and set the error. * * The errPtr parameter is optional - you may pass nil. * * registerWithPassword:error: is a convience method for creating an account using the given username and password. * * Security Note: * The password will be sent in the clear unless the stream has been secured. **/ - (BOOL)supportsInBandRegistration; - (BOOL)registerWithElements:(NSArray *)elements error:(NSError **)errPtr; - (BOOL)registerWithPassword:(NSString *)password error:(NSError **)errPtr; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Authentication //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Returns the server's list of supported authentication mechanisms. * Each item in the array will be of type NSString. * * For example, if the server supplied this stanza within it's reported stream:features: * * * DIGEST-MD5 * PLAIN * * * Then this method would return [@"DIGEST-MD5", @"PLAIN"]. **/ - (NSArray *)supportedAuthenticationMechanisms; /** * Returns whether or not the given authentication mechanism name was specified in the * server's list of supported authentication mechanisms. * * Note: The authentication classes often provide a category on XMPPStream, adding useful methods. * * @see XMPPPlainAuthentication - supportsPlainAuthentication * @see XMPPDigestMD5Authentication - supportsDigestMD5Authentication * @see XMPPXFacebookPlatformAuthentication - supportsXFacebookPlatformAuthentication * @see XMPPDeprecatedPlainAuthentication - supportsDeprecatedPlainAuthentication * @see XMPPDeprecatedDigestAuthentication - supportsDeprecatedDigestAuthentication **/ - (BOOL)supportsAuthenticationMechanism:(NSString *)mechanism; /** * This is the root authentication method. * All other authentication methods go through this one. * * This method attempts to start the authentication process given the auth instance. * That is, this method will invoke start: on the given auth instance. * If it returns YES, then the stream will enter into authentication mode. * It will then continually invoke the handleAuth: method on the given instance until authentication is complete. * * This method is asynchronous. * * If there is something immediately wrong, such as the stream is not connected, * the method will return NO and set the error. * Otherwise the delegate callbacks are used to communicate auth success or failure. * * @see xmppStreamDidAuthenticate: * @see xmppStream:didNotAuthenticate: * * @see authenticateWithPassword:error: * * Note: The security process is abstracted in order to provide flexibility, * and allow developers to easily implement their own custom authentication protocols. * The authentication classes often provide a category on XMPPStream, adding useful methods. * * @see XMPPXFacebookPlatformAuthentication - authenticateWithFacebookAccessToken:error: **/ - (BOOL)authenticate:(id )auth error:(NSError **)errPtr; /** * This method applies to standard password authentication schemes only. * This is NOT the primary authentication method. * * @see authenticate:error: * * This method exists for backwards compatibility, and may disappear in future versions. **/ - (BOOL)authenticateWithPassword:(NSString *)password error:(NSError **)errPtr; /** * Returns whether or not the xmpp stream is currently authenticating with the XMPP Server. **/ - (BOOL)isAuthenticating; /** * Returns whether or not the xmpp stream has successfully authenticated with the server. **/ - (BOOL)isAuthenticated; /** * Returns the date when the xmpp stream successfully authenticated with the server. **/ - (NSDate *)authenticationDate; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Compression //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Returns the server's list of supported compression methods in accordance to XEP-0138: Stream Compression * Each item in the array will be of type NSString. * * For example, if the server supplied this stanza within it's reported stream:features: * * * zlib * lzw * * * Then this method would return [@"zlib", @"lzw"]. **/ - (NSArray *)supportedCompressionMethods; /** * Returns whether or not the given compression method name was specified in the * server's list of supported compression methods. * * Note: The XMPPStream doesn't currently support any compression methods **/ - (BOOL)supportsCompressionMethod:(NSString *)compressionMethod; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Server Info //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * This method will return the root element of the document. * This element contains the opening and tags received from the server. * * If multiple have been received during the course of stream negotiation, * the root element contains only the most recent (current) version. * * Note: The rootElement is "empty", in-so-far as it does not contain all the XML elements the stream has * received during it's connection. This is done for performance reasons and for the obvious benefit * of being more memory efficient. **/ - (NSXMLElement *)rootElement; /** * Returns the version attribute from the servers's element. * This should be at least 1.0 to be RFC 3920 compliant. * If no version number was set, the server is not RFC compliant, and 0 is returned. **/ - (float)serverXmppStreamVersionNumber; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Sending //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Sends the given XML element. * If the stream is not yet connected, this method does nothing. **/ - (void)sendElement:(NSXMLElement *)element; /** * Just like the sendElement: method above, * but allows you to receive a receipt that can later be used to verify the element has been sent. * * If you later want to check to see if the element has been sent: * * if ([receipt wait:0]) { * // Element has been sent * } * * If you later want to wait until the element has been sent: * * if ([receipt wait:-1]) { * // Element was sent * } else { * // Element failed to send due to disconnection * } * * It is important to understand what it means when [receipt wait:timeout] returns YES. * It does NOT mean the server has received the element. * It only means the data has been queued for sending in the underlying OS socket buffer. * * So at this point the OS will do everything in its capacity to send the data to the server, * which generally means the server will eventually receive the data. * Unless, of course, something horrible happens such as a network failure, * or a system crash, or the server crashes, etc. * * Even if you close the xmpp stream after this point, the OS will still do everything it can to send the data. **/ - (void)sendElement:(NSXMLElement *)element andGetReceipt:(XMPPElementReceipt **)receiptPtr; /** * Fetches and resends the myPresence element (if available) in a single atomic operation. * * There are various xmpp extensions that hook into the xmpp stream and append information to outgoing presence stanzas. * For example, the XMPPCapabilities module automatically appends capabilities information (as a hash). * When these modules need to update/change their appended information, * they should use this method to do so. * * The alternative is to fetch the myPresence element, and resend it manually using the sendElement method. * However, that is 2 seperate operations, and the user, may send a different presence element inbetween. * Using this method guarantees everything is done as an atomic operation. **/ - (void)resendMyPresence; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Stanza Validation //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Validates that a response element is FROM the jid that the request element was sent TO. * Supports validating responses when request didn't specify a TO. **/ - (BOOL)isValidResponseElementFrom:(XMPPJID *)from forRequestElementTo:(XMPPJID *)to; - (BOOL)isValidResponseElement:(XMPPElement *)response forRequestElement:(XMPPElement *)request; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Module Plug-In System //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * The XMPPModule class automatically invokes these methods when it is activated/deactivated. * * The registerModule method registers the module with the xmppStream. * If there are any other modules that have requested to be automatically added as delegates to modules of this type, * then those modules are automatically added as delegates during the asynchronous execution of this method. * * The registerModule method is asynchronous. * * The unregisterModule method unregisters the module with the xmppStream, * and automatically removes it as a delegate of any other module. * * The unregisterModule method is fully synchronous. * That is, after this method returns, the module will not be scheduled in any more delegate calls from other modules. * However, if the module was already scheduled in an existing asynchronous delegate call from another module, * the scheduled delegate invocation remains queued and will fire in the near future. * Since the delegate invocation is already queued, * the module's retainCount has been incremented, * and the module will not be deallocated until after the delegate invocation has fired. **/ - (void)registerModule:(XMPPModule *)module; - (void)unregisterModule:(XMPPModule *)module; /** * Automatically registers the given delegate with all current and future registered modules of the given class. * * That is, the given delegate will be added to the delegate list ([module addDelegate:delegate delegateQueue:dq]) to * all current and future registered modules that respond YES to [module isKindOfClass:aClass]. * * This method is used by modules to automatically integrate with other modules. * For example, a module may auto-add itself as a delegate to XMPPCapabilities * so that it can broadcast its implemented features. * * This may also be useful to clients, for example, to add a delegate to instances of something like XMPPChatRoom, * where there may be multiple instances of the module that get created during the course of an xmpp session. * * If you auto register on multiple queues, you can remove all registrations with a single * call to removeAutoDelegate::: by passing NULL as the 'dq' parameter. * * If you auto register for multiple classes, you can remove all registrations with a single * call to removeAutoDelegate::: by passing nil as the 'aClass' parameter. **/ - (void)autoAddDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue toModulesOfClass:(Class)aClass; - (void)removeAutoDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue fromModulesOfClass:(Class)aClass; /** * Allows for enumeration of the currently registered modules. * * This may be useful if the stream needs to be queried for modules of a particular type. **/ - (void)enumerateModulesWithBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))block; /** * Allows for enumeration of the currently registered modules that are a kind of Class. * idx is in relation to all modules not just those of the given class. **/ - (void)enumerateModulesOfClass:(Class)aClass withBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))block; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Utilities //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generates and returns a new autoreleased UUID. * UUIDs (Universally Unique Identifiers) may also be known as GUIDs (Globally Unique Identifiers). * * The UUID is generated using the CFUUID library, which generates a unique 128 bit value. * The uuid is then translated into a string using the standard format for UUIDs: * "68753A44-4D6F-1226-9C60-0050E4C00067" * * This method is most commonly used to generate a unique id value for an xmpp element. **/ + (NSString *)generateUUID; - (NSString *)generateUUID; @end //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @interface XMPPElementReceipt : NSObject { uint32_t atomicFlags; dispatch_semaphore_t semaphore; } /** * Element receipts allow you to check to see if the element has been sent. * The timeout parameter allows you to do any of the following: * * - Do an instantaneous check (pass timeout == 0) * - Wait until the element has been sent (pass timeout < 0) * - Wait up to a certain amount of time (pass timeout > 0) * * It is important to understand what it means when [receipt wait:timeout] returns YES. * It does NOT mean the server has received the element. * It only means the data has been queued for sending in the underlying OS socket buffer. * * So at this point the OS will do everything in its capacity to send the data to the server, * which generally means the server will eventually receive the data. * Unless, of course, something horrible happens such as a network failure, * or a system crash, or the server crashes, etc. * * Even if you close the xmpp stream after this point, the OS will still do everything it can to send the data. **/ - (BOOL)wait:(NSTimeInterval)timeout; @end //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @protocol XMPPStreamDelegate @optional /** * This method is called before the stream begins the connection process. * * If developing an iOS app that runs in the background, this may be a good place to indicate * that this is a task that needs to continue running in the background. **/ - (void)xmppStreamWillConnect:(XMPPStream *)sender; /** * This method is called after the tcp socket has connected to the remote host. * It may be used as a hook for various things, such as updating the UI or extracting the server's IP address. * * If developing an iOS app that runs in the background, * please use XMPPStream's enableBackgroundingOnSocket property as opposed to doing it directly on the socket here. **/ - (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket; /** * This method is called after a TCP connection has been established with the server, * and the opening XML stream negotiation has started. **/ - (void)xmppStreamDidStartNegotiation:(XMPPStream *)sender; /** * This method is called immediately prior to the stream being secured via TLS/SSL. * Note that this delegate may be called even if you do not explicitly invoke the startTLS method. * Servers have the option of requiring connections to be secured during the opening process. * If this is the case, the XMPPStream will automatically attempt to properly secure the connection. * * The dictionary of settings is what will be passed to the startTLS method of the underlying GCDAsyncSocket. * The GCDAsyncSocket header file contains a discussion of the available key/value pairs, * as well as the security consequences of various options. * It is recommended reading if you are planning on implementing this method. * * The dictionary of settings that are initially passed will be an empty dictionary. * If you choose not to implement this method, or simply do not edit the dictionary, * then the default settings will be used. * That is, the kCFStreamSSLPeerName will be set to the configured host name, * and the default security validation checks will be performed. * * This means that authentication will fail if the name on the X509 certificate of * the server does not match the value of the hostname for the xmpp stream. * It will also fail if the certificate is self-signed, or if it is expired, etc. * * These settings are most likely the right fit for most production environments, * but may need to be tweaked for development or testing, * where the development server may be using a self-signed certificate. * * Note: If your development server is using a self-signed certificate, * you likely need to add GCDAsyncSocketManuallyEvaluateTrust=YES to the settings. * Then implement the xmppStream:didReceiveTrust:completionHandler: delegate method to perform custom validation. **/ - (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings; /** * Allows a delegate to hook into the TLS handshake and manually validate the peer it's connecting to. * * This is only called if the stream is secured with settings that include: * - GCDAsyncSocketManuallyEvaluateTrust == YES * That is, if a delegate implements xmppStream:willSecureWithSettings:, and plugs in that key/value pair. * * Thus this delegate method is forwarding the TLS evaluation callback from the underlying GCDAsyncSocket. * * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer. * * Note from Apple's documentation: * Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain, * [it] might block while attempting network access. You should never call it from your main thread; * call it only from within a function running on a dispatch queue or on a separate thread. * * This is why this method uses a completionHandler block rather than a normal return value. * The idea is that you should be performing SecTrustEvaluate on a background thread. * The completionHandler block is thread-safe, and may be invoked from a background queue/thread. * It is safe to invoke the completionHandler block even if the socket has been closed. * * Keep in mind that you can do all kinds of cool stuff here. * For example: * * If your development server is using a self-signed certificate, * then you could embed info about the self-signed cert within your app, and use this callback to ensure that * you're actually connecting to the expected dev server. * * Also, you could present certificates that don't pass SecTrustEvaluate to the client. * That is, if SecTrustEvaluate comes back with problems, you could invoke the completionHandler with NO, * and then ask the client if the cert can be trusted. This is similar to how most browsers act. * * Generally, only one delegate should implement this method. * However, if multiple delegates implement this method, then the first to invoke the completionHandler "wins". * And subsequent invocations of the completionHandler are ignored. **/ - (void)xmppStream:(XMPPStream *)sender didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler; /** * This method is called after the stream has been secured via SSL/TLS. * This method may be called if the server required a secure connection during the opening process, * or if the secureConnection: method was manually invoked. **/ - (void)xmppStreamDidSecure:(XMPPStream *)sender; /** * This method is called after the XML stream has been fully opened. * More precisely, this method is called after an opening and tag have been sent and received, * and after the stream features have been received, and any required features have been fullfilled. * At this point it's safe to begin communication with the server. **/ - (void)xmppStreamDidConnect:(XMPPStream *)sender; /** * This method is called after registration of a new user has successfully finished. * If registration fails for some reason, the xmppStream:didNotRegister: method will be called instead. **/ - (void)xmppStreamDidRegister:(XMPPStream *)sender; /** * This method is called if registration fails. **/ - (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error; /** * This method is called after authentication has successfully finished. * If authentication fails for some reason, the xmppStream:didNotAuthenticate: method will be called instead. **/ - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender; /** * This method is called if authentication fails. **/ - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error; /** * Binding a JID resource is a standard part of the authentication process, * and occurs after SASL authentication completes (which generally authenticates the JID username). * * This delegate method allows for a custom binding procedure to be used. * For example: * - a custom SASL authentication scheme might combine auth with binding * - stream management (xep-0198) replaces binding if it can resume a previous session * * Return nil (or don't implement this method) if you wish to use the standard binding procedure. **/ - (id )xmppStreamWillBind:(XMPPStream *)sender; /** * This method is called if the XMPP server doesn't allow our resource of choice * because it conflicts with an existing resource. * * Return an alternative resource or return nil to let the server automatically pick a resource for us. **/ - (NSString *)xmppStream:(XMPPStream *)sender alternativeResourceForConflictingResource:(NSString *)conflictingResource; /** * These methods are called before their respective XML elements are broadcast as received to the rest of the stack. * These methods can be used to modify elements on the fly. * (E.g. perform custom decryption so the rest of the stack sees readable text.) * * You may also filter incoming elements by returning nil. * * When implementing these methods to modify the element, you do not need to copy the given element. * You can simply edit the given element, and return it. * The reason these methods return an element, instead of void, is to allow filtering. * * Concerning thread-safety, delegates implementing the method are invoked one-at-a-time to * allow thread-safe modification of the given elements. * * You should NOT implement these methods unless you have good reason to do so. * For general processing and notification of received elements, please use xmppStream:didReceiveX: methods. * * @see xmppStream:didReceiveIQ: * @see xmppStream:didReceiveMessage: * @see xmppStream:didReceivePresence: **/ - (XMPPIQ *)xmppStream:(XMPPStream *)sender willReceiveIQ:(XMPPIQ *)iq; - (XMPPMessage *)xmppStream:(XMPPStream *)sender willReceiveMessage:(XMPPMessage *)message; - (XMPPPresence *)xmppStream:(XMPPStream *)sender willReceivePresence:(XMPPPresence *)presence; /** * This method is called if any of the xmppStream:willReceiveX: methods filter the incoming stanza. * * It may be useful for some extensions to know that something was received, * even if it was filtered for some reason. **/ - (void)xmppStreamDidFilterStanza:(XMPPStream *)sender; /** * These methods are called after their respective XML elements are received on the stream. * * In the case of an IQ, the delegate method should return YES if it has or will respond to the given IQ. * If the IQ is of type 'get' or 'set', and no delegates respond to the IQ, * then xmpp stream will automatically send an error response. * * Concerning thread-safety, delegates shouldn't modify the given elements. * As documented in NSXML / KissXML, elements are read-access thread-safe, but write-access thread-unsafe. * If you have need to modify an element for any reason, * you should copy the element first, and then modify and use the copy. **/ - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq; - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message; - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence; /** * This method is called if an XMPP error is received. * In other words, a . * * However, this method may also be called for any unrecognized xml stanzas. * * Note that standard errors ( for example) are delivered normally, * via the other didReceive...: methods. **/ - (void)xmppStream:(XMPPStream *)sender didReceiveError:(NSXMLElement *)error; /** * These methods are called before their respective XML elements are sent over the stream. * These methods can be used to modify outgoing elements on the fly. * (E.g. add standard information for custom protocols.) * * You may also filter outgoing elements by returning nil. * * When implementing these methods to modify the element, you do not need to copy the given element. * You can simply edit the given element, and return it. * The reason these methods return an element, instead of void, is to allow filtering. * * Concerning thread-safety, delegates implementing the method are invoked one-at-a-time to * allow thread-safe modification of the given elements. * * You should NOT implement these methods unless you have good reason to do so. * For general processing and notification of sent elements, please use xmppStream:didSendX: methods. * * @see xmppStream:didSendIQ: * @see xmppStream:didSendMessage: * @see xmppStream:didSendPresence: **/ - (XMPPIQ *)xmppStream:(XMPPStream *)sender willSendIQ:(XMPPIQ *)iq; - (XMPPMessage *)xmppStream:(XMPPStream *)sender willSendMessage:(XMPPMessage *)message; - (XMPPPresence *)xmppStream:(XMPPStream *)sender willSendPresence:(XMPPPresence *)presence; /** * These methods are called after their respective XML elements are sent over the stream. * These methods may be used to listen for certain events (such as an unavailable presence having been sent), * or for general logging purposes. (E.g. a central history logging mechanism). **/ - (void)xmppStream:(XMPPStream *)sender didSendIQ:(XMPPIQ *)iq; - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message; - (void)xmppStream:(XMPPStream *)sender didSendPresence:(XMPPPresence *)presence; /** * These methods are called after failing to send the respective XML elements over the stream. * This occurs when the stream gets disconnected before the element can get sent out. **/ - (void)xmppStream:(XMPPStream *)sender didFailToSendIQ:(XMPPIQ *)iq error:(NSError *)error; - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error; - (void)xmppStream:(XMPPStream *)sender didFailToSendPresence:(XMPPPresence *)presence error:(NSError *)error; /** * This method is called if the XMPP Stream's jid changes. **/ - (void)xmppStreamDidChangeMyJID:(XMPPStream *)xmppStream; /** * This method is called if the disconnect method is called. * It may be used to determine if a disconnection was purposeful, or due to an error. * * Note: A disconnect may be either "clean" or "dirty". * A "clean" disconnect is when the stream sends the closing stanza before disconnecting. * A "dirty" disconnect is when the stream simply closes its TCP socket. * In most cases it makes no difference how the disconnect occurs, * but there are a few contexts in which the difference has various protocol implications. * * @see xmppStreamDidSendClosingStreamStanza **/ - (void)xmppStreamWasToldToDisconnect:(XMPPStream *)sender; /** * This method is called after the stream has sent the closing stanza. * This signifies a "clean" disconnect. * * Note: A disconnect may be either "clean" or "dirty". * A "clean" disconnect is when the stream sends the closing stanza before disconnecting. * A "dirty" disconnect is when the stream simply closes its TCP socket. * In most cases it makes no difference how the disconnect occurs, * but there are a few contexts in which the difference has various protocol implications. **/ - (void)xmppStreamDidSendClosingStreamStanza:(XMPPStream *)sender; /** * This method is called if the XMPP stream's connect times out. **/ - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender; /** * This method is called after the stream is closed. * * The given error parameter will be non-nil if the error was due to something outside the general xmpp realm. * Some examples: * - The TCP socket was unexpectedly disconnected. * - The SRV resolution of the domain failed. * - Error parsing xml sent from server. * * @see xmppStreamConnectDidTimeout: **/ - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error; /** * This method is only used in P2P mode when the connectTo:withAddress: method was used. * * It allows the delegate to read the element if/when they arrive. * Recall that the XEP specifies that SHOULD be sent. **/ - (void)xmppStream:(XMPPStream *)sender didReceiveP2PFeatures:(NSXMLElement *)streamFeatures; /** * This method is only used in P2P mode when the connectTo:withSocket: method was used. * * It allows the delegate to customize the element, * adding any specific featues the delegate might support. **/ - (void)xmppStream:(XMPPStream *)sender willSendP2PFeatures:(NSXMLElement *)streamFeatures; /** * These methods are called as xmpp modules are registered and unregistered with the stream. * This generally corresponds to xmpp modules being initailzed and deallocated. * * The methods may be useful, for example, if a more precise auto delegation mechanism is needed * than what is available with the autoAddDelegate:toModulesOfClass: method. **/ - (void)xmppStream:(XMPPStream *)sender didRegisterModule:(id)module; - (void)xmppStream:(XMPPStream *)sender willUnregisterModule:(id)module; /** * Custom elements are Non-XMPP elements. * In other words, not , or elements. * * Typically these kinds of elements are not allowed by the XMPP server. * But some custom implementations may use them. * The standard example is XEP-0198, which uses & elements. * * If you're using custom elements, you must register the custom element name(s). * Otherwise the xmppStream will treat non-XMPP elements as errors (xmppStream:didReceiveError:). * * @see registerCustomElementNames (in XMPPInternal.h) **/ - (void)xmppStream:(XMPPStream *)sender didSendCustomElement:(NSXMLElement *)element; - (void)xmppStream:(XMPPStream *)sender didReceiveCustomElement:(NSXMLElement *)element; @end