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
maint-2.0.2
Wolfgang Sourdeau 2012-03-30 14:07:36 +00:00
parent 0c2c418046
commit f06d57373a
7 changed files with 406 additions and 17 deletions

View File

@ -1,3 +1,32 @@
2012-03-28 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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 <D:prop> tree.
* SoObjects/SOGo/LDAPSource.m (-allEntryIDs): take the _filter
ivar into account.
2012-03-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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 <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreTasksMessage.m (-save): take PR_HTML as

View File

@ -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;

View File

@ -166,6 +166,11 @@
return @"text/x-vcard";
}
- (NSString *) davAddressData
{
return [self contentAsString];
}
- (NSException *) save
{
return [(SOGoContactSourceFolder *) container saveLDIFEntry: self];

View File

@ -24,6 +24,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/NSException+HTTP.h>
@ -34,17 +35,23 @@
#import <NGObjWeb/SoObject.h>
#import <NGObjWeb/SoSelectorInvocation.h>
#import <NGObjWeb/SoUser.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
#import <EOControl/EOSortOrdering.h>
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/DOMNode+SOGo.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#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: @"<D:propstat><D:prop>"];
properties = [propstat objectForKey: @"properties"];
max = [properties count];
for (count = 0; count < max; count++)
[r appendString: [properties objectAtIndex: count]];
[r appendString: @"</D:prop><D:status>"];
[r appendString: [propstat objectForKey: @"status"]];
[r appendString: @"</D:status></D:propstat>"];
}
- (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: @"<D:response><D:href>"];
[r appendString: baseURL];
[r appendString: [[object objectForKey: @"c_name"] stringByEscapingURL]];
[r appendString: @"</D:href>"];
// 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: @"</D:response>"];
}
- (void) appendMissingObjectRef: (NSString *) href
toBuffer: (NSMutableString *) r
{
[r appendString: @"<D:response><D:href>"];
[r appendString: href];
[r appendString: @"</D:href><D:status>HTTP/1.1 404 Not Found</D:status></D:response>"];
}
- (void) _appendComponentProperties: (NSArray *) properties
matchingURLs: (id <DOMNodeList>) refs
toResponse: (WOResponse *) response
{
NSObject <DOMElement> *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 <DOMDocument> document;
DOMElement *documentElement, *propElement;
r = [context response];
[r prepareDAVResponse];
[r appendContentString:
[NSString stringWithFormat: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"%@\">", 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:@"</D:multistatus>"];
return r;
}
- (id) davAddressbookMultiget: (id) queryContext
{
return [self performMultigetInContext: queryContext
inNamespace: XMLNS_CARDDAV];
}
- (NSString *) davDisplayName
{
return displayName;

View File

@ -73,17 +73,17 @@
etagLine = [NSString stringWithFormat: @"<D:getetag>%@</D:getetag>",
[component davEntityTag]];
[r appendContentString: etagLine];
[r appendContentString: @"</D:prop>"
@"<D:status>HTTP/1.1 200 OK</D:status>"
@"</D:propstat>"
@"<C:addressbook-data>"];
[r appendContentString: @"<C:address-data>"];
contactString = [[component contentAsString] stringByEscapingXMLString];
[r appendContentString: contactString];
[r appendContentString: @"</C:addressbook-data>"
@"<C:address-data>"];
[r appendContentString: contactString];
[r appendContentString: @"</C:address-data>"
@"</D:response>"];
@"<C:addressbook-data>"];
[r appendContentString: contactString];
[r appendContentString: @"</C:addressbook-data>"
@"</D:prop>"
@"<D:status>HTTP/1.1 200 OK</D:status>"
@"</D:propstat>"
@"</D:response>"];
}
}
@ -124,6 +124,7 @@
return ([newString isEqualToString: @"sn"]
|| [newString isEqualToString: @"givenname"]
|| [newString isEqualToString: @"email"]
|| [newString isEqualToString: @"mail"]
|| [newString isEqualToString: @"telephonenumber"]);
}

View File

@ -29,6 +29,7 @@
#import <SaxObjC/XMLNamespaces.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/NSObject+DAV.h>
#import <SOGo/NSString+DAV.h>
@ -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

View File

@ -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]))