Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -1,6 +1,6 @@ all: - objfw-compile -Wall --lib 0.0 -o objxmpp *.m \ + objfw-compile -Wall -g --lib 0.0 -o objxmpp *.m \ `pkg-config --cflags --libs libidn` -lobjgnutls clean: rm -f *.o *.so *.dylib *.dll Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -33,10 +33,11 @@ @protocol XMPPConnectionDelegate @optional - (void)connectionWasAuthenticated: (XMPPConnection*)conn; - (void)connection: (XMPPConnection*)conn wasBoundToJID: (XMPPJID*)jid; +- (void)connectionDidReceiveRoster: (XMPPConnection*)conn; - (void)connection: (XMPPConnection*)conn didReceiveIQ: (XMPPIQ*)iq; - (void)connection: (XMPPConnection*)conn didReceivePresence: (XMPPPresence*)pres; - (void)connection: (XMPPConnection*)conn @@ -60,18 +61,20 @@ BOOL useTLS; id delegate; XMPPAuthenticator *authModule; BOOL needsSession; unsigned int lastID; - OFString *bindID, *sessionID; + OFString *bindID, *sessionID, *rosterID; + OFMutableDictionary *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; /** * Connects to the XMPP service. */ - (void)connect; @@ -92,6 +95,11 @@ * Generates a new, unique stanza ID. * * \return A new, generated, unique stanza ID. */ - (OFString*)generateStanzaID; + +/** + * Requests the roster. + */ +- (void)requestRoster; @end Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -38,10 +38,11 @@ #import "XMPPPresence.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_SESSION @"urn:ietf:params:xml:ns:xmpp-session" #define NS_STREAM @"http://etherx.jabber.org/streams" @@ -54,28 +55,36 @@ - (void)XMPP_handlePresence: (XMPPPresence*)pres; - (void)XMPP_sendResourceBind; - (void)XMPP_handleResourceBind: (XMPPIQ*)iq; - (void)XMPP_sendSession; - (void)XMPP_handleSession: (XMPPIQ*)iq; +- (void)XMPP_handleRoster: (XMPPIQ*)iq; @end @implementation XMPPConnection -@synthesize JID, port, useTLS, delegate; +@synthesize JID, port, useTLS, delegate, roster; - init { self = [super init]; - sock = [[OFTCPSocket alloc] init]; - parser = [[OFXMLParser alloc] init]; - elementBuilder = [[OFXMLElementBuilder alloc] init]; - - port = 5222; - useTLS = YES; - - parser.delegate = self; - elementBuilder.delegate = self; + @try { + sock = [[OFTCPSocket alloc] init]; + parser = [[OFXMLParser alloc] init]; + elementBuilder = [[OFXMLElementBuilder alloc] init]; + + port = 5222; + useTLS = YES; + + parser.delegate = self; + elementBuilder.delegate = self; + + roster = [[OFMutableDictionary alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } return self; } - (void)dealloc @@ -90,10 +99,12 @@ [JID release]; [delegate release]; [authModule release]; [bindID release]; [sessionID release]; + [rosterID release]; + [roster release]; [super dealloc]; } - (void)setUsername: (OFString*)username_ @@ -383,19 +394,24 @@ @"version='1.0'>", server]; } - (void)XMPP_handleIQ: (XMPPIQ*)iq { - if ([iq.ID isEqual: bindID] && [iq.type isEqual: @"result"]) { + if ([iq.ID isEqual: bindID]) { [self XMPP_handleResourceBind: iq]; return; } - if ([iq.ID isEqual: sessionID] && [iq.type isEqual: @"result"]) { + if ([iq.ID isEqual: sessionID]) { [self XMPP_handleSession: iq]; return; } + + if ([iq.ID isEqual: rosterID]) { + [self XMPP_handleRoster: iq]; + return; + } if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) [delegate connection: self didReceiveIQ: iq]; } @@ -506,13 +522,18 @@ [self sendStanza: iq]; } - (void)XMPP_handleResourceBind: (XMPPIQ*)iq { - OFXMLElement *bindElem = iq.children.firstObject; + OFXMLElement *bindElem; OFXMLElement *jidElem; + if (![iq.type isEqual: @"result"]) + assert(0); + + bindElem = iq.children.firstObject; + if (![bindElem.name isEqual: @"bind"] || ![bindElem.namespace isEqual: NS_BIND]) assert(0); jidElem = bindElem.children.firstObject; @@ -554,6 +575,66 @@ wasBoundToJID: JID]; [sessionID release]; sessionID = nil; } + +- (void)requestRoster +{ + XMPPIQ *iq; + + if (rosterID != nil) + assert(0); + + rosterID = [[self generateStanzaID] retain]; + iq = [XMPPIQ IQWithType: @"get" + ID: rosterID]; + [iq addChild: [OFXMLElement elementWithName: @"query" + namespace: NS_ROSTER]]; + [self sendStanza: iq]; +} + +- (void)XMPP_handleRoster: (XMPPIQ*)iq +{ + OFXMLElement *rosterElem; + + if (![iq.type isEqual: @"result"]) + assert(0); + + rosterElem = iq.children.firstObject; + + if (![rosterElem.name isEqual: @"query"] || + ![rosterElem.namespace isEqual: NS_ROSTER]) + assert(0); + + for (OFXMLElement *elem in rosterElem.children) { + OFString *group; + OFMutableArray *rosterGroup; + + if (![elem.name isEqual: @"item"] || + ![elem.ns isEqual: NS_ROSTER]) + continue; + + group = [[elem + elementsForName: @"group" + namespace: NS_ROSTER].firstObject stringValue]; + + if (group == nil) + group = @""; + + if ((rosterGroup = [roster objectForKey: group]) == nil) { + rosterGroup = [OFMutableArray array]; + [roster setObject: rosterGroup + forKey: group]; + } + + [rosterGroup addObject: elem]; + } + + if ([delegate respondsToSelector: + @selector(connectionDidReceiveRoster:)]) + [delegate connectionDidReceiveRoster: self]; + + [rosterID release]; + rosterID = nil; +} @end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -1,5 +1,5 @@ all: - objfw-compile -Wall -o tests *.m -I../src -L../src -lobjxmpp + objfw-compile -Wall -g -o tests *.m -I../src -L../src -lobjxmpp clean: rm -f tests *.o Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -109,13 +109,20 @@ } - (void)connection: (XMPPConnection*)conn wasBoundToJID: (XMPPJID*)jid { + of_log(@"Bound to JID: %@", [jid fullJID]); + + [conn requestRoster]; +} + +- (void)connectionDidReceiveRoster :(XMPPConnection*)conn +{ XMPPPresence *pres; - of_log(@"Bound to JID: %@", [jid fullJID]); + of_log(@"Got roster"); pres = [XMPPPresence presence]; [pres addPriority: 10]; [pres addStatus: @"ObjXMPP test is working!"];