From f06d57373ae200b22977827be48dd8502f8feb3a Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Mar 2012 14:07:36 +0000 Subject: [PATCH 1/2] Applied last changed from ca.inverse.sogo branch, pertaining to iOS handling of CardDAV Monotone-Parent: bee9d78831eda60f6cda2aadb62fc4a04a2e8e10 Monotone-Revision: 693036245b42a3d0059f8c06a2a725e716d54c9e Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-03-30T14:07:36 --- ChangeLog | 29 ++ SoObjects/Contacts/SOGoContactFolders.m | 46 +++ SoObjects/Contacts/SOGoContactLDIFEntry.m | 5 + SoObjects/Contacts/SOGoContactSourceFolder.m | 285 ++++++++++++++++++- SoObjects/Contacts/SOGoFolder+CardDAV.m | 17 +- SoObjects/Contacts/SOGoUserFolder+Contacts.m | 27 ++ SoObjects/SOGo/LDAPSource.m | 14 +- 7 files changed, 406 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7afe15f01..ca5486e35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2012-03-28 Wolfgang Sourdeau + + * SoObjects/Contacts/SOGoFolder+CardDAV.m (_isValidFilter:): + accept "email" as filter name. + (_appendObject:withBaseURL:toREPORTResponse:): fixed the ordering + of XML elements as the address*-data props where put outside of + the tree. + + * SoObjects/SOGo/LDAPSource.m (-allEntryIDs): take the _filter + ivar into account. + +2012-03-27 Wolfgang Sourdeau + + * SoObjects/Contacts/SOGoContactFolders.m + (-lookupName:inContext:acquire:): overriden method that enables + the lookup of hidden public sources with iOS devices. + + * SoObjects/Contacts/SOGoContactLDIFEntry.m (-davAddressData): new + getter similar to davCalendarData. + + * SoObjects/Contacts/SOGoContactSourceFolder.m + (-davAddressbookMultiget): new REPORT handler based on + -[SOGoAppointmentFolder davCalendarMultiget]. + + * SoObjects/Contacts/SOGoUserFolder+Contacts.m + (-davDirectoryGateway): new getter that returns the url to the + first available directory source. + (-davResourceType): declare resource as "directory" (carddav). + 2012-03-26 Wolfgang Sourdeau * OpenChange/MAPIStoreTasksMessage.m (-save): take PR_HTML as diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index 01b1f3abb..537d7a08a 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -259,6 +259,52 @@ return keys; } +- (id) lookupName: (NSString *) name + inContext: (WOContext *) lookupContext + acquire: (BOOL) acquire +{ + id folder = nil; + SOGoUser *currentUser; + SOGoUserManager *um; + SOGoSystemDefaults *sd; + NSEnumerator *domains; + NSArray *sourceIDs; + NSString *domain, *srcDisplayName; + + if ([[context request] isIPhoneAddressBookApp]) + { + currentUser = [context activeUser]; + if (activeUserIsOwner + || [[currentUser login] isEqualToString: owner]) + { + domain = [currentUser domain]; + um = [SOGoUserManager sharedUserManager]; + sd = [SOGoSystemDefaults sharedSystemDefaults]; + domains = [[sd visibleDomainsForDomain: domain] objectEnumerator]; + while (domain) + { + sourceIDs = [um addressBookSourceIDsInDomain: domain]; + if ([sourceIDs containsObject: name]) + { + srcDisplayName = [um displayNameForSourceWithID: name]; + folder = [SOGoContactSourceFolder + folderWithName: name + andDisplayName: srcDisplayName + inContainer: self]; + [folder setSource: [um sourceWithID: name]]; + } + domain = [domains nextObject]; + } + } + } + + if (!folder) + folder = [super lookupName: name inContext: lookupContext + acquire: acquire]; + + return folder; +} + - (NSException *) setDavContactsCategories: (NSString *) newCategories { SOGoUser *ownerUser; diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.m b/SoObjects/Contacts/SOGoContactLDIFEntry.m index 6801875fb..4472ae901 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.m +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.m @@ -166,6 +166,11 @@ return @"text/x-vcard"; } +- (NSString *) davAddressData +{ + return [self contentAsString]; +} + - (NSException *) save { return [(SOGoContactSourceFolder *) container saveLDIFEntry: self]; diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index b4a5b2318..260a7ce35 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -34,17 +35,23 @@ #import #import #import +#import #import +#import +#import #import #import +#import #import #import #import +#import #import #import #import #import +#import #import "SOGoContactFolders.h" #import "SOGoContactGCSFolder.h" @@ -130,14 +137,13 @@ - (NSArray *) davResourceType { NSMutableArray *resourceType; - NSArray *cardDavCollection; - - cardDavCollection - = [NSArray arrayWithObjects: @"addressbook", - @"urn:ietf:params:xml:ns:carddav", nil]; + NSArray *type; resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; - [resourceType addObject: cardDavCollection]; + type = [NSArray arrayWithObjects: @"addressbook", XMLNS_CARDDAV, nil]; + [resourceType addObject: type]; + type = [NSArray arrayWithObjects: @"directory", XMLNS_CARDDAV, nil]; + [resourceType addObject: type]; return resourceType; } @@ -350,6 +356,273 @@ return result; } +- (NSString *) _deduceObjectNameFromURL: (NSString *) url + fromBaseURL: (NSString *) baseURL +{ + NSRange urlRange; + NSString *name; + + urlRange = [url rangeOfString: baseURL]; + if (urlRange.location != NSNotFound) + { + name = [url substringFromIndex: NSMaxRange (urlRange)]; + if ([name hasPrefix: @"/"]) + name = [name substringFromIndex: 1]; + } + else + name = nil; + + return name; +} + +/* TODO: multiget reorg */ +- (NSString *) _nodeTagForProperty: (NSString *) property +{ + NSString *namespace, *nodeName, *nsRep; + NSRange nsEnd; + + nsEnd = [property rangeOfString: @"}"]; + namespace + = [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)]; + nodeName = [property substringFromIndex: nsEnd.location + 1]; + if ([namespace isEqualToString: XMLNS_CARDDAV]) + nsRep = @"C"; + else + nsRep = @"D"; + + return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName]; +} + +- (NSString *) _nodeTag: (NSString *) property +{ + static NSMutableDictionary *tags = nil; + NSString *nodeTag; + + if (!tags) + tags = [NSMutableDictionary new]; + nodeTag = [tags objectForKey: property]; + if (!nodeTag) + { + nodeTag = [self _nodeTagForProperty: property]; + [tags setObject: nodeTag forKey: property]; + } + + return nodeTag; +} + +- (NSString **) _properties: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + SOGoContactLDIFEntry *ldifEntry; + NSString **currentProperty; + NSString **values, **currentValue; + SEL methodSel; + +// NSLog (@"_properties:ofObject:: %@", [NSDate date]); + + values = NSZoneMalloc (NULL, + (propertiesCount + 1) * sizeof (NSString *)); + *(values + propertiesCount) = nil; + + ldifEntry = [SOGoContactLDIFEntry + contactEntryWithName: [object objectForKey: @"c_name"] + withLDIFEntry: object + inContainer: self]; + currentProperty = properties; + currentValue = values; + while (*currentProperty) + { + methodSel = SOGoSelectorForPropertyGetter (*currentProperty); + if (methodSel && [ldifEntry respondsToSelector: methodSel]) + *currentValue = [[ldifEntry performSelector: methodSel] + stringByEscapingXMLString]; + currentProperty++; + currentValue++; + } + +// NSLog (@"/_properties:ofObject:: %@", [NSDate date]); + + return values; +} + +- (NSArray *) _propstats: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + NSMutableArray *propstats, *properties200, *properties404, *propDict; + NSString **property, **values, **currentValue; + NSString *propertyValue, *nodeTag; + +// NSLog (@"_propstats:ofObject:: %@", [NSDate date]); + + propstats = [NSMutableArray array]; + + properties200 = [NSMutableArray array]; + properties404 = [NSMutableArray array]; + + values = [self _properties: properties count: propertiesCount + ofObject: object]; + currentValue = values; + + property = properties; + while (*property) + { + nodeTag = [self _nodeTag: *property]; + if (*currentValue) + { + propertyValue = [NSString stringWithFormat: @"<%@>%@", + nodeTag, *currentValue, nodeTag]; + propDict = properties200; + } + else + { + propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag]; + propDict = properties404; + } + [propDict addObject: propertyValue]; + property++; + currentValue++; + } + free (values); + + if ([properties200 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties200, @"properties", + @"HTTP/1.1 200 OK", @"status", + nil]]; + if ([properties404 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties404, @"properties", + @"HTTP/1.1 404 Not Found", @"status", + nil]]; +// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); + + return propstats; +} + +- (void) _appendPropstat: (NSDictionary *) propstat + toBuffer: (NSMutableString *) r +{ + NSArray *properties; + unsigned int count, max; + + [r appendString: @""]; + properties = [propstat objectForKey: @"properties"]; + max = [properties count]; + for (count = 0; count < max; count++) + [r appendString: [properties objectAtIndex: count]]; + [r appendString: @""]; + [r appendString: [propstat objectForKey: @"status"]]; + [r appendString: @""]; +} + +- (void) appendObject: (NSDictionary *) object + properties: (NSString **) properties + count: (unsigned int) propertiesCount + withBaseURL: (NSString *) baseURL + toBuffer: (NSMutableString *) r +{ + NSArray *propstats; + unsigned int count, max; + + [r appendFormat: @""]; + [r appendString: baseURL]; + [r appendString: [[object objectForKey: @"c_name"] stringByEscapingURL]]; + [r appendString: @""]; + +// NSLog (@"(appendPropstats...): %@", [NSDate date]); + propstats = [self _propstats: properties count: propertiesCount + ofObject: object]; + max = [propstats count]; + for (count = 0; count < max; count++) + [self _appendPropstat: [propstats objectAtIndex: count] + toBuffer: r]; +// NSLog (@"/(appendPropstats...): %@", [NSDate date]); + + [r appendString: @""]; +} + +- (void) appendMissingObjectRef: (NSString *) href + toBuffer: (NSMutableString *) r +{ + [r appendString: @""]; + [r appendString: href]; + [r appendString: @"HTTP/1.1 404 Not Found"]; +} + +- (void) _appendComponentProperties: (NSArray *) properties + matchingURLs: (id ) refs + toResponse: (WOResponse *) response +{ + NSObject *element; + NSString *url, *baseURL, *cname; + NSString **propertiesArray; + NSMutableString *buffer; + unsigned int count, max, propertiesCount; + + baseURL = [self davURLAsString]; +#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 + if (![baseURL hasSuffix: @"/"]) + baseURL = [NSString stringWithFormat: @"%@/", baseURL]; + + propertiesArray = [properties asPointersOfObjects]; + propertiesCount = [properties count]; + + max = [refs length]; + buffer = [NSMutableString stringWithCapacity: max*512]; + + for (count = 0; count < max; count++) + { + element = [refs objectAtIndex: count]; + url = [[[element firstChild] nodeValue] stringByUnescapingURL]; + cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL]; + if (cname) + [self appendObject: [source lookupContactEntry: cname] + properties: propertiesArray + count: propertiesCount + withBaseURL: baseURL + toBuffer: buffer]; + else + [self appendMissingObjectRef: url + toBuffer: buffer]; + } + [response appendContentString: buffer]; +// NSLog (@"/adding properties with url"); + + NSZoneFree (NULL, propertiesArray); +} + +- (WOResponse *) performMultigetInContext: (WOContext *) queryContext + inNamespace: (NSString *) namespace +{ + WOResponse *r; + id document; + DOMElement *documentElement, *propElement; + + r = [context response]; + [r prepareDAVResponse]; + [r appendContentString: + [NSString stringWithFormat: @"", namespace]]; + document = [[queryContext request] contentAsDOMDocument]; + documentElement = (DOMElement *) [document documentElement]; + propElement = [documentElement firstElementWithTag: @"prop" + inNamespace: @"DAV:"]; + [self _appendComponentProperties: [propElement flatPropertyNameOfSubElements] + matchingURLs: [documentElement getElementsByTagName: @"href"] + toResponse: r]; + [r appendContentString:@""]; + + return r; +} + +- (id) davAddressbookMultiget: (id) queryContext +{ + return [self performMultigetInContext: queryContext + inNamespace: XMLNS_CARDDAV]; +} + - (NSString *) davDisplayName { return displayName; diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index fed4c2105..3466e940e 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -73,17 +73,17 @@ etagLine = [NSString stringWithFormat: @"%@", [component davEntityTag]]; [r appendContentString: etagLine]; - [r appendContentString: @"" - @"HTTP/1.1 200 OK" - @"" - @""]; + [r appendContentString: @""]; contactString = [[component contentAsString] stringByEscapingXMLString]; [r appendContentString: contactString]; - [r appendContentString: @"" - @""]; - [r appendContentString: contactString]; [r appendContentString: @"" - @""]; + @""]; + [r appendContentString: contactString]; + [r appendContentString: @"" + @"" + @"HTTP/1.1 200 OK" + @"" + @""]; } } @@ -124,6 +124,7 @@ return ([newString isEqualToString: @"sn"] || [newString isEqualToString: @"givenname"] + || [newString isEqualToString: @"email"] || [newString isEqualToString: @"mail"] || [newString isEqualToString: @"telephonenumber"]); } diff --git a/SoObjects/Contacts/SOGoUserFolder+Contacts.m b/SoObjects/Contacts/SOGoUserFolder+Contacts.m index 817c91cb8..2244456d4 100644 --- a/SoObjects/Contacts/SOGoUserFolder+Contacts.m +++ b/SoObjects/Contacts/SOGoUserFolder+Contacts.m @@ -29,6 +29,7 @@ #import #import +#import #import #import @@ -58,4 +59,30 @@ return [NSArray arrayWithObject: tag]; } +- (NSArray *) davDirectoryGateway +{ + NSArray *tag, *sources; + SOGoContactFolders *parent; + SOGoUserManager *um; + NSString *domain, *url; + + domain = [[context activeUser] domain]; + um = [SOGoUserManager sharedUserManager]; + sources = [um addressBookSourceIDsInDomain: domain]; + if ([sources count] > 0) + { + parent = [self privateContacts: @"Contacts" inContext: context]; + url = [NSString stringWithFormat: @"%@%@/", [parent davURLAsString], + [sources objectAtIndex: 0]]; + tag = [NSArray arrayWithObject: + [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + url, nil]]; + } + else + tag = nil; + + return tag; +} + + @end diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index b5cbc146a..3269ca10c 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -830,6 +830,8 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField NSEnumerator *entries; NGLdapEntry *currentEntry; NGLdapConnection *ldapConnection; + EOQualifier *qualifier; + NSMutableString *qs; NSString *value; NSArray *attributes; NSMutableArray *ids; @@ -838,17 +840,23 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField ldapConnection = [self _ldapConnection]; attributes = [NSArray arrayWithObject: IDField]; + + qs = [NSMutableString stringWithFormat: @"(%@='*')", CNField]; + if ([_filter length]) + [qs appendFormat: @" AND %@", _filter]; + qualifier = [EOQualifier qualifierWithQualifierFormat: qs]; + if ([_scope caseInsensitiveCompare: @"BASE"] == NSOrderedSame) entries = [ldapConnection baseSearchAtBaseDN: baseDN - qualifier: nil + qualifier: qualifier attributes: attributes]; else if ([_scope caseInsensitiveCompare: @"ONE"] == NSOrderedSame) entries = [ldapConnection flatSearchAtBaseDN: baseDN - qualifier: nil + qualifier: qualifier attributes: attributes]; else entries = [ldapConnection deepSearchAtBaseDN: baseDN - qualifier: nil + qualifier: qualifier attributes: attributes]; while ((currentEntry = [entries nextObject])) From 64b8498eac7f1aa0b900e98675889cf2daa0dd5b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 30 Mar 2012 14:09:07 +0000 Subject: [PATCH 2/2] Monotone-Parent: 34215cf1c90ce31c32de705320b2cd5f2bee55ec Monotone-Revision: d1a795ace9b3ecfe78615e345153f5c37d0ca4b2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-03-30T14:09:07 Monotone-Branch: ca.inverse.sogo --- Version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Version b/Version index 4364995c1..09503ebca 100644 --- a/Version +++ b/Version @@ -2,6 +2,6 @@ # This file is included by library makefiles to set the version information # of the executable. -MAJOR_VERSION=1 -MINOR_VERSION=3 -SUBMINOR_VERSION=15 +MAJOR_VERSION=2 +MINOR_VERSION=0 +SUBMINOR_VERSION=0