diff --git a/ChangeLog b/ChangeLog index 3e23ec57f..4a1d197cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2008-07-10 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoUserFolder+Appointments.m + ([SOGoUserFolder -davPrincipalPropertySearch:queryContext]): new + method to answer to "principal-property-search" report. + + * SoObjects/Appointments/SOGoAppointmentFolders.m + ([SOGoAppointmentFolders -davComplianceClassesInContext:]): new + method that declares "calendar-access" and "calendar-schedule". + * SoObjects/Appointments/SOGoCalendarComponent.m ([SOGoCalendarComponent -sendIMIPReplyForEvent:eventto:recipient]): new method, derived from diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index c12ab6135..2e64e67c5 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -151,35 +151,22 @@ } #warning THIS CAUSES LIGHTNING TO FAIL (that is why its commented out) -// - (NSArray *) davComplianceClassesInContext: (id)_ctx -// { -// NSMutableArray *classes; -// NSArray *primaryClasses; +- (NSArray *) davComplianceClassesInContext: (id)_ctx +{ + NSMutableArray *classes; + NSArray *primaryClasses; -// classes = [NSMutableArray new]; -// [classes autorelease]; + classes = [NSMutableArray new]; + [classes autorelease]; -// primaryClasses = [super davComplianceClassesInContext: _ctx]; -// if (primaryClasses) -// [classes addObjectsFromArray: primaryClasses]; -// [classes addObject: @"calendar-access"]; -// [classes addObject: @"calendar-schedule"]; + primaryClasses = [super davComplianceClassesInContext: _ctx]; + if (primaryClasses) + [classes addObjectsFromArray: primaryClasses]; + [classes addObject: @"calendar-access"]; + [classes addObject: @"calendar-schedule"]; -// return classes; -// } - -// /* CalDAV support */ -// - (NSArray *) davComplianceClassesInContext: (WOContext *) localContext -// { -// NSMutableArray *newClasses; - -// newClasses -// = [NSMutableArray arrayWithArray: -// [super davComplianceClassesInContext: localContext]]; -// [newClasses addObject: @"calendar-access"]; - -// return newClasses; -// } + return classes; +} // - (NSArray *) davCalendarHomeSet // { diff --git a/SoObjects/Appointments/SOGoUserFolder+Appointments.m b/SoObjects/Appointments/SOGoUserFolder+Appointments.m index 8c2bea876..5d3ef1e07 100644 --- a/SoObjects/Appointments/SOGoUserFolder+Appointments.m +++ b/SoObjects/Appointments/SOGoUserFolder+Appointments.m @@ -21,11 +21,18 @@ */ #import +#import #import #import +#import +#import +#import +#import #import +#import +#import #import "SOGoUserFolder+Appointments.h" @@ -135,4 +142,311 @@ return [NSArray arrayWithObject: tag]; } +- (WOResponse *) _prepareResponseFromContext: (WOContext *) queryContext +{ + WOResponse *r; + + r = [queryContext response]; + [r setStatus: 207]; + [r setContentEncoding: NSUTF8StringEncoding]; + [r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"]; + [r setHeader: @"no-cache" forKey: @"pragma"]; + [r setHeader: @"no-cache" forKey: @"cache-control"]; + [r appendContentString:@"\r\n"]; + + return r; +} + +- (void) _fillMatches: (NSMutableDictionary *) matches + fromElement: (NSObject *) searchElement +{ + NSObject *list; + NSObject *valueNode; + NSArray *elements; + NSString *property, *match; + + list = [searchElement getElementsByTagName: @"prop"]; + if ([list length]) + { + elements = [self domNode: [list objectAtIndex: 0] + getChildNodesByType: DOM_ELEMENT_NODE]; + if ([elements count]) + { + valueNode = [elements objectAtIndex: 0]; + property = [NSString stringWithFormat: @"{%@}%@", + [valueNode namespaceURI], + [valueNode nodeName]]; + } + } + list = [searchElement getElementsByTagName: @"match"]; + if ([list length]) + { + valueNode = [[list objectAtIndex: 0] firstChild]; + match = [valueNode nodeValue]; + } + + [matches setObject: match forKey: property]; +} + +- (void) _fillProperties: (NSMutableArray *) properties + fromElement: (NSObject *) propElement +{ + NSEnumerator *elements; + NSObject *propNode; + NSString *property; + + elements = [[self domNode: propElement + getChildNodesByType: DOM_ELEMENT_NODE] + objectEnumerator]; + while ((propNode = [elements nextObject])) + { + property = [NSString stringWithFormat: @"{%@}%@", + [propNode namespaceURI], + [propNode nodeName]]; + [properties addObject: property]; + } +} + +- (void) _fillPrincipalMatches: (NSMutableDictionary *) matches + andProperties: (NSMutableArray *) properties + fromElement: (NSObject *) documentElement +{ + NSEnumerator *children; + NSObject *currentElement; + NSString *tag; + + children = [[self domNode: documentElement + getChildNodesByType: DOM_ELEMENT_NODE] + objectEnumerator]; + while ((currentElement = [children nextObject])) + { + tag = [currentElement tagName]; + if ([tag isEqualToString: @"property-search"]) + [self _fillMatches: matches fromElement: currentElement]; + else if ([tag isEqualToString: @"prop"]) + [self _fillProperties: properties fromElement: currentElement]; + else + [self errorWithFormat: @"principal-property-search: unknown tag '%@'", + tag]; + } +} + +#warning this is a bit ugly, as usual +- (void) _fillCollections: (NSMutableArray *) collections + withCalendarHomeSetMatching: (NSString *) value +{ + SOGoUserFolder *collection; + NSRange substringRange; + + substringRange = [value rangeOfString: @"/SOGo/dav/"]; + value = [value substringFromIndex: NSMaxRange (substringRange)]; + substringRange = [value rangeOfString: @"/Calendar"]; + value = [value substringToIndex: substringRange.location]; + collection = [[SOGoUser userWithLogin: value roles: nil] + homeFolderInContext: context]; + if (collection) + [collections addObject: collection]; +} + +- (NSMutableArray *) _firstPrincipalCollectionsWhere: (NSString *) key + matches: (NSString *) value +{ + NSMutableArray *collections; + + collections = [NSMutableArray array]; + if ([key + isEqualToString: @"{urn:ietf:params:xml:ns:caldav}calendar-home-set"]) + [self _fillCollections: collections withCalendarHomeSetMatching: value]; + else + [self errorWithFormat: @"principal-property-search: unhandled key '%@'", + key]; + + return collections; +} + +#warning unused stub +- (BOOL) collectionDavKey: (NSString *) key + matches: (NSString *) value +{ + return YES; +} + +- (void) _principalCollections: (NSMutableArray **) baseCollections + where: (NSString *) key + matches: (NSString *) value +{ + SOGoUserFolder *currentCollection; + unsigned int count, max; + + if (!*baseCollections) + *baseCollections = [self _firstPrincipalCollectionsWhere: key + matches: value]; + else + { + max = [*baseCollections count]; + for (count = max; count > 0; count--) + { + currentCollection = [*baseCollections objectAtIndex: count - 1]; + if (![currentCollection collectionDavKey: key matches: value]) + [*baseCollections removeObjectAtIndex: count - 1]; + } + } +} + +- (NSArray *) _principalCollectionsMatching: (NSDictionary *) matches +{ + NSMutableArray *collections; + NSEnumerator *allKeys; + NSString *currentKey; + + collections = nil; + + allKeys = [[matches allKeys] objectEnumerator]; + while ((currentKey = [allKeys nextObject])) + [self _principalCollections: &collections + where: currentKey + matches: [matches objectForKey: currentKey]]; + + return collections; +} + +/* + + + + + doE + + + + + + Sales + + + + + + + + + */ + +- (void) _appendProperties: (NSArray *) properties + ofCollection: (SOGoUserFolder *) collection + toResponses: (NSMutableArray *) responses +{ + unsigned int count, max; + SEL methodSel; + NSString *currentProperty; + id currentValue; + NSMutableArray *response, *props; + NSDictionary *keyTuple; + + response = [NSMutableArray new]; + [response addObject: davElementWithContent (@"href", XMLNS_WEBDAV, + [collection davURL])]; + props = [NSMutableArray new]; + max = [properties count]; + for (count = 0; count < max; count++) + { + currentProperty = [properties objectAtIndex: count]; + methodSel = SOGoSelectorForPropertyGetter (currentProperty); + if (methodSel && [collection respondsToSelector: methodSel]) + { + currentValue = [collection performSelector: methodSel]; + #warning evil eVIL EVIl! + if ([currentValue isKindOfClass: [NSArray class]]) + { + currentValue = [currentValue objectAtIndex: 0]; + currentValue + = davElementWithContent ([currentValue objectAtIndex: 0], + [currentValue objectAtIndex: 1], + [currentValue objectAtIndex: 3]); + } + keyTuple = [currentProperty asWebDAVTuple]; + [props addObject: davElementWithContent ([keyTuple objectForKey: @"method"], + [keyTuple objectForKey: @"ns"], + currentValue)]; + } + } + [response addObject: davElementWithContent (@"propstat", XMLNS_WEBDAV, + davElementWithContent + (@"prop", XMLNS_WEBDAV, + props))]; + [responses addObject: davElementWithContent (@"response", XMLNS_WEBDAV, + response)]; + [props release]; + [response release]; +} + +- (void) _appendProperties: (NSArray *) properties + ofCollections: (NSArray *) collections + toResponse: (WOResponse *) response +{ + NSDictionary *mStatus; + NSMutableArray *responses; + unsigned int count, max; + + responses = [NSMutableArray new]; + + max = [collections count]; + for (count = 0; count < max; count++) + [self _appendProperties: properties + ofCollection: [collections objectAtIndex: count] + toResponses: responses]; + mStatus = davElementWithContent (@"multistatus", XMLNS_WEBDAV, responses); + [response appendContentString: [mStatus asWebDavStringWithNamespaces: nil]]; + [responses release]; +} + +- (WOResponse *) davPrincipalPropertySearch: (WOContext *) queryContext +{ + NSObject *document; + NSObject *documentElement; + NSArray *collections; + NSMutableDictionary *matches; + NSMutableArray *properties; + WOResponse *r; + + document = [[context request] contentAsDOMDocument]; + documentElement = [document documentElement]; + + matches = [NSMutableDictionary new]; + properties = [NSMutableArray new]; + [self _fillPrincipalMatches: matches andProperties: properties + fromElement: documentElement]; + collections = [self _principalCollectionsMatching: matches]; + r = [self _prepareResponseFromContext: queryContext]; + [self _appendProperties: properties ofCollections: collections + toResponse: r]; + [properties release]; + [matches release]; + + return r; +// @"/SOGo/dav/wsourdeau/" +// @"" +// @"HTTP/1.1 200 OK" +// @"" +// @"/SOGo/dav/wsourdeau/Calendar/" +// @"MAILTO:wsourdeau@inverse.ca" +// @"/SOGo/dav/wsourdeau/Calendar/personal/" +// @"/SOGo/dav/wsourdeau/Calendar/personal/" +// @"" +// @""]; + +// +// +// +// +// /SOGo/dav/wsourdeau/Calendar +// +// +// +// +// +// +// +} + @end