diff --git a/ChangeLog b/ChangeLog index da06ae4bb..b5a43da44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2010-08-12 Wolfgang Sourdeau + + * SoObjects/Contacts/SOGoFolder+CardDAV.m: + (_appendObject:withBaseURL:toREPORTResponse:): moved method here + from SOGOContact{GCS,Source}Folder since its implementation was + mostly the same in the two classes and never invoked anywhere + else. + Modified to return both "address-data" and "addressbook-data" + (draft rev < 4) unless we parse the requested props appropriately. + + * SoObjects/Contacts/SOGoContactGCSFolder.m + (-davAddressbookMultiget): new method that responds the + CardDAV addressbook-multiget report. + (-davSQLFieldsTable): new overriden method that adds support for + the CardDAV address-data property. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-davCalendarMultiget:): make use of the new method below. + + * SoObjects/SOGo/SOGoGCSFolder.m + (-performMultigetInContext:inNamespace:): collection-type + independent form of -[SOGoAppointmentFolder davCalendarMultiget:], + moved along with all its private methods. + + * SoObjects/Contacts/SOGoContactFolders.m + (-toManyRelationshipKeys): new overriden method to return only the + personal addressbook when the request is performed by + AddressBook.app. + 2010-08-11 Francis Lachapelle * UI/WebServerResources/MailerUI.js (refreshMessage): if the diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 70a8e3aaf..4e436767e 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -81,6 +81,16 @@ #define defaultColor @"#AAAAAA" +@interface SOGoGCSFolder (SOGoPrivate) + +- (void) appendObject: (NSDictionary *) object + properties: (NSString **) properties + count: (unsigned int) propertiesCount + withBaseURL: (NSString *) baseURL + toBuffer: (NSMutableString *) r; + +@end + @implementation SOGoAppointmentFolder static NSNumber *sharedYes = nil; @@ -1065,27 +1075,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return ma; } -- (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: @""]; -} - #warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:) #warning components in calendar-data query are ignored -#warning the two following methods should be replaced with the new dav rendering mechanism - (NSString *) _nodeTagForProperty: (NSString *) property { NSString *namespace, *nodeName, *nsRep; @@ -1103,158 +1096,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir 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 -{ - SOGoCalendarComponent *sogoObject; - NSString **currentProperty; - NSString **values, **currentValue; - SEL methodSel; - -// NSLog (@"_properties:ofObject:: %@", [NSDate date]); - - values = NSZoneMalloc (NULL, - (propertiesCount + 1) * sizeof (NSString *)); - *(values + propertiesCount) = nil; - - //c = [self objectClassForComponentName: [object objectForKey: @"c_component"]]; - -#warning TODO: determine why this commented invocation takes so long... - // sogoObject = [self createChildComponentWithRecord: object]; - - sogoObject = [SOGoCalendarComponent objectWithRecord: object - inContainer: self]; - [sogoObject setComponentTag: [object objectForKey: @"c_component"]]; - - currentProperty = properties; - currentValue = values; - while (*currentProperty) - { - methodSel = SOGoSelectorForPropertyGetter (*currentProperty); - if (methodSel && [sogoObject respondsToSelector: methodSel]) - *currentValue = [[sogoObject 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; -} - -#warning We need to use the new DAV utilities here... -#warning this is baddddd because we return a single-valued dictionary containing \ - a cname which may not event exist... the logic behind appendObject:... should be \ - rethought, especially since we may start using SQL views - -- (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"]]; - [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"]; -} - - (NSCalendarDate *) _getMaxStartDate { NSCalendarDate *now, *rc; @@ -1688,211 +1529,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return r; } -- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls +- (WOResponse *) davCalendarMultiget: (WOContext *) queryContext { - unsigned int count, max; - NSString *url, *componentURLPath, *cName, *baseURLString; - NSMutableDictionary *cNames; - NSURL *componentURL, *baseURL; - NSArray *urlComponents; - - max = [urls count]; - cNames = [NSMutableDictionary dictionaryWithCapacity: max]; - baseURL = [self davURL]; - baseURLString = [self davURLAsString]; - - for (count = 0; count < max; count++) - { - url = [NSString stringWithFormat: @"%@/%@", - [[urls objectAtIndex: count] stringByDeletingLastPathComponent], - [[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]]; - componentURL = [[NSURL URLWithString: url relativeToURL: baseURL] - standardizedURL]; - componentURLPath = [componentURL absoluteString]; - if ([componentURLPath rangeOfString: baseURLString].location - != NSNotFound) - { - urlComponents = [componentURLPath componentsSeparatedByString: @"/"]; - cName = [[urls objectAtIndex: count] lastPathComponent]; - [cNames setObject: [urls objectAtIndex: count] forKey: cName]; - } - } - - return cNames; -} - -- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames - fields: (NSArray *) fields -{ - NSMutableString *filterString; - NSArray *records; - -// NSLog (@"fetchComponentsWithNames"); - filterString = [NSMutableString string]; - [filterString appendFormat: @"c_name='%@'", - [cNames componentsJoinedByString: @"' OR c_name='"]]; -// NSLog (@"fetchComponentsWithNames: query"); - records = [self bareFetchFields: fields - from: nil to: nil - title: nil - component: nil - additionalFilters: filterString]; -// NSLog (@"/fetchComponentsWithNames"); - - return records; -} - -#define maxQuerySize 2500 -#define baseQuerySize 160 -#define idQueryOverhead 13 - -- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames - fields: (NSArray *) fields -{ - NSMutableArray *components; - NSArray *records; - NSMutableArray *currentNames; - unsigned int count, max, currentSize, queryNameLength; - NSString *currentName; - -// NSLog (@"fetching components matching names"); - - currentNames = [NSMutableArray array]; - currentSize = baseQuerySize; - - max = [cNames count]; - components = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - currentName = [cNames objectAtIndex: count]; - queryNameLength = idQueryOverhead + [currentName length]; - if ((currentSize + queryNameLength) - > maxQuerySize) - { - records = [self _fetchComponentsWithNames: currentNames fields: fields]; - [components addObjectsFromArray: records]; - [currentNames removeAllObjects]; - currentSize = baseQuerySize; - } - [currentNames addObject: currentName]; - currentSize += queryNameLength; - } - - records = [self _fetchComponentsWithNames: currentNames fields: fields]; - [components addObjectsFromArray: records]; - -// NSLog (@"/fetching components matching names"); - - return components; -} - -- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls - fields: (NSArray *) fields -{ - NSMutableDictionary *components; - NSDictionary *cnames, *record; - NSString *recordURL; - NSArray *records; - unsigned int count, max; - - components = [NSMutableDictionary dictionary]; - - cnames = [self _deduceObjectNamesFromURLs: urls]; - records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys] - fields: fields]; - max = [records count]; - for (count = 0; count < max; count++) - { - record = [records objectAtIndex: count]; - recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]]; - if (recordURL) - [components setObject: record forKey: recordURL]; - } - - return components; -} - -- (void) _appendComponentProperties: (NSDictionary *) properties - matchingURLs: (id ) refs - toResponse: (WOResponse *) response -{ - NSObject *element; - NSDictionary *currentComponent, *components; - NSString *currentURL, *baseURL, *currentField; - NSString **propertiesArray; - NSMutableArray *urls, *fields; - NSMutableString *buffer; - unsigned int count, max, propertiesCount; - NSEnumerator *addFields; - - baseURL = [self davURLAsString]; -#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 - if (![baseURL hasSuffix: @"/"]) - baseURL = [NSString stringWithFormat: @"%@/", baseURL]; - - urls = [NSMutableArray array]; - max = [refs length]; - for (count = 0; count < max; count++) - { - element = [refs objectAtIndex: count]; - currentURL = [[element firstChild] nodeValue]; - [urls addObject: currentURL]; - } - - propertiesArray = [[properties allKeys] asPointersOfObjects]; - propertiesCount = [properties count]; - - fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil]; - addFields = [[properties allValues] objectEnumerator]; - while ((currentField = [addFields nextObject])) - if ([currentField length]) - [fields addObjectUniquely: currentField]; - - components = [self _fetchComponentsMatchingURLs: urls fields: fields]; - max = [urls count]; -// NSLog (@"adding properties with url"); - buffer = [NSMutableString stringWithCapacity: max*512]; - for (count = 0; count < max; count++) - { - currentComponent = [components objectForKey: [urls objectAtIndex: count]]; - if (currentComponent) - [self appendObject: currentComponent - properties: propertiesArray - count: propertiesCount - withBaseURL: baseURL - toBuffer: buffer]; - else - [self appendMissingObjectRef: currentURL - toBuffer: buffer]; - } - [response appendContentString: buffer]; -// NSLog (@"/adding properties with url"); - - NSZoneFree (NULL, propertiesArray); -} - -- (id) davCalendarMultiget: (id) queryContext -{ - WOResponse *r; - id document; - DOMElement *documentElement, *propElement; - - r = [context response]; - [r prepareDAVResponse]; - [r appendContentString: @""]; - - document = [[context request] contentAsDOMDocument]; - documentElement = (DOMElement *) [document documentElement]; - propElement = [documentElement firstElementWithTag: @"prop" - inNamespace: @"DAV:"]; - - [self _appendComponentProperties: [self parseDAVRequestedProperties: propElement] - matchingURLs: [documentElement getElementsByTagName: @"href"] - toResponse: r]; - [r appendContentString:@""]; - - return r; + return [self performMultigetInContext: queryContext + inNamespace: @"urn:ietf:params:xml:ns:caldav"]; } - (NSString *) additionalWebdavSyncFilters diff --git a/SoObjects/Contacts/SOGoContactFolder.h b/SoObjects/Contacts/SOGoContactFolder.h index dfcb4287f..00a52456f 100644 --- a/SoObjects/Contacts/SOGoContactFolder.h +++ b/SoObjects/Contacts/SOGoContactFolder.h @@ -42,10 +42,6 @@ @protocol SOGoContactFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r; - - (NSArray *) lookupContactsWithFilter: (NSString *) filter sortBy: (NSString *) sortKey ordering: (NSComparisonResult) sortOrdering; diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index 98197dfa6..6c791ba5b 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -35,6 +35,7 @@ #import #import #import +#import #import "SOGoContactGCSFolder.h" #import "SOGoContactSourceFolder.h" @@ -87,4 +88,16 @@ return [self labelForKey: @"Personal Address Book"]; } +- (NSArray *) toManyRelationshipKeys +{ + NSMutableArray *keys; + + if ([[context request] isAddressBookApp]) + keys = [NSMutableArray arrayWithObject: @"personal"]; + else + keys = (NSMutableArray *) [super toManyRelationshipKeys]; + + return keys; +} + @end diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 1fa3f8f9e..8a275d748 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -31,15 +32,23 @@ #import #import #import -#import +#import +#import +#import +#import #import #import + +#import #import +#import #import #import #import #import +#import +#import #import "SOGoContactGCSEntry.h" #import "SOGoContactGCSList.h" @@ -84,7 +93,7 @@ static NSArray *folderListingFields = nil; - (Class) objectClassForComponentName: (NSString *) componentName { Class objectClass; - + if ([componentName isEqualToString: @"vcard"]) objectClass = [SOGoContactGCSEntry class]; else if ([componentName isEqualToString: @"vlist"]) @@ -289,38 +298,17 @@ static NSArray *folderListingFields = nil; return records; } -#warning this should be unified within SOGoFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r +- (NSDictionary *) davSQLFieldsTable { - SOGoContactGCSEntry *component; - NSString *name, *etagLine, *contactString; + static NSMutableDictionary *davSQLFieldsTable = nil; - name = [object objectForKey: @"c_name"]; - component = [self lookupName: name inContext: context acquire: NO]; + if (!davSQLFieldsTable) + { + davSQLFieldsTable = [[super davSQLFieldsTable] mutableCopy]; + [davSQLFieldsTable setObject: @"c_content" forKey: @"{urn:ietf:params:xml:ns:carddav}address-data"]; + } - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - [r appendContentString: baseURL]; - if (![baseURL hasSuffix: @"/"]) - [r appendContentString: @"/"]; - [r appendContentString: name]; - [r appendContentString: @"\r\n"]; - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" \r\n"]; - etagLine = [NSString stringWithFormat: @" %@\r\n", - [component davEntityTag]]; - [r appendContentString: etagLine]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" HTTP/1.1 200 OK\r\n"]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - contactString = [[component contentAsString] stringByEscapingXMLString]; - [r appendContentString: contactString]; - [r appendContentString: @"\r\n"]; - [r appendContentString: @" \r\n"]; + return davSQLFieldsTable; } - (NSArray *) davComplianceClassesInContext: (id)_ctx @@ -359,6 +347,12 @@ static NSArray *folderListingFields = nil; return resourceType; } +- (id) davAddressbookMultiget: (id) queryContext +{ + return [self performMultigetInContext: queryContext + inNamespace: @"urn:ietf:params:xml:ns:carddav"]; +} + /* sorting */ - (NSComparisonResult) compare: (id) otherFolder { @@ -385,4 +379,22 @@ static NSArray *folderListingFields = nil; return @"IPF.Contact"; } +/* 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]; +} + @end /* SOGoContactGCSFolder */ diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index c33fff60c..214bafe06 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -34,7 +34,6 @@ #import #import #import -#import #import #import @@ -50,49 +49,6 @@ @implementation SOGoContactSourceFolder -#warning this should be unified within SOGoFolder -- (void) appendObject: (NSDictionary *) object - withBaseURL: (NSString *) baseURL - toREPORTResponse: (WOResponse *) r -{ - SOGoContactLDIFEntry *component; - NSString *name, *etagLine, *contactString; - - name = [object objectForKey: @"c_name"]; - if ([name length]) - { - component = [self lookupName: name inContext: context acquire: NO]; - - if ([component isKindOfClass: [NSException class]]) - { - [self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name]; - return; - } - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - [r appendContentString: baseURL]; - if (![baseURL hasSuffix: @"/"]) - [r appendContentString: @"/"]; - [r appendContentString: name]; - [r appendContentString: @"\r\n"]; - - [r appendContentString: @" \r\n"]; - [r appendContentString: @" \r\n"]; - etagLine = [NSString stringWithFormat: @" %@\r\n", - [component davEntityTag]]; - [r appendContentString: etagLine]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" HTTP/1.1 200 OK\r\n"]; - [r appendContentString: @" \r\n"]; - [r appendContentString: @" "]; - contactString = [[component contentAsString] stringByEscapingXMLString]; - [r appendContentString: contactString]; - [r appendContentString: @"\r\n"]; - [r appendContentString: @" \r\n"]; - } -} - + (id) folderWithName: (NSString *) aName andDisplayName: (NSString *) aDisplayName inContainer: (id) aContainer diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index 8ad51dc28..26494ba8e 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -39,6 +40,50 @@ @implementation SOGoFolder (CardDAV) +- (void) _appendObject: (NSDictionary *) object + withBaseURL: (NSString *) baseURL + toREPORTResponse: (WOResponse *) r +{ + id component; + NSString *name, *etagLine, *contactString; + + name = [object objectForKey: @"c_name"]; + if ([name length]) + { + component = [self lookupName: name inContext: context acquire: NO]; + if ([component isKindOfClass: [NSException class]]) + { + [self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name]; + return; + } + +#warning we provide both "address-data" and "addressbook-data" for compatibility reasons, we should actually check which one has been queried + [r appendContentString: @"" + @""]; + [r appendContentString: baseURL]; + if (![baseURL hasSuffix: @"/"]) + [r appendContentString: @"/"]; + [r appendContentString: name]; + [r appendContentString: @"" + @"" + @""]; + etagLine = [NSString stringWithFormat: @"%@", + [component davEntityTag]]; + [r appendContentString: etagLine]; + [r appendContentString: @"" + @"HTTP/1.1 200 OK" + @"" + @""]; + contactString = [[component contentAsString] stringByEscapingXMLString]; + [r appendContentString: contactString]; + [r appendContentString: @"" + @""]; + [r appendContentString: contactString]; + [r appendContentString: @"" + @""]; + } +} + - (void) _appendComponentsMatchingFilters: (NSArray *) filters toResponse: (WOResponse *) response context: (id) localContext @@ -60,9 +105,8 @@ objectEnumerator]; while ((contact = [contacts nextObject])) - [(id)self appendObject: contact - withBaseURL: baseURL - toREPORTResponse: response]; + [self _appendObject: contact withBaseURL: baseURL + toREPORTResponse: response]; } } @@ -143,7 +187,7 @@ [self _appendComponentsMatchingFilters: filters toResponse: r context: queryContext]; - [r appendContentString:@""]; + [r appendContentString: @""]; return r; } diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index eca340d24..7079c4991 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -121,6 +121,10 @@ - (NSString *) davCollectionTag; +/* multiget helper */ +- (WOResponse *) performMultigetInContext: (WOContext *) queryContext + inNamespace: (NSString *) namespace; + @end #endif /* __SOGo_SOGoGCSFolder_H__ */ diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 3f8388008..82df98235 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -1685,4 +1685,392 @@ static NSArray *childRecordFields = nil; [_ms appendFormat:@" ocs=%@", [self ocsPath]]; } +/* tmp: multiget */ + +- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames + fields: (NSArray *) fields +{ + NSArray *records; + NSString *sqlFilter; + NSMutableString *filterString; + EOQualifier *qualifier; + + sqlFilter = [self aclSQLListingFilter]; + if (sqlFilter) + { + filterString = [NSMutableString stringWithCapacity: 8192]; + [filterString appendFormat: @"(c_name='%@')", + [cNames componentsJoinedByString: @"' OR c_name='"]]; + if ([sqlFilter length] > 0) + [filterString appendFormat: @" AND (%@)", sqlFilter]; + qualifier = [EOQualifier qualifierWithQualifierFormat: filterString]; + records = [[self ocsFolder] fetchFields: fields + matchingQualifier: qualifier]; + if (![records isNotNull]) + { + [self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__]; + return nil; + } + } + else + records = [NSArray array]; + + return records; +} + +#define maxQuerySize 2500 +#define baseQuerySize 160 +#define idQueryOverhead 13 + +- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames + fields: (NSArray *) fields +{ + NSMutableArray *components; + NSArray *records; + NSMutableArray *currentNames; + unsigned int count, max, currentSize, queryNameLength; + NSString *currentName; + +// NSLog (@"fetching components matching names"); + + currentNames = [NSMutableArray array]; + currentSize = baseQuerySize; + + max = [cNames count]; + components = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + currentName = [cNames objectAtIndex: count]; + queryNameLength = idQueryOverhead + [currentName length]; + if ((currentSize + queryNameLength) + > maxQuerySize) + { + records = [self _fetchComponentsWithNames: currentNames fields: fields]; + [components addObjectsFromArray: records]; + [currentNames removeAllObjects]; + currentSize = baseQuerySize; + } + [currentNames addObject: currentName]; + currentSize += queryNameLength; + } + + records = [self _fetchComponentsWithNames: currentNames fields: fields]; + [components addObjectsFromArray: records]; + +// NSLog (@"/fetching components matching names"); + + return components; +} + +- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls +{ + unsigned int count, max; + NSString *url, *componentURLPath, *cName, *baseURLString; + NSMutableDictionary *cNames; + NSURL *componentURL, *baseURL; + NSArray *urlComponents; + + max = [urls count]; + cNames = [NSMutableDictionary dictionaryWithCapacity: max]; + baseURL = [self davURL]; + baseURLString = [self davURLAsString]; + + for (count = 0; count < max; count++) + { + url = [NSString stringWithFormat: @"%@/%@", + [[urls objectAtIndex: count] stringByDeletingLastPathComponent], + [[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]]; + componentURL = [[NSURL URLWithString: url relativeToURL: baseURL] + standardizedURL]; + componentURLPath = [componentURL absoluteString]; + if ([componentURLPath rangeOfString: baseURLString].location + != NSNotFound) + { + urlComponents = [componentURLPath componentsSeparatedByString: @"/"]; + cName = [[urls objectAtIndex: count] lastPathComponent]; + [cNames setObject: [urls objectAtIndex: count] forKey: cName]; + } + } + + return cNames; +} + +- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls + fields: (NSArray *) fields +{ + NSMutableDictionary *components; + NSDictionary *cnames, *record; + NSString *recordURL; + NSArray *records; + unsigned int count, max; + + components = [NSMutableDictionary dictionary]; + + cnames = [self _deduceObjectNamesFromURLs: urls]; + records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys] + fields: fields]; + max = [records count]; + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]]; + if (recordURL) + [components setObject: record forKey: recordURL]; + } + + return components; +} + +#warning the two following methods should be replaced with the new dav rendering mechanism +- (NSString *) _nodeTagForProperty: (NSString *) property +{ + [self subclassResponsibility: _cmd]; + return nil; +} + +- (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 +{ + SOGoContentObject *sogoObject; + NSString **currentProperty; + NSString **values, **currentValue; + SEL methodSel; + +// NSLog (@"_properties:ofObject:: %@", [NSDate date]); + + values = NSZoneMalloc (NULL, + (propertiesCount + 1) * sizeof (NSString *)); + *(values + propertiesCount) = nil; + + //c = [self objectClassForComponentName: [object objectForKey: @"c_component"]]; + + sogoObject = [self createChildComponentWithRecord: object]; + currentProperty = properties; + currentValue = values; + while (*currentProperty) + { + methodSel = SOGoSelectorForPropertyGetter (*currentProperty); + if (methodSel && [sogoObject respondsToSelector: methodSel]) + *currentValue = [[sogoObject 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: @""]; +} + +#warning We need to use the new DAV utilities here... +#warning this is baddddd because we return a single-valued dictionary containing \ + a cname which may not event exist... the logic behind appendObject:... should be \ + rethought, especially since we may start using SQL views + +- (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"]]; + [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: (NSDictionary *) properties + matchingURLs: (id ) refs + toResponse: (WOResponse *) response +{ + NSObject *element; + NSDictionary *currentComponent, *components; + NSString *currentURL, *baseURL, *currentField; + NSString **propertiesArray; + NSMutableArray *urls, *fields; + NSMutableString *buffer; + unsigned int count, max, propertiesCount; + NSEnumerator *addFields; + + baseURL = [self davURLAsString]; +#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 + if (![baseURL hasSuffix: @"/"]) + baseURL = [NSString stringWithFormat: @"%@/", baseURL]; + + urls = [NSMutableArray array]; + max = [refs length]; + for (count = 0; count < max; count++) + { + element = [refs objectAtIndex: count]; + currentURL = [[element firstChild] nodeValue]; + [urls addObject: currentURL]; + } + + propertiesArray = [[properties allKeys] asPointersOfObjects]; + propertiesCount = [properties count]; + + fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil]; + addFields = [[properties allValues] objectEnumerator]; + while ((currentField = [addFields nextObject])) + if ([currentField length]) + [fields addObjectUniquely: currentField]; + + components = [self _fetchComponentsMatchingURLs: urls fields: fields]; + max = [urls count]; +// NSLog (@"adding properties with url"); + buffer = [NSMutableString stringWithCapacity: max*512]; + for (count = 0; count < max; count++) + { + currentComponent = [components objectForKey: [urls objectAtIndex: count]]; + if (currentComponent) + [self appendObject: currentComponent + properties: propertiesArray + count: propertiesCount + withBaseURL: baseURL + toBuffer: buffer]; + else + [self appendMissingObjectRef: currentURL + 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: [self parseDAVRequestedProperties: propElement] + matchingURLs: [documentElement getElementsByTagName: @"href"] + toResponse: r]; + [r appendContentString:@""]; + + return r; +} + @end /* SOGoFolder */