Index: ObjXMPP.xcodeproj/project.pbxproj ================================================================== --- ObjXMPP.xcodeproj/project.pbxproj +++ ObjXMPP.xcodeproj/project.pbxproj @@ -33,10 +33,12 @@ 4BC55A001337AC1800E345C7 /* XMPPSCRAMAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC559FD1337AC1800E345C7 /* XMPPSCRAMAuth.m */; }; 4BC55A011337AC1800E345C7 /* XMPPStanza.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC559FE1337AC1800E345C7 /* XMPPStanza.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BC55A021337AC1800E345C7 /* XMPPStanza.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC559FF1337AC1800E345C7 /* XMPPStanza.m */; }; 4BD9BF59134003F700DAB43A /* XMPPRosterItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD9BF57134003F700DAB43A /* XMPPRosterItem.h */; }; 4BD9BF5A134003F700DAB43A /* XMPPRosterItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD9BF58134003F700DAB43A /* XMPPRosterItem.m */; }; + 4BDEF8071340B240000156D1 /* XMPPRoster.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDEF8051340B240000156D1 /* XMPPRoster.h */; }; + 4BDEF8081340B240000156D1 /* XMPPRoster.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDEF8061340B240000156D1 /* XMPPRoster.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 4B1295DE1337BD2D00154B25 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -77,10 +79,12 @@ 4BC559FE1337AC1800E345C7 /* XMPPStanza.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPStanza.h; path = src/XMPPStanza.h; sourceTree = SOURCE_ROOT; }; 4BC559FF1337AC1800E345C7 /* XMPPStanza.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPStanza.m; path = src/XMPPStanza.m; sourceTree = SOURCE_ROOT; }; 4BC55A051337ADA800E345C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 4BD9BF57134003F700DAB43A /* XMPPRosterItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPRosterItem.h; path = src/XMPPRosterItem.h; sourceTree = SOURCE_ROOT; }; 4BD9BF58134003F700DAB43A /* XMPPRosterItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPRosterItem.m; path = src/XMPPRosterItem.m; sourceTree = SOURCE_ROOT; }; + 4BDEF8051340B240000156D1 /* XMPPRoster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPRoster.h; path = src/XMPPRoster.h; sourceTree = SOURCE_ROOT; }; + 4BDEF8061340B240000156D1 /* XMPPRoster.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPRoster.m; path = src/XMPPRoster.m; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 4B1295DD1337BD2D00154B25 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -157,10 +161,12 @@ 4BC559E51337AC0900E345C7 /* XMPPMessage.m */, 4BC559E61337AC0900E345C7 /* XMPPPLAINAuth.h */, 4BC559E71337AC0900E345C7 /* XMPPPLAINAuth.m */, 4BC559E81337AC0900E345C7 /* XMPPPresence.h */, 4BC559E91337AC0900E345C7 /* XMPPPresence.m */, + 4BDEF8051340B240000156D1 /* XMPPRoster.h */, + 4BDEF8061340B240000156D1 /* XMPPRoster.m */, 4BD9BF57134003F700DAB43A /* XMPPRosterItem.h */, 4BD9BF58134003F700DAB43A /* XMPPRosterItem.m */, 4BC559EA1337AC0900E345C7 /* XMPPSCRAMAuth.h */, 4BC559FD1337AC1800E345C7 /* XMPPSCRAMAuth.m */, 4BC559FE1337AC1800E345C7 /* XMPPStanza.h */, @@ -194,10 +200,11 @@ 4BC559F81337AC0900E345C7 /* XMPPPLAINAuth.h in Headers */, 4BC559FA1337AC0900E345C7 /* XMPPPresence.h in Headers */, 4BC559FC1337AC0900E345C7 /* XMPPSCRAMAuth.h in Headers */, 4BC55A011337AC1800E345C7 /* XMPPStanza.h in Headers */, 4BD9BF59134003F700DAB43A /* XMPPRosterItem.h in Headers */, + 4BDEF8071340B240000156D1 /* XMPPRoster.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ @@ -293,10 +300,11 @@ 4BC559F91337AC0900E345C7 /* XMPPPLAINAuth.m in Sources */, 4BC559FB1337AC0900E345C7 /* XMPPPresence.m in Sources */, 4BC55A001337AC1800E345C7 /* XMPPSCRAMAuth.m in Sources */, 4BC55A021337AC1800E345C7 /* XMPPStanza.m in Sources */, 4BD9BF5A134003F700DAB43A /* XMPPRosterItem.m in Sources */, + 4BDEF8081340B240000156D1 /* XMPPRoster.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -27,10 +27,11 @@ @class XMPPJID; @class XMPPIQ; @class XMPPMessage; @class XMPPPresence; @class XMPPAuthenticator; +@class XMPPRoster; @protocol XMPPConnectionDelegate @optional - (void)connectionWasAuthenticated: (XMPPConnection*)conn; - (void)connection: (XMPPConnection*)conn @@ -62,19 +63,19 @@ id delegate; XMPPAuthenticator *authModule; BOOL needsSession; unsigned int lastID; OFString *bindID, *sessionID, *rosterID; - OFMutableDictionary *roster; + XMPPRoster *roster; } @property (copy) OFString *username, *password, *server, *resource; @property (copy, readonly) XMPPJID *JID; @property (assign) uint16_t port; @property (assign) BOOL useTLS; @property (retain) id delegate; -@property (copy, readonly) OFDictionary *roster; +@property (readonly, retain) XMPPRoster *roster; /** * Connects to the XMPP service. */ - (void)connect; Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -34,10 +34,11 @@ #import "XMPPStanza.h" #import "XMPPJID.h" #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" +#import "XMPPRoster.h" #import "XMPPRosterItem.h" #import "XMPPExceptions.h" #define NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind" #define NS_CLIENT @"jabber:client" @@ -79,11 +80,11 @@ useTLS = YES; parser.delegate = self; elementBuilder.delegate = self; - roster = [[OFMutableDictionary alloc] init]; + roster = [[XMPPRoster alloc] initWithConnection: self]; } @catch (id e) { [self release]; @throw e; } @@ -487,11 +488,11 @@ [self sendStanza: [OFXMLElement elementWithName: @"starttls" namespace: NS_STARTTLS]]; return; } - if ([mechs count] > 0) { + if (mechs.count > 0) { for (OFXMLElement *mech in [mechs.firstObject children]) [mechanisms addObject: [mech.children.firstObject stringValue]]; if ([mechanisms containsObject: @"SCRAM-SHA-1"]) { @@ -652,45 +653,25 @@ ![elem.ns isEqual: NS_ROSTER]) continue; rosterItem = [XMPPRosterItem rosterItem]; rosterItem.JID = [XMPPJID JIDWithString: - [rosterElem attributeForName: @"jid"].stringValue]; - rosterItem.name = - [rosterElem attributeForName: @"name"].stringValue; + [elem attributeForName: @"jid"].stringValue]; + rosterItem.name = [elem attributeForName: @"name"].stringValue; rosterItem.subscription = - [rosterElem attributeForName: @"subscription"].stringValue; + [elem attributeForName: @"subscription"].stringValue; for (OFXMLElement *groupElem in [elem elementsForName: @"group" - namespace: NS_ROSTER]) { - OFMutableArray *rosterGroup = - [roster objectForKey: rosterElem.stringValue]; - - if (rosterGroup == nil) { - rosterGroup = [OFMutableArray array]; - [roster setObject: rosterGroup - forKey: rosterElem.stringValue]; - } - - [rosterGroup addObject: rosterItem]; - } - - rosterItem.groups = groups; - - if (groups.count == 0) { - OFMutableArray *rosterGroup = - [roster objectForKey: @""]; - - if (rosterGroup == nil) { - rosterGroup = [OFMutableArray array]; - [roster setObject: rosterGroup - forKey: @""]; - } - - [rosterGroup addObject: rosterItem]; - } + namespace: NS_ROSTER]) + [groups addObject: + [groupElem.children.firstObject stringValue]]; + + if (groups.count > 0) + rosterItem.groups = groups; + + [roster XMPP_addRosterItem: rosterItem]; } if ([delegate respondsToSelector: @selector(connectionDidReceiveRoster:)]) [delegate connectionDidReceiveRoster: self]; Index: src/XMPPJID.m ================================================================== --- src/XMPPJID.m +++ src/XMPPJID.m @@ -199,6 +199,11 @@ node, domain, resource]; else return [OFString stringWithFormat: @"%@/%@", domain, resource]; } + +- (OFString*)description +{ + return [self fullJID]; +} @end ADDED src/XMPPRoster.h Index: src/XMPPRoster.h ================================================================== --- src/XMPPRoster.h +++ src/XMPPRoster.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011, 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 + +@class XMPPConnection; +@class XMPPRosterItem; + +@interface XMPPRoster: OFObject +{ + XMPPConnection *connection; + OFMutableDictionary *groups; +} + +- initWithConnection: (XMPPConnection*)conn; +- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; +- (OFArray*)groups; +- (OFArray*)rosterItemsInGroup: (OFString*)group; +@end ADDED src/XMPPRoster.m Index: src/XMPPRoster.m ================================================================== --- src/XMPPRoster.m +++ src/XMPPRoster.m @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011, 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 "XMPPRoster.h" +#import "XMPPRosterItem.h" + +@implementation XMPPRoster +- initWithConnection: (XMPPConnection*)conn +{ + self = [super init]; + + @try { + connection = [conn retain]; + groups = [[OFMutableDictionary alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [connection release]; + + [super dealloc]; +} + +- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem +{ + if (rosterItem.groups.count > 0) { + for (OFString *group in rosterItem.groups) { + OFMutableArray *rosterGroup = + [groups objectForKey: group]; + + if (rosterGroup == nil) { + rosterGroup = [OFMutableArray array]; + [groups setObject: rosterGroup + forKey: group]; + } + + [rosterGroup addObject: rosterItem]; + } + } else { + OFMutableArray *rosterGroup = [groups objectForKey: @""]; + + if (rosterGroup == nil) { + rosterGroup = [OFMutableArray array]; + [groups setObject: rosterGroup + forKey: @""]; + } + + [rosterGroup addObject: rosterItem]; + } +} + +- (OFArray*)groups +{ + OFMutableArray *ret = [OFMutableArray array]; + + for (OFString *group in groups) + [ret addObject: group]; + + ret->isa = [OFArray class]; + return ret; +} + +- (OFArray*)rosterItemsInGroup: (OFString*)group +{ + if (group == nil) + group = @""; + + return [[[groups objectForKey: group] copy] autorelease]; +} +@end ADDED src/XMPPRosterItem.h Index: src/XMPPRosterItem.h ================================================================== --- src/XMPPRosterItem.h +++ src/XMPPRosterItem.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011, 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 + +@class XMPPJID; + +@interface XMPPRosterItem: OFObject +@property (copy) XMPPJID *JID; +@property (copy) OFString *name; +@property (copy) OFString *subscription; +@property (copy) OFArray *groups; + ++ rosterItem; +@end ADDED src/XMPPRosterItem.m Index: src/XMPPRosterItem.m ================================================================== --- src/XMPPRosterItem.m +++ src/XMPPRosterItem.m @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011, 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 "XMPPRosterItem.h" + +@implementation XMPPRosterItem +@synthesize JID, name, subscription, groups; + ++ rosterItem +{ + return [[[self alloc] init] autorelease]; +} + +- (OFString*)description +{ + return [OFString stringWithFormat: @"", + JID, name, subscription, groups]; +} +@end Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -29,10 +29,11 @@ #import "XMPPJID.h" #import "XMPPStanza.h" #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" +#import "XMPPRoster.h" @interface AppDelegate: OFObject @end OF_APPLICATION_DELEGATE(AppDelegate) @@ -118,11 +119,14 @@ - (void)connectionDidReceiveRoster :(XMPPConnection*)conn { XMPPPresence *pres; - of_log(@"Got roster"); + of_log(@"Got roster! Groups: %@", conn.roster.groups); + for (OFString *group in conn.roster.groups) + of_log(@"Group %@: %@", group, + [conn.roster rosterItemsInGroup: group]); pres = [XMPPPresence presence]; [pres addPriority: 10]; [pres addStatus: @"ObjXMPP test is working!"];