Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -1,8 +1,8 @@ /* * Copyright (c) 2010, 2011, 2012, Jonathan Schleifer - * Copyright (c) 2011, Florian Zeitz + * Copyright (c) 2011, 2012, 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 @@ -27,12 +27,10 @@ @class XMPPJID; @class XMPPIQ; @class XMPPMessage; @class XMPPPresence; @class XMPPAuthenticator; -@class XMPPRoster; -@class XMPPRosterItem; @class SSLSocket; @class XMPPMulticastDelegate; @protocol XMPPConnectionDelegate #ifndef XMPP_CONNECTION_M @@ -46,19 +44,16 @@ - (void)connection: (XMPPConnection*)connection didSendElement: (OFXMLElement*)element; - (void)connectionWasAuthenticated: (XMPPConnection*)connection; - (void)connection: (XMPPConnection*)connection wasBoundToJID: (XMPPJID*)JID; -- (void)connectionDidReceiveRoster: (XMPPConnection*)connection; - (BOOL)connection: (XMPPConnection*)connection didReceiveIQ: (XMPPIQ*)iq; - (void)connection: (XMPPConnection*)connection didReceivePresence: (XMPPPresence*)presence; - (void)connection: (XMPPConnection*)connection didReceiveMessage: (XMPPMessage*)message; -- (void)connection: (XMPPConnection*)connection - didReceiveRosterItem: (XMPPRosterItem*)rosterItem; - (void)connectionWasClosed: (XMPPConnection*)connection; - (void)connectionWillUpgradeToTLS: (XMPPConnection*)connection; - (void)connectionDidUpgradeToTLS: (XMPPConnection*)connection; @end @@ -83,19 +78,17 @@ XMPPAuthenticator *authModule; BOOL streamOpen; BOOL needsSession; BOOL encryptionRequired, encrypted; unsigned int lastID; - XMPPRoster *roster; } #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 (readonly, retain) XMPPRoster *roster; @property (readonly, retain, getter=socket) OFTCPSocket *sock; @property (assign) BOOL encryptionRequired; @property (readonly) BOOL encrypted; @property (readonly) BOOL streamOpen; #endif @@ -220,11 +213,10 @@ - (void)setResource: (OFString*)resource; - (OFString*)resource; - (XMPPJID*)JID; - (void)setPort: (uint16_t)port; - (uint16_t)port; -- (XMPPRoster*)roster; - (void)XMPP_startStream; - (void)XMPP_handleStream: (OFXMLElement*)element; - (void)XMPP_handleTLS: (OFXMLElement*)element; - (void)XMPP_handleSASL: (OFXMLElement*)element; Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -1,8 +1,8 @@ /* * Copyright (c) 2010, 2011, 2012, Jonathan Schleifer - * Copyright (c) 2011, Florian Zeitz + * Copyright (c) 2011, 2012, 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 @@ -45,12 +45,10 @@ #import "XMPPStanza.h" #import "XMPPJID.h" #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 @@ -92,11 +90,10 @@ [resource release]; [JID release]; [delegates release]; [callbacks release]; [authModule release]; - [roster release]; [super dealloc]; } - (void)setUsername: (OFString*)username_ @@ -531,20 +528,16 @@ * them the next time the parser returns. */ oldParser = parser; oldElementBuilder = elementBuilder; - [roster release]; - parser = [[OFXMLParser alloc] init]; [parser setDelegate: self]; elementBuilder = [[OFXMLElementBuilder alloc] init]; [elementBuilder setDelegate: self]; - roster = [[XMPPRoster alloc] initWithConnection: self]; - [sock writeFormat: @"\n" @"", domain]; @@ -780,15 +773,10 @@ [callback runWithIQ: iq]; [callbacks removeObjectForKey: [iq ID]]; return; } - if ([iq elementForName: @"query" - namespace: XMPP_NS_ROSTER]) - if ([roster handleIQ: iq]) - return; - handled = [delegates broadcastSelector: @selector( connection:didReceiveIQ:) forConnection: self withObject: iq]; @@ -1057,15 +1045,10 @@ - (XMPPMulticastDelegate*)XMPP_delegates { return delegates; } - -- (XMPPRoster*)roster -{ - return [[roster retain] autorelease]; -} @end @implementation OFObject (XMPPConnectionDelegate) - (void)connection: (XMPPConnection*)connection didReceiveElement: (OFXMLElement*)element @@ -1084,14 +1067,10 @@ - (void)connection: (XMPPConnection*)connection wasBoundToJID: (XMPPJID*)JID { } -- (void)connectionDidReceiveRoster: (XMPPConnection*)connection -{ -} - - (BOOL)connection: (XMPPConnection*)connection didReceiveIQ: (XMPPIQ*)iq { return NO; } @@ -1102,15 +1081,10 @@ } - (void)connection: (XMPPConnection*)connection didReceiveMessage: (XMPPMessage*)message { -} - -- (void)connection: (XMPPConnection*)connection - didReceiveRosterItem: (XMPPRosterItem*)rosterItem -{ } - (void)connectionWasClosed: (XMPPConnection*)connection { } Index: src/XMPPRoster.h ================================================================== --- src/XMPPRoster.h +++ src/XMPPRoster.h @@ -1,7 +1,8 @@ /* * Copyright (c) 2011, Jonathan Schleifer + * Copyright (c) 2012, 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 @@ -20,27 +21,55 @@ * POSSIBILITY OF SUCH DAMAGE. */ #import -@class XMPPConnection; +#import "XMPPConnection.h" + @class XMPPRosterItem; @class XMPPIQ; +@class XMPPRoster; + +@protocol XMPPRosterDelegate +#ifndef XMPP_ROSTER_M + +#endif +#ifdef OF_HAVE_OPTIONAL_PROTOCOLS +@optional +#endif +- (void)rosterWasReceived: (XMPPRoster*)roster; +- (void)roster: (XMPPRoster*)roster + didReceiveRosterItem: (XMPPRosterItem*)rosterItem; +@end + @interface XMPPRoster: OFObject +#ifdef OF_HAVE_OPTIONAL_PROTOCOLS + +#endif { XMPPConnection *connection; OFMutableDictionary *rosterItems; - OFString *rosterID; + id delegate; } + +#ifdef OF_HAVE_PROPERTIES +@property (assign) id delegate; +#endif - initWithConnection: (XMPPConnection*)conn; -- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; -- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem; -- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem; - (OFDictionary*)rosterItems; -- (BOOL)handleIQ: (XMPPIQ*)iq; - (void)requestRoster; - (void)addRosterItem: (XMPPRosterItem*)rosterItem; - (void)updateRosterItem: (XMPPRosterItem*)rosterItem; - (void)deleteRosterItem: (XMPPRosterItem*)rosterItem; +- (void)setDelegate: (id )delegate; +- (id )delegate; +- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; +- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem; +- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem; +- (void)XMPP_handleInitialRoster: (XMPPIQ*)iq; +- (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element; +@end + +@interface OFObject (XMPPRosterDelegate) @end Index: src/XMPPRoster.m ================================================================== --- src/XMPPRoster.m +++ src/XMPPRoster.m @@ -1,7 +1,8 @@ /* * Copyright (c) 2011, Jonathan Schleifer + * Copyright (c) 2012, 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 @@ -22,10 +23,12 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#define XMPP_ROSTER_M + #include #import "XMPPRoster.h" #import "XMPPRosterItem.h" #import "XMPPConnection.h" @@ -33,16 +36,18 @@ #import "XMPPJID.h" #import "XMPPMulticastDelegate.h" #import "namespaces.h" @implementation XMPPRoster -- initWithConnection: (XMPPConnection*)conn +- initWithConnection: (XMPPConnection*)connection_ { self = [super init]; @try { rosterItems = [[OFMutableDictionary alloc] init]; + connection = connection_; + [connection addDelegate: self]; } @catch (id e) { [self release]; @throw e; } @@ -50,30 +55,14 @@ } - (void)dealloc { [rosterItems release]; - [rosterID release]; [super dealloc]; } -- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem -{ - return [self XMPP_updateRosterItem: rosterItem]; -} - -- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem -{ - [rosterItems setObject: rosterItem - forKey: [[rosterItem JID] bareJID]]; -} - -- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem -{ - [rosterItems removeObjectForKey: [[rosterItem JID] bareJID]]; -} - (OFDictionary*)rosterItems { return [[rosterItems copy] autorelease]; } @@ -80,106 +69,53 @@ - (void)requestRoster { XMPPIQ *iq; - if (rosterID != nil) - assert(0); - - rosterID = [[connection generateStanzaID] retain]; iq = [XMPPIQ IQWithType: @"get" - ID: rosterID]; + ID: [connection generateStanzaID]]; [iq addChild: [OFXMLElement elementWithName: @"query" namespace: XMPP_NS_ROSTER]]; - [connection sendStanza: iq]; + [connection sendIQ: iq + withCallbackObject: self + selector: @selector(XMPP_handleInitialRoster:)]; } -- (BOOL)handleIQ: (XMPPIQ*)iq +- (BOOL)connection: (XMPPConnection*)connection_ + didReceiveIQ: (XMPPIQ*)iq { OFXMLElement *rosterElement; OFXMLElement *element; - XMPPRosterItem *rosterItem = nil; - OFString *subscription; - OFEnumerator *enumerator; - BOOL isPush = ![[iq ID] isEqual: rosterID]; + XMPPRosterItem *rosterItem; rosterElement = [iq elementForName: @"query" namespace: XMPP_NS_ROSTER]; if (rosterElement == nil) return NO; - if (isPush) { - if (![[iq type] isEqual: @"set"]) - return NO; - } else { - if (![[iq type] isEqual: @"result"]) - return NO; - } - - enumerator = [[rosterElement children] objectEnumerator]; - while ((element = [enumerator nextObject]) != nil) { - OFMutableArray *groups = [OFMutableArray array]; - OFEnumerator *groupEnumerator; - OFXMLElement *groupElement; - - if (![[element name] isEqual: @"item"] || - ![[element namespace] isEqual: XMPP_NS_ROSTER]) - continue; - - rosterItem = [XMPPRosterItem rosterItem]; - [rosterItem setJID: [XMPPJID JIDWithString: - [[element attributeForName: @"jid"] stringValue]]]; - [rosterItem setName: - [[element attributeForName: @"name"] stringValue]]; - - subscription = [[element attributeForName: - @"subscription"] stringValue]; - - if (![subscription isEqual: @"none"] && - ![subscription isEqual: @"to"] && - ![subscription isEqual: @"from"] && - ![subscription isEqual: @"both"] && - (![subscription isEqual: @"remove"] || !isPush)) - subscription = @"none"; - - [rosterItem setSubscription: subscription]; - - groupEnumerator = [[element - elementsForName: @"group" - namespace: XMPP_NS_ROSTER] objectEnumerator]; - while ((groupElement = [groupEnumerator nextObject]) != nil) - [groups addObject: [groupElement stringValue]]; - - if ([groups count] > 0) - [rosterItem setGroups: groups]; - - if ([subscription isEqual: @"remove"]) + if (![[iq type] isEqual: @"set"]) + return NO; + + element = [rosterElement elementForName: @"item" + namespace: XMPP_NS_ROSTER]; + + if (element != nil) { + rosterItem = [self XMPP_rosterItemWithXMLElement: element]; + + if ([[rosterItem subscription] isEqual: @"remove"]) [self XMPP_deleteRosterItem: rosterItem]; else [self XMPP_addRosterItem: rosterItem]; - if (isPush) { - SEL sel = @selector(connection:didReceiveRosterItem:); - - [[connection XMPP_delegates] - broadcastSelector: sel - forConnection: connection - withObject: rosterItem]; - } - } - - if (isPush) { - [connection sendStanza: [iq resultIQ]]; - } else { - [[connection XMPP_delegates] - broadcastSelector: @selector(connectionDidReceiveRoster:) - forConnection: connection]; - - [rosterID release]; - rosterID = nil; - } + if ([delegate respondsToSelector: + @selector(roster:didReceiveRosterItem:)]) + [delegate roster: self + didReceiveRosterItem: rosterItem]; + } + + [connection_ sendStanza: [iq resultIQ]]; return YES; } - (void)addRosterItem: (XMPPRosterItem*)rosterItem @@ -233,6 +169,107 @@ [query addChild: item]; [iq addChild: query]; [connection sendStanza: iq]; } + +- (void)setDelegate: (id )delegate_ +{ + delegate = (id )delegate_; +} + +- (id )delegate +{ + return delegate; +} + +- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem +{ + return [self XMPP_updateRosterItem: rosterItem]; +} + +- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem +{ + [rosterItems setObject: rosterItem + forKey: [[rosterItem JID] bareJID]]; +} + +- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem +{ + [rosterItems removeObjectForKey: [[rosterItem JID] bareJID]]; +} + +- (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element +{ + OFString *subscription; + OFEnumerator *groupEnumerator; + OFXMLElement *groupElement; + OFMutableArray *groups = [OFMutableArray array]; + XMPPRosterItem *rosterItem = [XMPPRosterItem rosterItem]; + [rosterItem setJID: [XMPPJID JIDWithString: + [[element attributeForName: @"jid"] stringValue]]]; + [rosterItem setName: + [[element attributeForName: @"name"] stringValue]]; + + subscription = [[element attributeForName: + @"subscription"] stringValue]; + + if (![subscription isEqual: @"none"] && + ![subscription isEqual: @"to"] && + ![subscription isEqual: @"from"] && + ![subscription isEqual: @"both"] && + ![subscription isEqual: @"remove"]) + subscription = @"none"; + + [rosterItem setSubscription: subscription]; + + groupEnumerator = [[element + elementsForName: @"group" + namespace: XMPP_NS_ROSTER] objectEnumerator]; + while ((groupElement = [groupEnumerator nextObject]) != nil) + [groups addObject: [groupElement stringValue]]; + + if ([groups count] > 0) + [rosterItem setGroups: groups]; + + return rosterItem; +} + +- (void)XMPP_handleInitialRoster: (XMPPIQ*)iq +{ + OFXMLElement *rosterElement; + OFEnumerator *enumerator; + OFXMLElement *element; + XMPPRosterItem *rosterItem = nil; + + rosterElement = [iq elementForName: @"query" + namespace: XMPP_NS_ROSTER]; + + enumerator = [[rosterElement children] objectEnumerator]; + while ((element = [enumerator nextObject]) != nil) { + if (![[element name] isEqual: @"item"] || + ![[element namespace] isEqual: XMPP_NS_ROSTER]) + continue; + + rosterItem = [self XMPP_rosterItemWithXMLElement: element]; + + if ([[rosterItem subscription] isEqual: @"remove"]) + [self XMPP_deleteRosterItem: rosterItem]; + else + [self XMPP_addRosterItem: rosterItem]; + } + + if ([delegate respondsToSelector: @selector(rosterWasReceived:)]) + [delegate rosterWasReceived: self]; +} +@end + +@implementation OFObject (XMPPRosterDelegate) +- (void)rosterWasReceived: (XMPPRoster*)roster +{ +} + +- (void)roster: (XMPPRoster*)roster + didReceiveRosterItem: (XMPPRosterItem*)rosterItem +{ +} @end Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -1,8 +1,8 @@ /* * Copyright (c) 2010, 2011, Jonathan Schleifer - * Copyright (c) 2011, Florian Zeitz + * Copyright (c) 2011, 2012, 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 @@ -34,20 +34,23 @@ #import "XMPPPresence.h" #import "XMPPRoster.h" @interface AppDelegate: OFObject #ifdef OF_HAVE_OPTIONAL_PROTOCOLS - + #endif +{ + XMPPConnection * conn; + XMPPRoster *roster; +} @end OF_APPLICATION_DELEGATE(AppDelegate) @implementation AppDelegate - (void)applicationDidFinishLaunching { - XMPPConnection *conn; OFArray *arguments = [OFApplication arguments]; XMPPPresence *pres = [XMPPPresence presence]; [pres addShow: @"chat"]; [pres addStatus: @"Bored"]; @@ -89,11 +92,14 @@ assert(([[OFString stringWithFormat: @"%@, %@, %@, %@", [[stanza from] fullJID], [[stanza to] fullJID], [stanza type], [stanza ID]] isEqual: @"bob@localhost, alice@localhost, get, 42"])); conn = [[XMPPConnection alloc] init]; + roster = [[XMPPRoster alloc] initWithConnection: conn]; + [conn addDelegate: self]; + [roster setDelegate: self]; if ([arguments count] != 3) { of_log(@"Invalid count of command line arguments!"); [OFApplication terminateWithStatus: 1]; } @@ -131,18 +137,18 @@ - (void)connection: (XMPPConnection*)conn wasBoundToJID: (XMPPJID*)jid { of_log(@"Bound to JID: %@", [jid fullJID]); - [[conn roster] requestRoster]; + [roster requestRoster]; } -- (void)connectionDidReceiveRoster: (XMPPConnection*)conn +- (void)rosterWasReceived: (XMPPRoster*)roster_ { XMPPPresence *pres; - of_log(@"Got roster: %@", [[conn roster] rosterItems]); + of_log(@"Got roster: %@", [roster_ rosterItems]); pres = [XMPPPresence presence]; [pres addPriority: 10]; [pres addStatus: @"ObjXMPP test is working!"]; @@ -158,14 +164,14 @@ of_log(@"Ping response: %@", resp); }]; #endif } -- (void)connectionDidUpgradeToTLS: (XMPPConnection*)conn +- (void)connectionDidUpgradeToTLS: (XMPPConnection*)conn_ { @try { - [conn checkCertificate]; + [conn_ checkCertificate]; } @catch (SSLInvalidCertificateException *e) { OFString *answer; [of_stdout writeString: @"Couldn't verify certificate: "]; [of_stdout writeFormat: @"%@\n", e]; [of_stdout writeString: @"Do you want to continue [y/N]? "]; @@ -173,11 +179,11 @@ if (![answer hasPrefix: @"y"]) @throw e; } } -- (void)connection: (XMPPConnection*)conn +- (void)roster: (XMPPRoster*)roster_ didReceiveRosterItem: (XMPPRosterItem*)rosterItem { of_log(@"Got roster push: %@", rosterItem); }