+2005-06-26 Helge Hess <helge.hess@opengroupware.org>
+
+ * v4.5.167
+
+ * WebDAV/SoWebDAVRenderer.m: improved reliability by checking the class
+ of OPTIONS method results, deprecated array results
+
+ * WebDAV/SoObjectWebDAVDispatcher.m: when receiving an OPTIONS request,
+ the dispatcher will try to invoke a method with the same name on the
+ object. If none is available, the dispatcher checks supported methods
+ and DAV compliance classes
+
+ * WebDAV/SoObject+SoDAV.m: added method to determine the WebDAV
+ compliance classes supported by an object
+ (davComplianceClassesInContext:). The method now only returns class 2
+ if the object returns a lock manager object. Also moved the 'allowed'
+ processing to the object (-davAllowedMethodsInContext: method)
+
2005-06-24 Helge Hess <helge.hess@opengroupware.org>
* SoObjects/SoProductRegistry.m: fixed product lookup on MacOSX with
# version file
-SUBMINOR_VERSION:=166
+SUBMINOR_VERSION:=167
# v4.5.122 requires libNGExtensions v4.5.153
# v4.5.91 requires libNGExtensions v4.5.134
- (NSArray *)davQueryOnSelf:(EOFetchSpecification *)_fs inContext:(id)_ctx;
- (NSArray *)defaultWebDAVPropertyNamesInContext:(id)_ctx;
+- (NSArray *)davComplianceClassesInContext:(id)_ctx;
+- (NSArray *)davAllowedMethodsInContext:(id)_ctx;
/*
Editing the object properties (PROPPATCH)
return defNames;
}
+- (NSArray *)davComplianceClassesInContext:(id)_ctx {
+ /*
+ Class 1 is everything in WebDAV which is a MUST.
+
+ Class 2 adds the LOCK method, the supportedlock property, the lockdiscovery
+ property, the time-out response header and the lock-token request header.
+
+ In this method we check that by querying the lock manager. If the object
+ has one, it will return class 2, otherwise just class 1.
+ */
+ static NSArray *class1 = nil, *class2 = nil;
+
+ if (class1 == nil)
+ class1 = [[NSArray alloc] initWithObjects:@"1", nil];
+ if (class2 == nil)
+ class2 = [[NSArray alloc] initWithObjects:@"1", @"2", nil];
+
+ return ([self davLockManagerInContext:_ctx] != nil)
+ ? class2 : class1;
+}
+
+- (NSArray *)davAllowedMethodsInContext:(id)_ctx {
+ static NSArray *defMethods = nil;
+ NSMutableArray *allow;
+
+ if (defMethods == nil) {
+ defMethods = [[[NSUserDefaults standardUserDefaults]
+ arrayForKey:@"SoWebDAVDefaultAllowMethods"]
+ copy];
+ }
+
+ allow = [NSMutableArray arrayWithCapacity:16];
+ if (defMethods) [allow addObjectsFromArray:defMethods];
+
+ if ([self respondsToSelector:@selector(performWebDAVQuery:inContext:)]) {
+ [allow addObject:@"PROPFIND"];
+ [allow addObject:@"SEARCH"];
+ }
+ if ([self respondsToSelector:
+ @selector(davSetProperties:removePropertiesNamed:)])
+ [allow addObject:@"PROPPATCH"];
+
+ return allow;
+}
+
/* attributes */
- (BOOL)davIsCollection {
ctx = [[WOApplication application] context];
i = 0;
e = [self davChildKeysInContext:ctx];
- while ((childName = [e nextObject])) {
- if (![[self lookupName:childName inContext:ctx acquire:NO] davIsCollection])
+ while ((childName = [e nextObject]) != nil) {
+ if (![[self lookupName:childName inContext:ctx acquire:NO]davIsCollection])
i++;
}
return i;
userInfo:ui];
}
-- (NSArray *)allowedMethods {
- static NSArray *defMethods = nil;
- NSMutableArray *allow;
-
- if (defMethods == nil) {
- defMethods = [[[NSUserDefaults standardUserDefaults]
- arrayForKey:@"SoWebDAVDefaultAllowMethods"]
- copy];
- }
-
- allow = [NSMutableArray arrayWithCapacity:16];
- if (defMethods) [allow addObjectsFromArray:defMethods];
-
- if ([self->object
- respondsToSelector:@selector(performWebDAVQuery:inContext:)]) {
- [allow addObject:@"PROPFIND"];
- [allow addObject:@"SEARCH"];
- }
- if ([self->object respondsToSelector:
- @selector(davSetProperties:removePropertiesNamed:)])
- [allow addObject:@"PROPPATCH"];
-
- return allow;
-}
-
- (NSString *)baseURLForContext:(WOContext *)_ctx {
/*
Note: Evolution doesn't correctly transfer the "Host:" header, it
/* core HTTP methods */
-- (id)doGET:(WOContext *)_ctx {
+- (id)_callObjectMethod:(NSString *)_method inContext:(WOContext *)_ctx {
+ /* returns 'nil' if the object had no such method */
NSException *e;
id methodObject;
+ id result;
+ methodObject =
+ [self->object lookupName:_method inContext:_ctx acquire:NO];
+ if (![methodObject isNotNull])
+ return nil;
+ if ([methodObject isKindOfClass:[NSException class]])
+ return methodObject;
+ if ((e = [self->object validateName:_method inContext:_ctx]) != nil)
+ return e;
- if ((methodObject =
- [self->object lookupName:@"GET" inContext:_ctx acquire:NO]) == nil)
+ if ([methodObject respondsToSelector:
+ @selector(takeValuesFromRequest:inContext:)])
+ [methodObject takeValuesFromRequest:[_ctx request] inContext:_ctx];
+
+ result = [methodObject callOnObject:self->object inContext:_ctx];
+ return (result != nil) ? result : [NSNull null];
+}
+
+- (id)doGET:(WOContext *)_ctx {
+ NSException *e;
+ id methodObject;
+
+ methodObject = [self->object lookupName:@"GET" inContext:_ctx acquire:NO];
+ if (methodObject == nil)
methodObject = [self->object lookupDefaultMethod];
else {
- if ((e = [self->object validateName:@"GET" inContext:_ctx]))
+ if ((e = [self->object validateName:@"GET" inContext:_ctx]) != nil)
return e;
}
}
- (id)doOPTIONS:(WOContext *)_ctx {
- return [self allowedMethods];
+ WOResponse *response;
+ NSArray *tmp;
+ id result;
+
+ if ((result = [self _callObjectMethod:@"OPTIONS" inContext:_ctx]) != nil)
+ return result;
+
+ response = [_ctx response];
+ [response setStatus:200 /* OK */];
+
+ if ((tmp = [self->object davAllowedMethodsInContext:_ctx]) != nil)
+ [response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"allow"];
+
+ if ((tmp = [self->object davComplianceClassesInContext:_ctx]) != nil)
+ [response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"dav"];
+
+ return response;
}
- (id)doHEAD:(WOContext *)_ctx {
}
- (BOOL)renderOptions:(id)_object inContext:(WOContext *)_ctx {
- WOResponse *r = [_ctx response];
+ WOResponse *r;
- [r setStatus:200];
+ r = [_ctx response];
+ [r setStatus:200 /* OK */];
[r setHeader:@"1,2" forKey:@"DAV"]; // TODO: select protocol level
//[r setHeader:@"" forKey:@"Etag"];
- [r setHeader:[_object componentsJoinedByString:@", "] forKey:@"allow"];
+
+ if (![_object isNotNull])
+ ;
+ else if ([_object isKindOfClass:[NSArray class]]) {
+ /* DEPRECATED */
+ [r setHeader:[_object componentsJoinedByString:@", "] forKey:@"allow"];
+ }
+ else {
+ [self logWithFormat:@"ERROR: unexpected options result: %@ (class=%@)",
+ _object, [_object class]];
+ }
return YES;
}