Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -155,11 +155,11 @@ OFMutableDictionary *callbacks; XMPPAuthenticator *authModule; BOOL streamOpen; BOOL needsSession; BOOL encryptionRequired, encrypted; - BOOL rosterVersioningSupported; + BOOL supportsRosterVersioning; unsigned int lastID; /// \endcond } #ifdef OF_HAVE_PROPERTIES @@ -194,11 +194,11 @@ /// \brief Whether encryption is required @property BOOL encryptionRequired; /// \brief Whether the connection is encrypted @property (readonly) BOOL encrypted; /// \brief Whether roster versioning is supported -@property (readonly) BOOL rosterVersioningSupported; +@property (readonly) BOOL supportsRosterVersioning; #endif /** * \brief Creates a new autoreleased XMPPConnection. * @@ -336,11 +336,11 @@ - (uint16_t)port; - (void)setDataStorage: (id )dataStorage; - (id )dataStorage; - (void)setLanguage: (OFString*)language; - (OFString*)language; -- (BOOL)rosterVersioningSupported; +- (BOOL)supportsRosterVersioning; /// \cond internal - (void)XMPP_startStream; - (void)XMPP_handleStream: (OFXMLElement*)element; - (void)XMPP_handleTLS: (OFXMLElement*)element; Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -348,13 +348,13 @@ - (BOOL)streamOpen { return streamOpen; } -- (BOOL)rosterVersioningSupported +- (BOOL)supportsRosterVersioning { - return rosterVersioningSupported; + return supportsRosterVersioning; } - (BOOL)checkCertificateAndGetReason: (OFString**)reason { X509Certificate *cert; @@ -850,11 +850,11 @@ /* TODO: Find/create an exception to throw here */ @throw [OFException exceptionWithClass: isa]; if ([element elementForName: @"ver" namespace: XMPP_NS_ROSTERVER] != nil) - rosterVersioningSupported = YES; + supportsRosterVersioning = YES; if (mechs != nil) { OFEnumerator *enumerator; OFXMLElement *mech; Index: src/XMPPRoster.h ================================================================== --- src/XMPPRoster.h +++ src/XMPPRoster.h @@ -145,16 +145,14 @@ - (void)setDataStorage: (id )dataStorage; - (id )dataStorage; /// \cond internal -- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem; - (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem; -- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem; - (void)XMPP_handleInitialRosterForConnection: (XMPPConnection*)connection withIQ: (XMPPIQ*)iq; - (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element; /// \endcond @end @interface OFObject (XMPPRosterDelegate) @end Index: src/XMPPRoster.m ================================================================== --- src/XMPPRoster.m +++ src/XMPPRoster.m @@ -73,17 +73,32 @@ } - (void)requestRoster { XMPPIQ *iq; + OFXMLElement *query; rosterRequested = YES; iq = [XMPPIQ IQWithType: @"get" ID: [connection generateStanzaID]]; - [iq addChild: [OFXMLElement elementWithName: @"query" - namespace: XMPP_NS_ROSTER]]; + + query = [OFXMLElement elementWithName: @"query" + namespace: XMPP_NS_ROSTER]; + + if ([connection supportsRosterVersioning]) { + OFString *ver = [dataStorage stringValueForPath: @"roster.ver"]; + + if (ver == nil) + ver = @""; + + [query addAttributeWithName: @"ver" + stringValue: ver]; + } + + [iq addChild: query]; + [connection sendIQ: iq withCallbackObject: self selector: @selector(XMPP_handleInitialRosterForConnection: withIQ:)]; } @@ -108,20 +123,24 @@ 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]; - - [delegates broadcastSelector: @selector( - roster:didReceiveRosterItem:) - withObject: self - withObject: rosterItem]; - } + [self XMPP_updateRosterItem: rosterItem]; + } + + if ([connection supportsRosterVersioning]) { + OFString *ver = + [[rosterElement attributeForName: @"ver"] stringValue]; + [dataStorage setStringValue: ver + forPath: @"roster.ver"]; + } + + [delegates broadcastSelector: @selector( + roster:didReceiveRosterItem:) + withObject: self + withObject: rosterItem]; [connection_ sendStanza: [iq resultIQ]]; return YES; } @@ -201,24 +220,45 @@ - (id )dataStorage { return dataStorage; } -- (void)XMPP_addRosterItem: (XMPPRosterItem*)rosterItem -{ - return [self XMPP_updateRosterItem: rosterItem]; -} - - (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem { - [rosterItems setObject: rosterItem - forKey: [[rosterItem JID] bareJID]]; -} + if ([connection supportsRosterVersioning]) { + OFMutableDictionary *items = [[[dataStorage dictionaryForPath: + @"roster.items"] mutableCopy] autorelease]; + + if (![[rosterItem subscription] isEqual: @"remove"]) { + OFMutableDictionary *item = [OFMutableDictionary + dictionaryWithKeysAndObjects: + @"JID", [[rosterItem JID] bareJID], + @"subscription", [rosterItem subscription], + nil]; + + if ([rosterItem name] != nil) + [item setObject: [rosterItem name] + forKey: @"name"]; + + if ([rosterItem groups] != nil) + [item setObject: [rosterItem groups] + forKey: @"groups"]; + + [items setObject: item + forKey: [[rosterItem JID] bareJID]]; + } else + [items removeObjectForKey: [[rosterItem JID] bareJID]]; + + [dataStorage setDictionary: items + forPath: @"roster.items"]; + } -- (void)XMPP_deleteRosterItem: (XMPPRosterItem*)rosterItem -{ - [rosterItems removeObjectForKey: [[rosterItem JID] bareJID]]; + if (![[rosterItem subscription] isEqual: @"remove"]) + [rosterItems setObject: rosterItem + forKey: [[rosterItem JID] bareJID]]; + else + [rosterItems removeObjectForKey: [[rosterItem JID] bareJID]]; } - (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element { OFString *subscription; @@ -253,33 +293,57 @@ [rosterItem setGroups: groups]; return rosterItem; } -- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection*)connection +- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection*)connection_ withIQ: (XMPPIQ*)iq { OFXMLElement *rosterElement; OFEnumerator *enumerator; OFXMLElement *element; - XMPPRosterItem *rosterItem = nil; + XMPPRosterItem *rosterItem; rosterElement = [iq elementForName: @"query" namespace: XMPP_NS_ROSTER]; + + if ([connection supportsRosterVersioning]) { + OFDictionary *items = [dataStorage + dictionaryForPath: @"roster.items"]; + OFEnumerator *enumerator = [items objectEnumerator]; + OFDictionary *item; + + while ((item = [enumerator nextObject]) != nil) { + rosterItem = [XMPPRosterItem rosterItem]; + [rosterItem setJID: [XMPPJID JIDWithString: + [item objectForKey: @"JID"]]]; + [rosterItem setName: [item objectForKey: @"name"]]; + [rosterItem setSubscription: + [item objectForKey: @"subscription"]]; + [rosterItem setGroups: [item objectForKey: @"groups"]]; + + [rosterItems setObject: rosterItem + forKey: [[rosterItem JID] bareJID]]; + } + } 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]; + [self XMPP_updateRosterItem: rosterItem]; + } + + if ([connection supportsRosterVersioning]) { + OFString *ver = + [[rosterElement attributeForName: @"ver"] stringValue]; + [dataStorage setStringValue: ver + forPath: @"roster.ver"]; } [delegates broadcastSelector: @selector(rosterWasReceived:) withObject: self]; } Index: src/XMPPRosterItem.m ================================================================== --- src/XMPPRosterItem.m +++ src/XMPPRosterItem.m @@ -23,10 +23,12 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif #import "XMPPRosterItem.h" + +#import @implementation XMPPRosterItem + rosterItem { return [[[self alloc] init] autorelease]; @@ -102,15 +104,13 @@ return [[subscription copy] autorelease]; } - (void)setGroups: (OFArray*)groups_ { - OFArray *old = groups; - groups = [groups_ copy]; - [old release]; + OF_SETTER(groups, groups_, YES, YES) } - (OFArray*)groups { - return [[groups copy] autorelease]; + OF_GETTER(groups, YES) } @end