Index: .hgignore ================================================================== --- .hgignore +++ .hgignore @@ -10,12 +10,13 @@ buildsys.mk config.h.in config.log config.status configure +DerivedData docs extra.mk ObjXMPP.xcodeproj/*.mode1v3 ObjXMPP.xcodeproj/*.pbxuser ObjXMPP.xcodeproj/project.xcworkspace ObjXMPP.xcodeproj/xcuserdata tests/tests Index: ObjXMPP.xcodeproj/project.pbxproj ================================================================== --- ObjXMPP.xcodeproj/project.pbxproj +++ ObjXMPP.xcodeproj/project.pbxproj @@ -14,10 +14,12 @@ 4B19F57A14D17082005D52DC /* XMPPCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19F57614D17081005D52DC /* XMPPCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B19F57B14D17082005D52DC /* XMPPCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B19F57714D17081005D52DC /* XMPPCallback.m */; }; 4B19F57C14D17082005D52DC /* XMPPEXTERNALAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19F57814D17081005D52DC /* XMPPEXTERNALAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B19F57D14D17082005D52DC /* XMPPEXTERNALAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B19F57914D17081005D52DC /* XMPPEXTERNALAuth.m */; }; 4B19F5A314D1779E005D52DC /* ObjOpenSSL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B19F5A214D1779E005D52DC /* ObjOpenSSL.framework */; }; + 4B19F5D114D20B01005D52DC /* XMPPMulticastDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B19F5CF14D20B01005D52DC /* XMPPMulticastDelegate.h */; }; + 4B19F5D214D20B01005D52DC /* XMPPMulticastDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B19F5D014D20B01005D52DC /* XMPPMulticastDelegate.m */; }; 4B4844F5138BBC7500EB48A5 /* XMPPSRVLookup.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B4844F3138BBC7400EB48A5 /* XMPPSRVLookup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B4844F6138BBC7500EB48A5 /* XMPPSRVLookup.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B4844F4138BBC7500EB48A5 /* XMPPSRVLookup.m */; }; 4B484501138BBEEB00EB48A5 /* libresolv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B484500138BBEEB00EB48A5 /* libresolv.dylib */; }; 4BC559D71337ABD300E345C7 /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC559D61337ABD300E345C7 /* ObjFW.framework */; }; 4BC559EC1337AC0900E345C7 /* XMPPAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC559DA1337AC0900E345C7 /* XMPPAuthenticator.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -66,10 +68,12 @@ 4B19F57614D17081005D52DC /* XMPPCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPCallback.h; path = src/XMPPCallback.h; sourceTree = SOURCE_ROOT; }; 4B19F57714D17081005D52DC /* XMPPCallback.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPCallback.m; path = src/XMPPCallback.m; sourceTree = SOURCE_ROOT; }; 4B19F57814D17081005D52DC /* XMPPEXTERNALAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPEXTERNALAuth.h; path = src/XMPPEXTERNALAuth.h; sourceTree = SOURCE_ROOT; }; 4B19F57914D17081005D52DC /* XMPPEXTERNALAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPEXTERNALAuth.m; path = src/XMPPEXTERNALAuth.m; sourceTree = SOURCE_ROOT; }; 4B19F5A214D1779E005D52DC /* ObjOpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjOpenSSL.framework; path = /Library/Frameworks/ObjOpenSSL.framework; sourceTree = ""; }; + 4B19F5CF14D20B01005D52DC /* XMPPMulticastDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPMulticastDelegate.h; path = src/XMPPMulticastDelegate.h; sourceTree = SOURCE_ROOT; }; + 4B19F5D014D20B01005D52DC /* XMPPMulticastDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPMulticastDelegate.m; path = src/XMPPMulticastDelegate.m; sourceTree = SOURCE_ROOT; }; 4B4844F3138BBC7400EB48A5 /* XMPPSRVLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPSRVLookup.h; path = src/XMPPSRVLookup.h; sourceTree = SOURCE_ROOT; }; 4B4844F4138BBC7500EB48A5 /* XMPPSRVLookup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPSRVLookup.m; path = src/XMPPSRVLookup.m; sourceTree = SOURCE_ROOT; }; 4B484500138BBEEB00EB48A5 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = usr/lib/libresolv.dylib; sourceTree = SDKROOT; }; 4BC559911337A65400E345C7 /* ObjXMPP.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ObjXMPP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4BC559D61337ABD300E345C7 /* ObjFW.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjFW.framework; path = ../../../../Library/Frameworks/ObjFW.framework; sourceTree = ""; }; @@ -186,10 +190,12 @@ 4BC559E11337AC0900E345C7 /* XMPPIQ.m */, 4BC559E21337AC0900E345C7 /* XMPPJID.h */, 4BC559E31337AC0900E345C7 /* XMPPJID.m */, 4BC559E41337AC0900E345C7 /* XMPPMessage.h */, 4BC559E51337AC0900E345C7 /* XMPPMessage.m */, + 4B19F5CF14D20B01005D52DC /* XMPPMulticastDelegate.h */, + 4B19F5D014D20B01005D52DC /* XMPPMulticastDelegate.m */, 4BC559E61337AC0900E345C7 /* XMPPPLAINAuth.h */, 4BC559E71337AC0900E345C7 /* XMPPPLAINAuth.m */, 4BC559E81337AC0900E345C7 /* XMPPPresence.h */, 4BC559E91337AC0900E345C7 /* XMPPPresence.m */, 4BDEF8051340B240000156D1 /* XMPPRoster.h */, @@ -237,10 +243,11 @@ 4BD9BF59134003F700DAB43A /* XMPPRosterItem.h in Headers */, 4BC559FC1337AC0900E345C7 /* XMPPSCRAMAuth.h in Headers */, 4B4844F5138BBC7500EB48A5 /* XMPPSRVLookup.h in Headers */, 4BC55A011337AC1800E345C7 /* XMPPStanza.h in Headers */, 4B01D020137C7E7D005624EA /* namespaces.h in Headers */, + 4B19F5D114D20B01005D52DC /* XMPPMulticastDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ @@ -342,10 +349,11 @@ 4BC55A001337AC1800E345C7 /* XMPPSCRAMAuth.m in Sources */, 4BC55A021337AC1800E345C7 /* XMPPStanza.m in Sources */, 4BD9BF5A134003F700DAB43A /* XMPPRosterItem.m in Sources */, 4BDEF8081340B240000156D1 /* XMPPRoster.m in Sources */, 4B4844F6138BBC7500EB48A5 /* XMPPSRVLookup.m in Sources */, + 4B19F5D214D20B01005D52DC /* XMPPMulticastDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -11,10 +11,11 @@ XMPPExceptions.m \ XMPPEXTERNALAuth.m \ XMPPIQ.m \ XMPPJID.m \ XMPPMessage.m \ + XMPPMulticastDelegate.m \ XMPPPLAINAuth.m \ XMPPPresence.m \ XMPPRoster.m \ XMPPRosterItem.m \ XMPPSCRAMAuth.m \ Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010, 2011, Jonathan Schleifer + * Copyright (c) 2010, 2011, 2012, Jonathan Schleifer * Copyright (c) 2011, Florian Zeitz * * https://webkeks.org/hg/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any @@ -30,10 +30,11 @@ @class XMPPPresence; @class XMPPAuthenticator; @class XMPPRoster; @class XMPPRosterItem; @class SSLSocket; +@class XMPPMulticastDelegate; @protocol XMPPConnectionDelegate #ifndef XMPP_CONNECTION_M #endif @@ -75,11 +76,11 @@ OFString *username, *password, *server, *resource; OFString *privateKeyFile, *certificateFile; OFString *domain, *domainToASCII; XMPPJID *JID; uint16_t port; - id delegate; + XMPPMulticastDelegate *delegates; OFMutableDictionary *callbacks; XMPPAuthenticator *authModule; BOOL streamOpen; BOOL needsSession; BOOL encryptionRequired, encrypted; @@ -90,11 +91,10 @@ #ifdef OF_HAVE_PROPERTIES @property (copy) OFString *username, *password, *server, *domain, *resource; @property (copy) OFString *privateKeyFile, *certificateFile; @property (copy, readonly) XMPPJID *JID; @property (assign) uint16_t port; -@property (assign) id delegate; @property (readonly, retain) XMPPRoster *roster; @property (readonly, retain, getter=socket) OFTCPSocket *sock; @property (assign) BOOL encryptionRequired; @property (readonly) BOOL encrypted; @property (readonly) BOOL streamOpen; @@ -103,10 +103,24 @@ /** * \return A new autoreleased XMPPConnection */ + connection; +/** + * Adds the specified delegate. + * + * \param delegate The delegate to add + */ +- (void)addDelegate: (id )delegate; + +/** + * Removes the specified delegate. + * + * \param delegate The delegate to remove + */ +- (void)removeDelegate: (id )delegate; + /** * Connects to the XMPP service. */ - (void)connect; @@ -172,22 +186,22 @@ * * \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; +- (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; +- (void)sendIQ: (XMPPIQ*)iq + withCallbackBlock: (void(^)(XMPPIQ*))block; #endif /** * Generates a new, unique stanza ID. * @@ -206,12 +220,10 @@ - (void)setResource: (OFString*)resource; - (OFString*)resource; - (XMPPJID*)JID; - (void)setPort: (uint16_t)port; - (uint16_t)port; -- (void)setDelegate: (id )delegate; -- (id )delegate; - (XMPPRoster*)roster; - (void)XMPP_startStream; - (void)XMPP_handleStream: (OFXMLElement*)element; - (void)XMPP_handleTLS: (OFXMLElement*)element; @@ -227,9 +239,10 @@ - (void)XMPP_handleFeatures: (OFXMLElement*)element; - (void)XMPP_handleResourceBind: (XMPPIQ*)iq; - (void)XMPP_sendSession; - (void)XMPP_handleSession: (XMPPIQ*)iq; - (OFString*)XMPP_IDNAToASCII: (OFString*)domain; +- (XMPPMulticastDelegate*)XMPP_delegates; @end @interface OFObject (XMPPConnectionDelegate) @end Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010, 2011, Jonathan Schleifer + * Copyright (c) 2010, 2011, 2012, Jonathan Schleifer * Copyright (c) 2011, Florian Zeitz * * https://webkeks.org/hg/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any @@ -47,10 +47,11 @@ #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" #import "XMPPRoster.h" #import "XMPPRosterItem.h" +#import "XMPPMulticastDelegate.h" #import "XMPPExceptions.h" #import "namespaces.h" @implementation XMPPConnection + connection @@ -65,10 +66,11 @@ @try { sock = [[OFTCPSocket alloc] init]; port = 5222; encrypted = NO; streamOpen = NO; + delegates = [[XMPPMulticastDelegate alloc] init]; callbacks = [[OFMutableDictionary alloc] init]; } @catch (id e) { [self release]; @throw e; } @@ -87,10 +89,11 @@ [certificateFile release]; [server release]; [domain release]; [resource release]; [JID release]; + [delegates release]; [callbacks release]; [authModule release]; [roster release]; [super dealloc]; @@ -302,13 +305,13 @@ } - (void)parseBuffer: (const char*)buffer withLength: (size_t)length { - if (length < 1 && [delegate respondsToSelector: - @selector(connectionWasClosed:)]) { - [delegate connectionWasClosed: self]; + if (length < 1) { + [delegates broadcastSelector: @selector(connectionWasClosed:) + forConnection: self]; return; } [parser parseBuffer: buffer withLength: length]; @@ -373,53 +376,52 @@ reason: @"No matching identifier"]; } - (void)sendStanza: (OFXMLElement*)element { - if ([delegate respondsToSelector: - @selector(connection:didSendElement:)]) - [delegate connection: self - didSendElement: element]; + [delegates broadcastSelector: @selector(connection:didSendElement:) + forConnection: self + withObject: 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: [XMPPCallback - callbackWithCallbackObject: object - selector: selector] - forKey: [iq ID]]; - } @finally { - [pool release]; - } + OFAutoreleasePool *pool; + XMPPCallback *callback; + + if (![iq ID]) + [iq setID: [self generateStanzaID]]; + + pool = [[OFAutoreleasePool alloc] init]; + callback = [XMPPCallback callbackWithCallbackObject: object + selector: selector]; + [callbacks setObject: callback + forKey: [iq ID]]; + [pool release]; [self sendStanza: iq]; } #ifdef OF_HAVE_BLOCKS - (void)sendIQ: (XMPPIQ*)iq - withCallbackBlock: (xmpp_callback_block_t)callback; -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - @try { - if (![iq ID]) - [iq setID: [self generateStanzaID]]; - - [callbacks setObject: [XMPPCallback - callbackWithCallbackBlock: callback] - forKey: [iq ID]]; - } @finally { - [pool release]; - } + withCallbackBlock: (xmpp_callback_block_t)block; +{ + OFAutoreleasePool *pool; + XMPPCallback *callback; + + if (![iq ID]) + [iq setID: [self generateStanzaID]]; + + pool = [[OFAutoreleasePool alloc] init]; + callback = [XMPPCallback callbackWithCallbackBlock: block]; + [callbacks setObject: callback + forKey: [iq ID]]; + [pool release]; [self sendStanza: iq]; } #endif @@ -484,14 +486,13 @@ [element setDefaultNamespace: XMPP_NS_CLIENT]; [element setPrefix: @"stream" forNamespace: XMPP_NS_STREAM]; - if ([delegate respondsToSelector: - @selector(connection:didReceiveElement:)]) - [delegate connection: self - didReceiveElement: element]; + [delegates broadcastSelector: @selector(connection:didReceiveElement:) + forConnection: self + withObject: element]; if ([[element namespace] isEqual: XMPP_NS_CLIENT]) [self XMPP_handleStanza: element]; if ([[element namespace] isEqual: XMPP_NS_STREAM]) @@ -690,25 +691,25 @@ { if ([[element name] isEqual: @"proceed"]) { /* FIXME: Catch errors here */ SSLSocket *newSock; - if ([delegate respondsToSelector: - @selector(connectionWillUpgradeToTLS:)]) - [delegate connectionWillUpgradeToTLS: self]; + [delegates broadcastSelector: @selector( + connectionWillUpgradeToTLS:) + forConnection: self]; newSock = [[SSLSocket alloc] initWithSocket: sock privateKeyFile: privateKeyFile certificateFile: certificateFile]; [sock release]; sock = newSock; encrypted = YES; - if ([delegate respondsToSelector: - @selector(connectionDidUpgradeToTLS:)]) - [delegate connectionDidUpgradeToTLS: self]; + [delegates broadcastSelector: @selector( + connectionDidUpgradeToTLS:) + forConnection: self]; /* Stream restart */ [self XMPP_startStream]; return; @@ -746,13 +747,13 @@ if ([[element name] isEqual: @"success"]) { [authModule continueWithData: [OFDataArray dataArrayWithBase64EncodedString: [element stringValue]]]; - if ([delegate respondsToSelector: - @selector(connectionWasAuthenticated:)]) - [delegate connectionWasAuthenticated: self]; + [delegates broadcastSelector: @selector( + connectionWWasAuthenticated:) + forConnection: self]; /* Stream restart */ [self XMPP_startStream]; return; @@ -784,13 +785,14 @@ if ([iq elementForName: @"query" namespace: XMPP_NS_ROSTER]) if ([roster handleIQ: iq]) return; - if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) - handled = [delegate connection: self - didReceiveIQ: iq]; + handled = [delegates broadcastSelector: @selector( + connection:didReceiveIQ:) + forConnection: self + withObject: iq]; if (!handled && ![[iq type] isEqual: @"error"] && ![[iq type] isEqual: @"result"]) { [self sendStanza: [iq errorIQWithType: @"cancel" condition: @"service-unavailable"]]; @@ -797,22 +799,20 @@ } } - (void)XMPP_handleMessage: (XMPPMessage*)message { - if ([delegate respondsToSelector: - @selector(connection:didReceiveMessage:)]) - [delegate connection: self - didReceiveMessage: message]; + [delegates broadcastSelector: @selector(connection:didReceiveMessage:) + forConnection: self + withObject: message]; } - (void)XMPP_handlePresence: (XMPPPresence*)presence { - if ([delegate respondsToSelector: - @selector(connection:didReceivePresence:)]) - [delegate connection: self - didReceivePresence: presence]; + [delegates broadcastSelector: @selector(connection:didReceivePresence:) + forConnection: self + withObject: presence]; } - (void)XMPP_handleFeatures: (OFXMLElement*)element { OFXMLElement *starttls = [element elementForName: @"starttls" @@ -977,13 +977,13 @@ if (needsSession) { [self XMPP_sendSession]; return; } - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; + [delegates broadcastSelector: @selector(connection:wasBoundToJID:) + forConnection: self + withObject: JID]; } - (void)XMPP_sendSession { XMPPIQ *iq; @@ -1000,13 +1000,13 @@ - (void)XMPP_handleSession: (XMPPIQ*)iq { if (![[iq type] isEqual: @"result"]) assert(0); - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; + [delegates broadcastSelector: @selector(connection:wasBoundToJID:) + forConnection: self + withObject: JID]; } - (OFString*)XMPP_IDNAToASCII: (OFString*)domain_ { OFString *ret; @@ -1043,18 +1043,23 @@ - (uint16_t)port { return port; } -- (void)setDelegate: (id )delegate_ +- (void)addDelegate: (id )delegate +{ + [delegates addDelegate: delegate]; +} + +- (void)removeDelegate: (id )delegate { - delegate = (id )delegate_; + [delegates removeDelegate: delegate]; } -- (id )delegate +- (XMPPMulticastDelegate*)XMPP_delegates { - return delegate; + return delegates; } - (XMPPRoster*)roster { return [[roster retain] autorelease]; ADDED src/XMPPMulticastDelegate.h Index: src/XMPPMulticastDelegate.h ================================================================== --- src/XMPPMulticastDelegate.h +++ src/XMPPMulticastDelegate.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Jonathan Schleifer + * + * 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 "XMPPConnection.h" + +@class OFDataArray; + +@interface XMPPMulticastDelegate: OFObject +{ + OFDataArray *delegates; +} + +- (void)addDelegate: (id )delegate; +- (void)removeDelegate: (id )delegate; +- (BOOL)broadcastSelector: (SEL)selector + forConnection: (XMPPConnection*)connection; +- (BOOL)broadcastSelector: (SEL)selector + forConnection: (XMPPConnection*)connection + withObject: (id)object; +@end ADDED src/XMPPMulticastDelegate.m Index: src/XMPPMulticastDelegate.m ================================================================== --- src/XMPPMulticastDelegate.m +++ src/XMPPMulticastDelegate.m @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012, Jonathan Schleifer + * + * 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 + +#import "XMPPMulticastDelegate.h" + +@implementation XMPPMulticastDelegate +- init +{ + self = [super init]; + + @try { + delegates = [[OFDataArray alloc] initWithItemSize: sizeof(id)]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [delegates release]; + + [super dealloc]; +} + +- (void)addDelegate: (id )delegate +{ + [delegates addItem: &delegate]; +} + +- (void)removeDelegate: (id)delegate +{ + id *cArray = [delegates cArray]; + size_t i, count = [delegates count]; + + for (i = 0; i < count; i++) { + if (cArray[i] == delegate) { + [delegates removeItemAtIndex: i]; + return; + } + } +} + +- (BOOL)broadcastSelector: (SEL)selector + forConnection: (XMPPConnection*)connection +{ + id *cArray = [delegates cArray]; + size_t i, count = [delegates count]; + BOOL handled = NO; + + for (i = 0; i < count; i++) { + if (![cArray[i] respondsToSelector: selector]) + continue; + + BOOL (*imp)(id, SEL, id) = (BOOL(*)(id, SEL, id)) + [cArray[i] methodForSelector: selector]; + + handled |= imp(cArray[i], selector, connection); + } + + return handled; +} + +- (BOOL)broadcastSelector: (SEL)selector + forConnection: (XMPPConnection*)connection + withObject: (id)object +{ + id *cArray = [delegates cArray]; + size_t i, count = [delegates count]; + BOOL handled = NO; + + for (i = 0; i < count; i++) { + if (![cArray[i] respondsToSelector: selector]) + continue; + + BOOL (*imp)(id, SEL, id, id) = (BOOL(*)(id, SEL, id, id)) + [cArray[i] methodForSelector: selector]; + + handled |= imp(cArray[i], selector, connection, object); + } + + return handled; +} +@end Index: src/XMPPRoster.m ================================================================== --- src/XMPPRoster.m +++ src/XMPPRoster.m @@ -29,10 +29,11 @@ #import "XMPPRoster.h" #import "XMPPRosterItem.h" #import "XMPPConnection.h" #import "XMPPIQ.h" #import "XMPPJID.h" +#import "XMPPMulticastDelegate.h" #import "namespaces.h" @implementation XMPPRoster - initWithConnection: (XMPPConnection*)conn { @@ -155,23 +156,26 @@ if ([subscription isEqual: @"remove"]) [self XMPP_deleteRosterItem: rosterItem]; else [self XMPP_addRosterItem: rosterItem]; - if (isPush && [[connection delegate] respondsToSelector: - @selector(connection:didReceiveRosterItem:)]) - [[connection delegate] connection: connection - didReceiveRosterItem: rosterItem]; + if (isPush) { + SEL sel = @selector(connection:didReceiveRosterItem:); + + [[connection XMPP_delegates] + broadcastSelector: sel + forConnection: connection + withObject: rosterItem]; + } } if (isPush) { [connection sendStanza: [iq resultIQ]]; } else { - if ([[connection delegate] respondsToSelector: - @selector(connectionDidReceiveRoster:)]) - [[connection delegate] - connectionDidReceiveRoster: connection]; + [[connection XMPP_delegates] + broadcastSelector: @selector(connectionDidReceiveRoster:) + forConnection: connection]; [rosterID release]; rosterID = nil; } Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -89,11 +89,11 @@ assert(([[OFString stringWithFormat: @"%@, %@, %@, %@", [[stanza from] fullJID], [[stanza to] fullJID], [stanza type], [stanza ID]] isEqual: @"bob@localhost, alice@localhost, get, 42"])); conn = [[XMPPConnection alloc] init]; - [conn setDelegate: self]; + [conn addDelegate: self]; if ([arguments count] != 3) { of_log(@"Invalid count of command line arguments!"); [OFApplication terminateWithStatus: 1]; }