From 170a5b914a8956eee17cc7b805fa3d63afcb450d Mon Sep 17 00:00:00 2001 From: helge Date: Sun, 26 Jun 2005 12:19:27 +0000 Subject: [PATCH] improved WebDAV layer git-svn-id: http://svn.opengroupware.org/SOPE/trunk@865 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-appserver/NGObjWeb/ChangeLog | 18 +++++ sope-appserver/NGObjWeb/Version | 2 +- .../NGObjWeb/WebDAV/SoObject+SoDAV.h | 2 + .../NGObjWeb/WebDAV/SoObject+SoDAV.m | 49 ++++++++++++- .../WebDAV/SoObjectWebDAVDispatcher.m | 73 +++++++++++-------- .../NGObjWeb/WebDAV/SoWebDAVRenderer.m | 17 ++++- 6 files changed, 125 insertions(+), 36 deletions(-) diff --git a/sope-appserver/NGObjWeb/ChangeLog b/sope-appserver/NGObjWeb/ChangeLog index b4586d9e..19247a24 100644 --- a/sope-appserver/NGObjWeb/ChangeLog +++ b/sope-appserver/NGObjWeb/ChangeLog @@ -1,3 +1,21 @@ +2005-06-26 Helge Hess + + * 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 * SoObjects/SoProductRegistry.m: fixed product lookup on MacOSX with diff --git a/sope-appserver/NGObjWeb/Version b/sope-appserver/NGObjWeb/Version index f1dd79df..ad9ca6c9 100644 --- a/sope-appserver/NGObjWeb/Version +++ b/sope-appserver/NGObjWeb/Version @@ -1,6 +1,6 @@ # version file -SUBMINOR_VERSION:=166 +SUBMINOR_VERSION:=167 # v4.5.122 requires libNGExtensions v4.5.153 # v4.5.91 requires libNGExtensions v4.5.134 diff --git a/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h b/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h index 5f7504f9..98ef1922 100644 --- a/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h +++ b/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.h @@ -49,6 +49,8 @@ - (NSArray *)davQueryOnSelf:(EOFetchSpecification *)_fs inContext:(id)_ctx; - (NSArray *)defaultWebDAVPropertyNamesInContext:(id)_ctx; +- (NSArray *)davComplianceClassesInContext:(id)_ctx; +- (NSArray *)davAllowedMethodsInContext:(id)_ctx; /* Editing the object properties (PROPPATCH) diff --git a/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.m b/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.m index 4c663685..38ee46b8 100644 --- a/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.m +++ b/sope-appserver/NGObjWeb/WebDAV/SoObject+SoDAV.m @@ -59,6 +59,51 @@ 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 { @@ -124,8 +169,8 @@ 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; diff --git a/sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m b/sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m index 1d716a36..80c157f5 100644 --- a/sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m +++ b/sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m @@ -105,31 +105,6 @@ static NSTimeZone *gmt = nil; 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 @@ -175,16 +150,38 @@ static NSTimeZone *gmt = nil; /* 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; } @@ -269,7 +266,23 @@ static NSTimeZone *gmt = nil; } - (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 { diff --git a/sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m b/sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m index c053f499..c4490572 100644 --- a/sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m +++ b/sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m @@ -889,12 +889,23 @@ static BOOL formatOutput = NO; } - (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; } -- 2.39.5