Index: ObjXMPP.xcodeproj/project.pbxproj ================================================================== --- ObjXMPP.xcodeproj/project.pbxproj +++ ObjXMPP.xcodeproj/project.pbxproj @@ -35,10 +35,11 @@ 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 */; }; + 4BF459B91340DE3600701BCC /* XMPPRoster_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF459B81340DE3600701BCC /* XMPPRoster_private.h */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 4B1295DE1337BD2D00154B25 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -81,10 +82,11 @@ 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; }; + 4BF459B81340DE3600701BCC /* XMPPRoster_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPRoster_private.h; path = src/XMPPRoster_private.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 4B1295DD1337BD2D00154B25 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -162,10 +164,11 @@ 4BC559E61337AC0900E345C7 /* XMPPPLAINAuth.h */, 4BC559E71337AC0900E345C7 /* XMPPPLAINAuth.m */, 4BC559E81337AC0900E345C7 /* XMPPPresence.h */, 4BC559E91337AC0900E345C7 /* XMPPPresence.m */, 4BDEF8051340B240000156D1 /* XMPPRoster.h */, + 4BF459B81340DE3600701BCC /* XMPPRoster_private.h */, 4BDEF8061340B240000156D1 /* XMPPRoster.m */, 4BD9BF57134003F700DAB43A /* XMPPRosterItem.h */, 4BD9BF58134003F700DAB43A /* XMPPRosterItem.m */, 4BC559EA1337AC0900E345C7 /* XMPPSCRAMAuth.h */, 4BC559FD1337AC1800E345C7 /* XMPPSCRAMAuth.m */, @@ -201,10 +204,11 @@ 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 */, + 4BF459B91340DE3600701BCC /* XMPPRoster_private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -29,10 +29,19 @@ @class XMPPMessage; @class XMPPPresence; @class XMPPAuthenticator; @class XMPPRoster; +#define XMPP_NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind" +#define XMPP_NS_CLIENT @"jabber:client" +#define XMPP_NS_ROSTER @"jabber:iq:roster" +#define XMPP_NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" +#define XMPP_NS_STARTTLS @"urn:ietf:params:xml:ns:xmpp-tls" +#define XMPP_NS_STANZAS @"urn:ietf:params:xml:ns:xmpp-stanzas" +#define XMPP_NS_SESSION @"urn:ietf:params:xml:ns:xmpp-session" +#define XMPP_NS_STREAM @"http://etherx.jabber.org/streams" + @protocol XMPPConnectionDelegate @optional - (void)connectionWasAuthenticated: (XMPPConnection*)conn; - (void)connection: (XMPPConnection*)conn wasBoundToJID: (XMPPJID*)jid; Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -35,22 +35,14 @@ #import "XMPPJID.h" #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" #import "XMPPRoster.h" +#import "XMPPRoster_private.h" #import "XMPPRosterItem.h" #import "XMPPExceptions.h" -#define NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind" -#define NS_CLIENT @"jabber:client" -#define NS_ROSTER @"jabber:iq:roster" -#define NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" -#define NS_STARTTLS @"urn:ietf:params:xml:ns:xmpp-tls" -#define NS_STANZAS @"urn:ietf:params:xml:ns:xmpp-stanzas" -#define NS_SESSION @"urn:ietf:params:xml:ns:xmpp-session" -#define NS_STREAM @"http://etherx.jabber.org/streams" - @interface XMPPConnection () - (void)XMPP_startStream; - (void)XMPP_handleStanza: (OFXMLElement*)elem; - (void)XMPP_sendAuth: (OFString*)name; - (void)XMPP_handleIQ: (XMPPIQ*)iq; @@ -267,11 +259,11 @@ attributes: (OFArray*)attrs { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; if (![name isEqual: @"stream"] || ![prefix isEqual: @"stream"] || - ![ns isEqual: NS_STREAM]) { + ![ns isEqual: XMPP_NS_STREAM]) { of_log(@"Did not get expected stream start!"); assert(0); } for (OFXMLAttribute *attr in attrs) { @@ -290,13 +282,13 @@ - (void)elementBuilder: (OFXMLElementBuilder*)b didBuildElement: (OFXMLElement*)elem { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - elem.defaultNamespace = NS_CLIENT; + elem.defaultNamespace = XMPP_NS_CLIENT; [elem setPrefix: @"stream" - forNamespace: NS_STREAM]; + forNamespace: XMPP_NS_STREAM]; of_log(@"In: %@", elem); [self XMPP_handleStanza: elem]; @@ -304,18 +296,19 @@ } - (void)XMPP_startStream { [sock writeFormat: @"\n" - @"", server]; } - (void)XMPP_handleStanza: (OFXMLElement*)elem { - if ([elem.namespace isEqual: NS_CLIENT]) { + if ([elem.namespace isEqual: XMPP_NS_CLIENT]) { if ([elem.name isEqual: @"iq"]) { [self XMPP_handleIQ: [XMPPIQ stanzaWithElement: elem]]; return; } @@ -332,20 +325,20 @@ } assert(0); } - if ([elem.namespace isEqual: NS_STREAM]) { + if ([elem.namespace isEqual: XMPP_NS_STREAM]) { if ([elem.name isEqual: @"features"]) { [self XMPP_handleFeatures: elem]; return; } assert(0); } - if ([elem.namespace isEqual: NS_STARTTLS]) { + if ([elem.namespace isEqual: XMPP_NS_STARTTLS]) { if ([elem.name isEqual: @"proceed"]) { /* FIXME: Catch errors here */ sock = [[GTLSSocket alloc] initWithSocket: sock]; /* Stream restart */ @@ -359,21 +352,22 @@ @throw [OFException newWithClass: isa]; assert(0); } - if ([elem.namespace isEqual: NS_SASL]) { + if ([elem.namespace isEqual: XMPP_NS_SASL]) { if ([elem.name isEqual: @"challenge"]) { OFXMLElement *responseTag; OFDataArray *challenge = [OFDataArray dataArrayWithBase64EncodedString: [elem.children.firstObject stringValue]]; OFDataArray *response = [authModule calculateResponseWithChallenge: challenge]; - responseTag = [OFXMLElement elementWithName: @"response" - namespace: NS_SASL]; + responseTag = [OFXMLElement + elementWithName: @"response" + namespace: XMPP_NS_SASL]; [responseTag addChild: [OFXMLElement elementWithCharacters: [response stringByBase64Encoding]]]; [self sendStanza: responseTag]; @@ -446,11 +440,11 @@ error = [OFXMLElement elementWithName: @"error"]; [error addAttributeWithName: @"type" stringValue: @"cancel"]; [error addChild: [OFXMLElement elementWithName: @"service-unavailable" - namespace: NS_STANZAS]]; + namespace: XMPP_NS_STANZAS]]; [iq addChild: error]; [self sendStanza: iq]; } } @@ -473,22 +467,24 @@ - (void)XMPP_handleFeatures: (OFXMLElement*)elem { OFXMLElement *starttls = [elem elementsForName: @"starttls" - namespace: NS_STARTTLS].firstObject; + namespace: XMPP_NS_STARTTLS].firstObject; OFXMLElement *bind = [elem elementsForName: @"bind" - namespace: NS_BIND].firstObject; - OFXMLElement *session = [elem elementsForName: @"session" - namespace: NS_SESSION].firstObject; + namespace: XMPP_NS_BIND].firstObject; + OFXMLElement *session = + [elem elementsForName: @"session" + namespace: XMPP_NS_SESSION].firstObject; OFArray *mechs = [elem elementsForName: @"mechanisms" - namespace: NS_SASL]; + namespace: XMPP_NS_SASL]; OFMutableArray *mechanisms = [OFMutableArray array]; if (starttls != nil) { - [self sendStanza: [OFXMLElement elementWithName: @"starttls" - namespace: NS_STARTTLS]]; + [self sendStanza: + [OFXMLElement elementWithName: @"starttls" + namespace: XMPP_NS_STARTTLS]]; return; } if (mechs.count > 0) { for (OFXMLElement *mech in [mechs.firstObject children]) @@ -529,11 +525,11 @@ - (void)XMPP_sendAuth: (OFString*)name { OFXMLElement *authTag; authTag = [OFXMLElement elementWithName: @"auth" - namespace: NS_SASL]; + namespace: XMPP_NS_SASL]; [authTag addAttributeWithName: @"mechanism" stringValue: name]; [authTag addChild: [OFXMLElement elementWithCharacters: [[authModule clientFirstMessage] stringByBase64Encoding]]]; @@ -548,11 +544,11 @@ bindID = [[self generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"set" ID: bindID]; bind = [OFXMLElement elementWithName: @"bind" - namespace: NS_BIND]; + namespace: XMPP_NS_BIND]; if (resource != nil) [bind addChild: [OFXMLElement elementWithName: @"resource" stringValue: resource]]; @@ -570,11 +566,11 @@ assert(0); bindElem = iq.children.firstObject; if (![bindElem.name isEqual: @"bind"] || - ![bindElem.namespace isEqual: NS_BIND]) + ![bindElem.namespace isEqual: XMPP_NS_BIND]) assert(0); jidElem = bindElem.children.firstObject; JID = [[XMPPJID alloc] initWithString: [jidElem.children.firstObject stringValue]]; @@ -598,11 +594,11 @@ sessionID = [[self generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"set" ID: sessionID]; [iq addChild: [OFXMLElement elementWithName: @"session" - namespace: NS_SESSION]]; + namespace: XMPP_NS_SESSION]]; [self sendStanza: iq]; } - (void)XMPP_handleSession: (XMPPIQ*)iq { @@ -626,11 +622,11 @@ rosterID = [[self generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"get" ID: rosterID]; [iq addChild: [OFXMLElement elementWithName: @"query" - namespace: NS_ROSTER]]; + namespace: XMPP_NS_ROSTER]]; [self sendStanza: iq]; } - (void)XMPP_handleRoster: (XMPPIQ*)iq { @@ -640,19 +636,19 @@ assert(0); rosterElem = iq.children.firstObject; if (![rosterElem.name isEqual: @"query"] || - ![rosterElem.namespace isEqual: NS_ROSTER]) + ![rosterElem.namespace isEqual: XMPP_NS_ROSTER]) assert(0); for (OFXMLElement *elem in rosterElem.children) { XMPPRosterItem *rosterItem; OFMutableArray *groups = [OFMutableArray array]; if (![elem.name isEqual: @"item"] || - ![elem.ns isEqual: NS_ROSTER]) + ![elem.ns isEqual: XMPP_NS_ROSTER]) continue; rosterItem = [XMPPRosterItem rosterItem]; rosterItem.JID = [XMPPJID JIDWithString: [elem attributeForName: @"jid"].stringValue]; @@ -660,11 +656,11 @@ rosterItem.subscription = [elem attributeForName: @"subscription"].stringValue; for (OFXMLElement *groupElem in [elem elementsForName: @"group" - namespace: NS_ROSTER]) + namespace: XMPP_NS_ROSTER]) [groups addObject: [groupElem.children.firstObject stringValue]]; if (groups.count > 0) rosterItem.groups = groups; Index: src/XMPPRoster.h ================================================================== --- src/XMPPRoster.h +++ src/XMPPRoster.h @@ -30,9 +30,9 @@ XMPPConnection *connection; OFMutableDictionary *groups; } - initWithConnection: (XMPPConnection*)conn; -- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; - (OFArray*)groups; - (OFArray*)rosterItemsInGroup: (OFString*)group; +- (void)addRosterItem: (XMPPRosterItem*)rosterItem; @end Index: src/XMPPRoster.m ================================================================== --- src/XMPPRoster.m +++ src/XMPPRoster.m @@ -19,11 +19,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #import "XMPPRoster.h" +#import "XMPPRoster_private.h" #import "XMPPRosterItem.h" +#import "XMPPConnection.h" +#import "XMPPIQ.h" +#import "XMPPJID.h" @implementation XMPPRoster - initWithConnection: (XMPPConnection*)conn { self = [super init]; @@ -90,6 +94,32 @@ if (group == nil) group = @""; return [[[groups objectForKey: group] copy] autorelease]; } + +- (void)addRosterItem: (XMPPRosterItem*)rosterItem +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" + ID: [connection generateStanzaID]]; + OFXMLElement *query = [OFXMLElement elementWithName: @"query" + namespace: XMPP_NS_ROSTER]; + OFXMLElement *item = [OFXMLElement elementWithName: @"item" + namespace: XMPP_NS_ROSTER]; + + [item addAttributeWithName: @"jid" + stringValue: rosterItem.JID.bareJID]; + if (rosterItem.name != nil) + [item addAttributeWithName: @"name" + stringValue: rosterItem.name]; + + for (OFString *group in rosterItem.groups) + [item addChild: [OFXMLElement elementWithName: @"group" + namespace: XMPP_NS_ROSTER + stringValue: group]]; + + [query addChild: item]; + [iq addChild: query]; + + [connection sendStanza: iq]; +} @end ADDED src/XMPPRoster_private.h Index: src/XMPPRoster_private.h ================================================================== --- /dev/null +++ src/XMPPRoster_private.h @@ -0,0 +1,27 @@ +/* + * 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" + +@interface XMPPRoster () +- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; +@end