Skip to content

Commit

Permalink
Improved AB integration. Now merges cards nicely. If you have duplica…
Browse files Browse the repository at this point in the history
…tes, try deleting the Jabber People group created by a previous version and let the client recreate it.

git-svn-id: http://svn.gna.org/svn/etoile/trunk/Etoile/Services/User/Jabber/xmpp@2638 7b3f36aa-e6db-49c1-8b4c-66eac1997e64
  • Loading branch information
davidchisnall committed Nov 20, 2007
1 parent d5f818b commit e04d499
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 26 deletions.
23 changes: 23 additions & 0 deletions ABPerson+merging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// ABPerson+merging.h
// Jabber
//
// Created by David Chisnall on 19/11/2007.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <AddressBook/AddressBook.h>

@interface ABPerson (Merging)
/**
* Attempts to find an existing person in the address book who
* might correspond to this one.
*/
- (ABPerson*) findExistingPerson;
/**
* Attempts to merge the fields in the argument in to
* this person. Returns an array of conlicting properties.
*/
- (NSArray*) mergePerson:(ABPerson*)aPerson;
@end
131 changes: 131 additions & 0 deletions ABPerson+merging.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// ABPerson+merging.m
// Jabber
//
// Created by David Chisnall on 19/11/2007.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "ABPerson+merging.h"
#import "Macros.h"

#define MATCH(x) [ABPerson searchElementForProperty:x\
label:nil\
key:nil\
value:[self valueForProperty:x]\
comparison:kABEqual];
#define MATCH_IM_ADDRESS(type) if((addresses = [self valueForProperty:type]) != nil)\
{\
for(unsigned int i=0 ; i<[addresses count] ; i++)\
{\
id address = [addresses valueAtIndex:i];\
ABSearchElement * search = [ABPerson searchElementForProperty:type\
label:nil\
key:nil\
value:address\
comparison:kABEqual];\
NSArray * people = [ab recordsMatchingSearchElement:search];\
if([people count] == 1)\
{\
return [people objectAtIndex:0];\
}\
}\
}



