-# $Id$
-
NGObjWeb WebDAV Implementation for SoObjects
============================================
-PROPFIND, TODO:
-"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."
-- means, an empty request is to be treated as allprop?
+This subproject contains a WebDAV implementation for SOPE which is based on top
+of the SoObjectRequestHandler.
+
+The central point where processing starts once the request is received by the
+handler, is the
+
+ SoObjectWebDAVDispatcher
+
+class. This class knows about the various WebDAV HTTP methods and takes
+appropriate actions by calling specified methods on either the SoObject which
+is targetted by the URL or on one of the maintenance objects. Eg WebDAV
+locking and notification is handled by the
+
+ SoDAVLockManager
+ SoSubscriptionManager
+
+classes, so the targetted object itself doesn't need to concern itself about
+that.
+
+Once a WebDAV action has been performed, the
+
+ SoWebDAVRenderer
+
+class is used to turn the result value of the HTTP method in a suitable WebDAV
+XML representation. To support non basic XML properties there is also a helper
+class
+
+ SoWebDAVValue
+
+
+PROPFIND/SEARCH Queries
+=======================
+
+The payload of both, WebDAV PROPFIND and SEARCH methods are represented as a
+regular EOControl EOFetchSpecification. The fetchspec is annotated with various
+hints to add additional WebDAV information. This includes the required depth
+of the query as well as the properties being requested.
+
+In case a SEARCH query is decoded, a regular EOQualifier/EOSortOrdering is
+used for representing that query.
+
+To perform a query, the layer will call:
+
+ - (id)performWebDAVQuery:(EOFetchSpecification *)_fetchSpecification
+ inContext:(WOContext *)_context;
+
+on the target object.
+
+Note that there are default implementations for queries based on KVC
+(see below)
+
+
+A special feature is that an object can make the WebDAV layer "remap" the
+public WebDAV property names to internal names, for example KVC keys. To do so
+the object needs to implement
+
+ - (NSDictionary *)davAttributeMapInContext:(WOContext *)_context;
+
+For example this can be used to map the "davDisplayName" to the "title" of a
+given object.
+
+
+Default Query Implementation
+============================
+
+The default implementation is done in the NSObject(SoDAVQuery) category which
+requires an NSObject conforming to the "So" model of doing things. A query is
+initiated by
+
+ - (id)performWebDAVQuery:(EOFetchSpecification *)_fetchSpecification
+ inContext:(WOContext *)_context;
+
+The method then splits the operation based on the depth into one of those
+methods for 'self'(0) or 'deep'(infinity) queries:
+
+ - (id)davQueryOnSelf:(EOFetchSpecification *)_fs inContext:(WOContext *)_ctx;
+ - (id)performWebDAVDeepQuery:((EOFetchSpecification *)_fs
+ inContext:(WOContext *)_ctx;
+
+For 'flat'(1) queries it uses a datasource to fetch the content and the
+-davQueryOnSelf:inContext: method to retrieve info on the object itself
+(required by WebDAV, but buggy in some implementations, eg WebFolders). The
+datasource is determined by
+
+ - (EODataSource *)contentDataSourceInContext:(WOContext *)_context;
+
+Per default this returns the SoObjectDataSource which performs KVC / EOControl
+based filtering and sorting.
+
+Besides those standard queries, the default implementations also deals with
+bulk queries as generated by either BPROPFIND or by the _range_ URI hack for
+ZideLook. Those will call
+
+ - (id)performWebDAVBulkQuery:(EOFetchSpecification *)_fetchSpec
+ inContext:(WOContext *)_context;
+
+Its discouraged to use bulk queries, since they are non-standard.
+
+
+Special Features
+================
+
+The dispatcher supports various Exchange WebDAV enhancements, eg bulk queries
+like BPROPFIND.
+
+Further it supports so called "ZideStore range" queries which are treated
+similiar to BPROPFIND and catches URLs starting with _range.
+
+
+TODO
+====
+
+REPORT
+- find a good way to represent arbitary reports
+
+extended ops: ACL, CHECKOUT etc
+
+
+Notes
+=====
DASL:
---snip---
: 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;
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) {
e = [sm validatePermission:SoPerm_AccessContentsInformation
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* perform search */
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 */
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];
: SoPerm_ChangeImagesAndFiles
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* check for conflicts */
davSetProperties:setProps
removePropertiesNamed:delProps
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
}
else {
/* create an object */
e = [sm validatePermission:SoPerm_WebDAVLockItems
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* check lock manager */
e = [sm validatePermission:SoPerm_WebDAVUnlockItems
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* check lock manager */
e = [sm validatePermission:SoPerm_AccessContentsInformation
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* perform search */
e = [sm validatePermission:SoPerm_AccessContentsInformation
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* perform search */
reason:@"WebDAV operation not yet implemented."];
}
+/* DAV reports */
+
+- (id)doREPORT:(WOContext *)_ctx {
+ return [self httpException:405 /* method not allowed */
+ reason:@"WebDAV reports not yet implemented."];
+}
+
/* DAV access control lists */
- (id)doACL:(WOContext *)_ctx {
e = [sm validatePermission:SoPerm_WebDAVAccess
onObject:self->object
inContext:_ctx];
- if (e) return e;
+ if (e != nil) return e;
/* perform search */