]> err.no Git - sope/blobdiff - sope-appserver/NGObjWeb/WebDAV/SoObjectWebDAVDispatcher.m
reverted gstep-base workaround (fixed in gstep-base svn trunk), bumped framework...
[sope] / sope-appserver / NGObjWeb / WebDAV / SoObjectWebDAVDispatcher.m
index 496d9746236be18b6164b9092545980889914179..82f813d35ab3e41d64698622ef18e2c7cef78fa4 100644 (file)
@@ -30,6 +30,8 @@
 #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>
 #include <NGObjWeb/WOContext.h>
@@ -104,55 +106,12 @@ 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
-    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 {
@@ -174,16 +133,43 @@ 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]]) {
+    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];
+  
+  result = [methodObject callOnObject:self->object inContext:_ctx];
+  return (result != nil) ? result : [NSNull null];
+}
+
+- (id)doGET:(WOContext *)_ctx {
+  NSException *e;
+  id methodObject;
   
-  if ((methodObject = 
-       [self->object lookupName:@"GET" inContext:_ctx acquire:NO]) == nil)
+  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;
   }
   
@@ -216,7 +202,7 @@ static NSTimeZone                *gmt      = nil;
             : SoPerm_ChangeImagesAndFiles
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   if ((e = [self->object validateName:@"PUT" inContext:_ctx]))
     return e;
@@ -225,6 +211,7 @@ static NSTimeZone                *gmt      = nil;
   
   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:
@@ -255,10 +242,11 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_DeleteObjects
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
-  if ((e = [self->object validateName:@"DELETE" inContext:_ctx]))
+  if (e != nil) 
     return e;
-
+  if ((e = [self->object validateName:@"DELETE" inContext:_ctx]) != nil)
+    return e;
+  
   // TODO: IE WebFolders sent a "Destroy" header together with the
   //       DELETE request, eg:
   //       "Destroy: NoUndelete"
@@ -267,7 +255,35 @@ static NSTimeZone                *gmt      = nil;
 }
 
 - (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 {
@@ -300,7 +316,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_AddFolders 
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
 
   /* check whether all the parent collections are available */
   if ([pathInfo rangeOfString:@"/"].length > 0) {
@@ -400,7 +416,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_AccessContentsInformation 
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   /* perform search */
   
@@ -416,15 +432,29 @@ static NSTimeZone                *gmt      = nil;
   
   if ([depth length] == 0) depth = @"infinity";
   
-  [self lockParser:davsax];
-  {
-    [xmlParser parseFromSource:[rq content]];
-    propNames = [[davsax propFindQueriedNames] copy];
-    findAll   = [davsax  propFindAllProperties];
-    findNames = [davsax  propFindPropertyNames];
+  if ([[rq content] length] > 0) {
+    [self lockParser:davsax];
+    {
+      [xmlParser parseFromSource:[rq content]];
+      propNames = [[davsax propFindQueriedNames] copy];
+      findAll   = [davsax  propFindAllProperties];
+      findNames = [davsax  propFindPropertyNames];
+    }
+    [self unlockParser:davsax];
+    propNames = [propNames autorelease];
+  }
+  else {
+    /*
+      8.1 PROPFIND
+      "A client may choose not to submit a request body.  An empty PROPFIND 
+       request body MUST be treated as a request for the names and values of
+       all properties."
+      TODO: means, an empty request is to be treated as allprop?
+    */
+    propNames = nil;
+    findAll   = YES;
+    findNames = NO;
   }
-  [self unlockParser:davsax];
-  propNames = [propNames autorelease];
   
   /* check query all properties */
   
@@ -471,7 +501,7 @@ static NSTimeZone                *gmt      = nil;
     
     hints = [self hintsWithScope:[self scopeForDepth:depth inContext:_ctx]
                  propNames:propNames findAll:findAll namesOnly:findNames];
-    if (rtargets) /* range-query keys */
+    if (rtargets != nil) /* range-query keys */
       [hints setObject:rtargets forKey:@"bulkTargetKeys"];
     
     fs = [EOFetchSpecification alloc];
@@ -546,7 +576,7 @@ static NSTimeZone                *gmt      = nil;
             : SoPerm_ChangeImagesAndFiles
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   /* check for conflicts */
 
@@ -674,7 +704,7 @@ static NSTimeZone                *gmt      = nil;
             davSetProperties:setProps
             removePropertiesNamed:delProps
             inContext:_ctx];
-    if (e) return e;
+    if (e != nil) return e;
   }
   else {
     /* create an object */
@@ -709,7 +739,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_WebDAVLockItems
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   /* check lock manager */
   
@@ -761,7 +791,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_WebDAVUnlockItems
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   /* check lock manager */
   
@@ -973,7 +1003,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_AccessContentsInformation 
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
 
   /* perform search */
   
@@ -1269,7 +1299,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_AccessContentsInformation 
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
 
   /* perform search */
   
@@ -1452,6 +1482,43 @@ static NSTimeZone                *gmt      = nil;
               reason:@"WebDAV operation not yet implemented."];
 }
 
+/* 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 {
@@ -1518,7 +1585,7 @@ static NSTimeZone                *gmt      = nil;
   e  = [sm validatePermission:SoPerm_WebDAVAccess
           onObject:self->object
           inContext:_ctx];
-  if (e) return e;
+  if (e != nil) return e;
   
   /* perform search */