Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -4,10 +4,11 @@ LIB_MAJOR = 0 LIB_MINOR = 0 STATIC_LIB = ${OBJXMPP_STATIC_LIB} SRCS = XMPPAuthenticator.m \ + XMPPCallback.m \ XMPPConnection.m \ XMPPExceptions.m \ XMPPIQ.m \ XMPPJID.m \ XMPPMessage.m \ ADDED src/XMPPCallback.h Index: src/XMPPCallback.h ================================================================== --- src/XMPPCallback.h +++ src/XMPPCallback.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011, Florian Zeitz + * + * https://webkeks.org/hg/objxmpp/ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class XMPPIQ; + +@protocol XMPPCallback +- (void)runWithIQ: (XMPPIQ*)iq; +@end + +#ifdef OF_HAVE_BLOCKS +typedef void(^xmpp_callback_block)(XMPPIQ*); + +@interface XMPPBlockCallback: OFObject +{ + xmpp_callback_block callback; +} ++ callbackWithCallbackBlock: (xmpp_callback_block)callback; +- initWithCallbackBlock: (xmpp_callback_block)callback; +@end +#endif + +@interface XMPPObjectCallback: OFObject +{ + id object; + SEL selector; +} ++ callbackWithCallbackObject: (id)object + selector: (SEL)selector; +- initWithCallbackObject: (id)object + selector: (SEL)selector; +@end ADDED src/XMPPCallback.m Index: src/XMPPCallback.m ================================================================== --- src/XMPPCallback.m +++ src/XMPPCallback.m @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011, Florian Zeitz + * + * https://webkeks.org/hg/objxmpp/ + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#import "XMPPCallback.h" + +#ifdef OF_HAVE_BLOCKS +@implementation XMPPBlockCallback ++ callbackWithCallbackBlock: (xmpp_callback_block)callback_ +{ + return [[[self alloc] initWithCallbackBlock: callback_] autorelease]; +} + +- initWithCallbackBlock: (xmpp_callback_block)callback_ +{ + self = [super init]; + + callback = [callback_ copy]; + + return self; +} + +- (void)dealloc +{ + [callback release]; + + [super dealloc]; +} + +- (void)runWithIQ: (XMPPIQ*)iq +{ + callback(iq); +} +@end +#endif + +@implementation XMPPObjectCallback ++ callbackWithCallbackObject: (id)object_ + selector: (SEL)selector_ +{ + return [[[self alloc] initWithCallbackObject: object_ + selector: selector_] autorelease]; +} + +- initWithCallbackObject: (id)object_ + selector: (SEL)selector_ +{ + self = [super init]; + + // TODO: Retain or follow delegate paradigm? + object = [object_ retain]; + selector = selector_; + + return self; +} + +- (void)dealloc +{ + [object release]; + + [super dealloc]; +} + +- (void)runWithIQ: (XMPPIQ*)iq +{ + [object performSelector: selector + withObject: iq]; +} +@end Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -75,15 +75,15 @@ OFString *username, *password, *server, *resource; OFString *domain, *domainToASCII; XMPPJID *JID; uint16_t port; id delegate; + OFMutableDictionary *callbacks; XMPPAuthenticator *authModule; BOOL needsSession; BOOL encryptionRequired, encrypted; unsigned int lastID; - OFString *bindID, *sessionID; XMPPRoster *roster; } #ifdef OF_HAVE_PROPERTIES @property (copy) OFString *username, *password, *server, *domain, *resource; @@ -156,10 +156,31 @@ * * \param element The element to send */ - (void)sendStanza: (OFXMLElement*)element; +/** + * Sends an XMPPIQ, registering a callback method + * + * \param object The object that contains the callback method + * \param selector The selector of the callback method, + * must take exactly one parameter of type XMPPIQ* + */ +- (void)sendIQ: (XMPPIQ*)iq +withCallbackObject: (id)object + selector: (SEL)selector; + +#ifdef OF_HAVE_BLOCKS +/** + * Sends an XMPPIQ, registering a callback block + * + * \param callback The callback block + */ +- (void)sendIQ: (XMPPIQ*)iq +withCallbackBlock: (void(^)(XMPPIQ*))callback; +#endif + /** * Generates a new, unique stanza ID. * * \return A new, generated, unique stanza ID. */ Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -35,10 +35,11 @@ #import #import #import #import "XMPPConnection.h" +#import "XMPPCallback.h" #import "XMPPSRVLookup.h" #import "XMPPSCRAMAuth.h" #import "XMPPPLAINAuth.h" #import "XMPPStanza.h" #import "XMPPJID.h" @@ -62,10 +63,11 @@ @try { sock = [[OFTCPSocket alloc] init]; port = 5222; encrypted = NO; + callbacks = [[OFMutableDictionary alloc] init]; } @catch (id e) { [self release]; @throw e; } @@ -81,13 +83,12 @@ [password release]; [server release]; [domain release]; [resource release]; [JID release]; + [callbacks release]; [authModule release]; - [bindID release]; - [sessionID release]; [roster release]; [super dealloc]; } @@ -349,10 +350,50 @@ [delegate connection: self didSendElement: element]; [sock writeString: [element XMLString]]; } + +- (void)sendIQ: (XMPPIQ*)iq +withCallbackObject: (id)object + selector: (SEL)selector +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + @try { + if (![iq ID]) + [iq setID: [self generateStanzaID]]; + + [callbacks setObject: [XMPPObjectCallback + callbackWithCallbackObject: object + selector: selector] + forKey: [iq ID]]; + } @finally { + [pool release]; + } + + [self sendStanza: iq]; +} + +#ifdef OF_HAVE_BLOCKS +- (void)sendIQ: (XMPPIQ*)iq +withCallbackBlock: (xmpp_callback_block)callback; +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + @try { + if (![iq ID]) + [iq setID: [self generateStanzaID]]; + + [callbacks setObject: [XMPPBlockCallback + callbackWithCallbackBlock: callback] + forKey: [iq ID]]; + } @finally { + [pool release]; + } + + [self sendStanza: iq]; +} +#endif - (OFString*)generateStanzaID { return [OFString stringWithFormat: @"objxmpp_%u", lastID++]; } @@ -654,18 +695,15 @@ } - (void)XMPP_handleIQ: (XMPPIQ*)iq { BOOL handled = NO; - - if ([[iq ID] isEqual: bindID]) { - [self XMPP_handleResourceBind: iq]; - return; - } - - if ([[iq ID] isEqual: sessionID]) { - [self XMPP_handleSession: iq]; + id callback; + + if ((callback = [callbacks objectForKey: [iq ID]])) { + [callback runWithIQ: iq]; + [callbacks removeObjectForKey: [iq ID]]; return; } if ([iq elementForName: @"query" namespace: XMPP_NS_ROSTER]) @@ -797,13 +835,12 @@ - (void)XMPP_sendResourceBind { XMPPIQ *iq; OFXMLElement *bind; - bindID = [[self generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"set" - ID: bindID]; + ID: [self generateStanzaID]]; bind = [OFXMLElement elementWithName: @"bind" namespace: XMPP_NS_BIND]; if (resource != nil) @@ -811,11 +848,13 @@ namespace: XMPP_NS_BIND stringValue: resource]]; [iq addChild: bind]; - [self sendStanza: iq]; + [self sendIQ: iq + withCallbackObject: self + selector: @selector(XMPP_handleResourceBind:)]; } - (void)XMPP_handleResourceBind: (XMPPIQ*)iq { OFXMLElement *bindElement; @@ -830,13 +869,10 @@ jidElement = [bindElement elementForName: @"jid" namespace: XMPP_NS_BIND]; JID = [[XMPPJID alloc] initWithString: [jidElement stringValue]]; - [bindID release]; - bindID = nil; - if (needsSession) { [self XMPP_sendSession]; return; } @@ -847,16 +883,17 @@ - (void)XMPP_sendSession { XMPPIQ *iq; - sessionID = [[self generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"set" - ID: sessionID]; + ID: [self generateStanzaID]]; [iq addChild: [OFXMLElement elementWithName: @"session" namespace: XMPP_NS_SESSION]]; - [self sendStanza: iq]; + [self sendIQ: iq + withCallbackObject: self + selector: @selector(XMPP_handleSession:)]; } - (void)XMPP_handleSession: (XMPPIQ*)iq { if (![[iq type] isEqual: @"result"]) @@ -863,13 +900,10 @@ assert(0); if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) [delegate connection: self wasBoundToJID: JID]; - - [sessionID release]; - sessionID = nil; } - (OFString*)XMPP_IDNAToASCII: (OFString*)domain_ { OFString *ret;