#include "SoDAVLockManager.h"
#include "EOFetchSpecification+SoDAV.h"
#include "WOContext+SoObjects.h"
+#include "NSException+HTTP.h"
#include <NGObjWeb/WOApplication.h>
#include <NGObjWeb/WORequest.h>
#include <NGObjWeb/WOResponse.h>
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
- misses the port argument :-(
- */
- NSString *baseURL;
- WORequest *rq;
- NSString *hostport;
- id tmp;
+ extern NSString *SoObjectRootURLInContext(WOContext *_ctx, id logobj, BOOL withAppPart);
+ NSString *rootURL;
- rq = [_ctx request];
-
- if ((tmp = [rq headerForKey:@"x-webobjects-server-name"])) {
- hostport = tmp;
- if ((tmp = [rq headerForKey:@"x-webobjects-server-port"]))
- hostport = [NSString stringWithFormat:@"%@:%@", hostport, tmp];
- }
- else if ((tmp = [rq headerForKey:@"host"]))
- hostport = tmp;
- else
- hostport = [[NSHost currentHost] name];
-
- baseURL = [NSString stringWithFormat:@"http://%@%@", hostport, [rq uri]];
- return baseURL;
+ rootURL = SoObjectRootURLInContext(_ctx, self, NO);
+ return [rootURL stringByAppendingString:[[_ctx request] uri]];
}
- (id)primaryCallWebDAVMethod:(NSString *)_name inContext:(WOContext *)_ctx {
/* 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]]) {
+ if ([(NSException *)methodObject httpStatus] == 404 /* Not Found */) {
+ /* not found */
+ return nil;
+ }
+ return methodObject; /* the exception */
+ }
+ if ((e = [self->object validateName:_method inContext:_ctx]) != nil)
+ return e;
+ if ([methodObject respondsToSelector:
+ @selector(takeValuesFromRequest:inContext:)])
+ [methodObject takeValuesFromRequest:[_ctx request] inContext:_ctx];
- if ((methodObject =
- [self->object lookupName:@"GET" inContext:_ctx acquire:NO]) == nil)
+ 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;
}
if ([pathInfo length] > 0) {
/* check whether all the parent collections are available */
+ // TODO: we might also want to check for a 'create' permission
if ([pathInfo rangeOfString:@"/"].length > 0) {
return [self httpException:409 /* Conflict */
reason:
}
- (id)doOPTIONS:(WOContext *)_ctx {
- return [self allowedMethods];
+ WOResponse *response;
+ NSArray *tmp;
+ id result;
+
+ /* this checks whether the object provides a specific OPTIONS method */
+ 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 ([[[_ctx request] clientCapabilities] isWebFolder]) {
+ /*
+ As described over here:
+ http://teyc.editthispage.com/2005/06/02
+
+ This page also says that: "MS-Auth-Via header is not required to work
+ with Web Folders".
+ */
+ [response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"public"];
+ }
+
+ if ((tmp = [self->object davComplianceClassesInContext:_ctx]) != nil)
+ [response setHeader:[tmp componentsJoinedByString:@", "] forKey:@"dav"];
+
+ return response;
}
- (id)doHEAD:(WOContext *)_ctx {
/* DAV reports */
- (id)doREPORT:(WOContext *)_ctx {
+ WORequest *rq;
+ id domDocument;
+
+ rq = [_ctx request];
+
+ /* ensure XML */
+
+ if (![[rq headerForKey:@"content-type"] hasPrefix:@"text/xml"]) {
+ return [self httpException:400 /* invalid request */
+ reason:@"XML entity expected for WebDAV REPORT."];
+ }
+
+ /* retrieve XML */
+
+ if ((domDocument = [rq contentAsDOMDocument]) == nil) {
+ return [self httpException:400 /* invalid request */
+ reason:@"Could not parse XML of WebDAV REPORT."];
+ }
+
+ /* process DOM */
+
+ [self logWithFormat:@"TODO: process REPORT: %@", domDocument];
+
return [self httpException:405 /* method not allowed */
reason:@"WebDAV reports not yet implemented."];
}
+/* CalDAV */
+
+- (id)doMKCALENDAR:(WOContext *)_ctx {
+ return [self httpException:405 /* method not allowed */
+ reason:@"CalDAV calendar creation not yet implemented."];
+}
+
/* DAV access control lists */
- (id)doACL:(WOContext *)_ctx {