@implementation ABPerson (merging)
- (ABPerson*) findExistingPerson
{
ABAddressBook * ab = [ABAddressBook sharedAddressBook];
NSString * firstName = [self valueForProperty:kABFirstNameProperty];
if([self valueForProperty:kABLastNameProperty] && firstName)
{
ABSearchElement * search = MATCH(kABLastNameProperty);
NSArray * people = [ab recordsMatchingSearchElement:search];
FOREACH(people, person, ABPerson*)
{
if([firstName isEqualToString:[person valueForProperty:kABFirstNameProperty]])
{
return person;
}
}
}
else
{
ABMultiValue * addresses;
MATCH_IM_ADDRESS(kABJabberInstantProperty)
MATCH_IM_ADDRESS(kABMSNInstantProperty)
MATCH_IM_ADDRESS(kABAIMInstantProperty)
MATCH_IM_ADDRESS(kABICQInstantProperty)
MATCH_IM_ADDRESS(kABYahooInstantProperty)
}
return nil;
}
- (NSArray*) mergePerson:(ABPerson*)aPerson
{
NSArray * properties = [ABPerson properties];
NSMutableArray * failedProperties = [NSMutableArray array];
FOREACH(properties, property, NSString*)
{
//Don't update implicit properties.
if(!([property isEqualToString:kABUIDProperty]
||
[property isEqualToString:kABCreationDateProperty]
||
[property isEqualToString:kABModificationDateProperty]))
{
id otherValue = [aPerson valueForProperty:property];
if(otherValue != nil)
{
id value = [self valueForProperty:property];
//If we have no copy of this, just add it
if(value == nil)
{
[self setValue:otherValue forProperty:property];
}
else
{
//If it's a multivalue with compatible types, merge it:
if([value isKindOfClass:[ABMultiValue class]] &&
([value propertyType] == [otherValue propertyType]))
{
ABMutableMultiValue * old = [value mutableCopy];
for(unsigned int i=0 ; i<[otherValue count] ; i++)
{
id v = [(ABMultiValue*)otherValue valueAtIndex:i];
BOOL duplicate = NO;
for(unsigned int j=0 ; i<[old count] ; i++)
{
if([v isEqual:[old valueAtIndex:j]])
{
duplicate = YES;
break;
}
}
if(!duplicate)
{
[old addValue:v withLabel:[otherValue labelAtIndex:i]];
}
}
[self setValue:old forProperty:property];
}
else
{
if(![otherValue isEqual:value])
{
NSLog(@"Not merging %@. %@ => %@", property, value, otherValue);
[failedProperties addObject:property];
}
}
}
}
}
}
if([self imageData] == nil)
{
[self setImageData:[aPerson imageData]];
}
return failedProperties;
}
@end
3 changes: 2 additions & 1 deletion JabberPerson.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
id roster;
unsigned int hash;
ABPerson * vCard;
NSString * photoHash;
NSMutableDictionary * photoHashes;
NSString * currentHash;
NSImage * avatar;
}
/**
Expand Down
87 changes: 65 additions & 22 deletions JabberPerson.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import "XMPPConnection.h"
#import "ServiceDiscovery.h"
#import "NSData+Base64.h"
#import "ABPerson+merging.h"
#import "../Macros.h"

@implementation JabberPerson
Expand Down Expand Up @@ -41,7 +42,8 @@ - (id) init
{
SUPERINIT
identities = [[NSMutableDictionary alloc] init];
identityList = [[NSMutableArray alloc] init];
identityList = [[NSMutableArray alloc] init];
photoHashes = [[NSMutableDictionary alloc] init];
return self;
}

Expand Down Expand Up @@ -165,11 +167,11 @@ - (void) handlePresence:(Presence*)aPresence
NSString * newPhotoHash = [[aPresence children] objectForKey:@"vCardUpdate"];
if(newPhotoHash)
{
if(photoHash == nil)
if(currentHash == nil)
{
photoHash = [[[vCard imageData] sha1] retain];
currentHash = [[[vCard imageData] sha1] retain];
}
if(![photoHash isEqualToString:newPhotoHash])
if(![photoHashes objectForKey:newPhotoHash])
{
[self requestvCard:from];
}
Expand Down Expand Up @@ -209,7 +211,12 @@ - (void) handleIq:(Iq*)anIq
ABAddressBook * ab = [ABAddressBook sharedAddressBook];
if(vCard != nil)
{
//TODO: Merge the vCard data
//Merge the vCard data
NSArray * unmerged = [vCard mergePerson:identityvCard];
if([unmerged count] > 0)
{
//TODO: Ask about which ones to merge
}
}
else
{
Expand All @@ -218,7 +225,7 @@ - (void) handleIq:(Iq*)anIq
{
[vCard setValue:name forProperty:kABNicknameProperty];
}
//TODO: Parse bridged JIDs correctly (i.e. parse [email protected]
//Parse bridged JIDs correctly (i.e. parse [email protected]
//as an ICQ UIN, not a JID)
id property = kABJabberInstantProperty;
id label = kABJabberHomeLabel;
Expand Down Expand Up @@ -264,25 +271,40 @@ - (void) handleIq:(Iq*)anIq
[vCard setValue:vCardJID forProperty:property];
[vCardJID release];
}
//Get the group that contains Jabber people
ABGroup * jabberGroup = nil;
NSArray * groups = [ab groups];
FOREACH(groups, abgroup, ABRecord*)
ABPerson * oldPerson = [vCard findExistingPerson];
if(oldPerson != nil)
{
if([[abgroup valueForProperty:kABGroupNameProperty] isEqualToString:@"Jabber People"])
NSString * notes = [oldPerson valueForProperty:kABNoteProperty];
if(notes == nil)
{
jabberGroup = (ABGroup*)abgroup;
notes = @"";
}
notes = [notes stringByAppendingFormat:@"\nMerged properties from %@", [jid jidString]];
[oldPerson setValue:notes forProperty:kABNoteProperty];
[oldPerson mergePerson:vCard];
vCard = oldPerson;
}
if(jabberGroup == nil)
else
{
jabberGroup = [[[ABGroup alloc] init] autorelease];
[jabberGroup setValue:@"Jabber People" forProperty:kABGroupNameProperty];
[ab addRecord:jabberGroup];
//Get the group that contains Jabber people
ABGroup * jabberGroup = nil;
NSArray * groups = [ab groups];
FOREACH(groups, abgroup, ABRecord*)
{
if([[abgroup valueForProperty:kABGroupNameProperty] isEqualToString:@"Jabber People"])
{
jabberGroup = (ABGroup*)abgroup;
}
}
if(jabberGroup == nil)
{
jabberGroup = [[[ABGroup alloc] init] autorelease];
[jabberGroup setValue:@"Jabber People" forProperty:kABGroupNameProperty];
[ab addRecord:jabberGroup];
}
[ab addRecord:vCard];
[jabberGroup addMember:vCard];
}
//
[ab addRecord:vCard];
[jabberGroup addMember:vCard];
[ab save];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSDictionary * vCards = [defaults dictionaryForKey:@"vCards"];
Expand All @@ -293,8 +315,19 @@ - (void) handleIq:(Iq*)anIq
NSMutableDictionary * newvCards = [vCards mutableCopy];
[newvCards setObject:[vCard uniqueId] forKey:[NSString stringWithFormat:@"%@!%@", group, name]];
[defaults setObject:newvCards forKey:@"vCards"];
[photoHash release];
photoHash = [[[vCard imageData] sha1] retain];
}
NSData * imageData = [vCard imageData];
if(imageData != nil)
{
NSString * imageHash = [imageData sha1];
if(![imageHash isEqualToString:currentHash])
{
[photoHashes setObject:imageData forKey:imageHash];
[avatar release];
avatar = nil;
[currentHash release];
currentHash = [imageHash retain];
}
}
}
}
Expand Down Expand Up @@ -326,7 +359,16 @@ - (NSImage*) avatar
{
if(avatar == nil)
{
NSData * avatarData = [vCard imageData];
NSData * avatarData = [photoHashes objectForKey:currentHash];
if(avatarData == nil)
{
avatarData = [vCard imageData];
if(avatarData != nil)
{
currentHash = [[avatarData sha1] retain];
[photoHashes setObject:avatarData forKey:currentHash];
}
}
if(avatarData != nil)
{
avatar = [[NSImage alloc] initWithData:avatarData];
Expand All @@ -340,6 +382,7 @@ - (void) dealloc
[group release];
[name release];
[identities release];
[photoHashes release];
[super dealloc];
}
@end
15 changes: 12 additions & 3 deletions XMPPvCard.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ - (void)startElement:(NSString *)aName
#define PROPERTY_FROM_XML(property, xml)\
- (void) add ## xml:(NSString*)aString\
{\
NSLog(@"Trying to add '%@' for property %s", aString, #property);\
if(aString != nil && ![aString isEqualToString:@""])\
{\
[person setValue:aString forProperty:property];\
Expand All @@ -88,7 +87,6 @@ - (void) add ## xml:(NSString*)aString\
#define MULTI_PROPERTY_FROM_XML(property, label, xml) \
- (void) add ## xml:(NSString*)aString\
{\
NSLog(@"Trying to add '%@' for property %s", aString, #property);\
if(aString != nil && ![aString isEqualToString:@""])\
{\
MULTI_INIT;\
Expand All @@ -99,10 +97,21 @@ - (void) add ## xml:(NSString*)aString\
}
MULTI_PROPERTY_FROM_XML(kABEmailProperty, kABEmailHomeLabel, EMAIL)
MULTI_PROPERTY_FROM_XML(kABURLsProperty, kABHomePageLabel, URL)
- (void) addBINVAL:(NSString*)aString
//FIXME: This should actually be getting <PHOTO><BINVAL>{CDATA}</BINVAL></PHOTO>, not <PHOTO>{CDATA}</PHOTO>
- (void) addPHOTO:(NSString*)aString
{
NSMutableString * photo = [aString mutableCopy];
[photo replaceOccurrencesOfString:@"\n" withString:@"" options:0 range:NSMakeRange(0, [photo length])];


if([photo length] > 9 && [[photo substringToIndex:9] isEqualToString:@"image/png"])
{
[photo deleteCharactersInRange:NSMakeRange(0,9)];
}
else if([photo length] > 10 && [[photo substringToIndex:10] isEqualToString:@"image/jpeg"])
{
[photo deleteCharactersInRange:NSMakeRange(0,10)];
}
[person setImageData:[photo base64DecodedData]];
}
- (void) addFN:(NSString*)aString
Expand Down

0 comments on commit e04d499

Please sign in to comment.