]> granicus.if.org Git - apache/commitdiff
- implement DeltaV OPTIONS extensions
authorGreg Stein <gstein@apache.org>
Sat, 20 Jan 2001 02:00:01 +0000 (02:00 +0000)
committerGreg Stein <gstein@apache.org>
Sat, 20 Jan 2001 02:00:01 +0000 (02:00 +0000)
- let live prop providers get first crack at PROPFIND
- work around MS Web Folders limit on DAV header length

Submitted by: John Vasta <jvasta@rational.com>
Reviewed by: Greg Stein

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87752 13f79535-47bb-0310-9956-ffa450edef68

modules/dav/fs/repos.c
modules/dav/fs/repos.h
modules/dav/main/mod_dav.c
modules/dav/main/mod_dav.h
modules/dav/main/props.c
modules/dav/main/std_liveprop.c

index 8ee5e839845e1a38cc09a519f7bbd44bd2bc50bd..913067d591f3cf68729d66b6ec12bc6f63aac5cc 100644 (file)
@@ -1717,12 +1717,11 @@ static const dav_hooks_repository dav_hooks_repository_fs =
 };
 
 static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
-                                         int propid, int insvalue,
+                                         int propid, dav_prop_insert what,
                                          ap_text_header *phdr)
 {
     const char *value;
     const char *s;
-    dav_prop_insert which;
     apr_pool_t *p = resource->info->pool;
     const dav_liveprop_spec *info;
     int global_ns;
@@ -1806,19 +1805,23 @@ static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
 
     /* DBG3("FS: inserting lp%d:%s  (local %d)", ns, scan->name, scan->ns); */
 
-    if (insvalue) {
+    if (what == DAV_PROP_INSERT_VALUE) {
        s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
                          global_ns, info->name, value, global_ns, info->name);
-       which = DAV_PROP_INSERT_VALUE;
     }
-    else {
+    else if (what == DAV_PROP_INSERT_NAME) {
        s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name);
-       which = DAV_PROP_INSERT_NAME;
+    }
+    else {
+        /* assert: what == DAV_PROP_INSERT_SUPPORTED */
+        s = apr_psprintf(p, "<supported-live-property name=\"%s\""
+                            " namespace=\"%s\" xmlns=\"DAV:\"/>" DEBUG_CR,
+                         info->name, dav_fs_namespace_uris[info->ns]);
     }
     ap_text_append(p, phdr, s);
 
-    /* we inserted a name or value (this prop is done) */
-    return which;
+    /* we inserted what was asked for */
+    return what;
 }
 
 static int dav_fs_is_writable(const dav_resource *resource, int propid)
@@ -2010,7 +2013,7 @@ int dav_fs_find_liveprop(const dav_resource *resource,
 }
 
 void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
-                                 int insvalue, ap_text_header *phdr)
+                                 dav_prop_insert what, ap_text_header *phdr)
 {
     /* don't insert any liveprops if this isn't "our" resource */
     if (resource->hooks != &dav_hooks_repository_fs)
@@ -2027,13 +2030,13 @@ void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
     }
 
     (void) dav_fs_insert_prop(resource, DAV_PROPID_creationdate,
-                             insvalue, phdr);
+                             what, phdr);
     (void) dav_fs_insert_prop(resource, DAV_PROPID_getcontentlength,
-                             insvalue, phdr);
+                             what, phdr);
     (void) dav_fs_insert_prop(resource, DAV_PROPID_getlastmodified,
-                             insvalue, phdr);
+                             what, phdr);
     (void) dav_fs_insert_prop(resource, DAV_PROPID_getetag,
-                             insvalue, phdr);
+                             what, phdr);
 
 #ifndef WIN32
     /*
@@ -2042,7 +2045,7 @@ void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
     **       well not even call it.
     */
     (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_executable,
-                             insvalue, phdr);
+                             what, phdr);
 #endif
 
     /* ### we know the others aren't defined as liveprops */
index cfdd9dbc5358eb79633a94c495da19064b205b04..904ea3882a7d5fdb91d93f075c931d22180f2b3f 100644 (file)
@@ -103,7 +103,7 @@ int dav_fs_find_liveprop(const dav_resource *resource,
                          const char *ns_uri, const char *name,
                          const dav_hooks_liveprop **hooks);
 void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
-                                 int insvalue, ap_text_header *phdr);
+                                 dav_prop_insert what, ap_text_header *phdr);
 
 void dav_fs_register(apr_pool_t *p);
 
index 1657c163a81dec3bf9896de516a5093b8d3309be..3fe03d223be732c3d71e9ee161c9770f54187500 100644 (file)
@@ -1261,6 +1261,244 @@ static int dav_method_delete(request_rec *r)
     return HTTP_NO_CONTENT;
 }
 
+/* generate DAV:supported-method-set OPTIONS response */
+static dav_error *dav_gen_supported_methods(request_rec *r,
+                                            const ap_xml_elem *elem,
+                                            const apr_table_t *methods,
+                                            ap_text_header *body)
+{
+    apr_array_header_t *arr;
+    apr_table_entry_t *elts;
+    ap_xml_elem *child;
+    ap_xml_attr *attr;
+    char *s;
+    int i;
+
+    ap_text_append(r->pool, body, "<D:supported-method-set>" DEBUG_CR);
+
+    if (elem->first_child == NULL) {
+        /* show all supported methods */
+        arr = apr_table_elts(methods);
+        elts = (apr_table_entry_t *) arr->elts;
+
+        for (i = 0; i < arr->nelts; ++i) {
+            if (elts[i].key == NULL)
+                continue;
+            s = apr_psprintf(r->pool,
+                            "<D:supported-method D:name=\"%s\"/>" DEBUG_CR,
+                            elts[i].key);
+            ap_text_append(r->pool, body, s);
+        }
+    }
+    else {
+        /* check for support of specific methods */
+        for (child = elem->first_child; child != NULL; child = child->next) {
+            if (child->ns == AP_XML_NS_DAV_ID
+                && strcmp(child->name, "supported-method") == 0) {
+                const char *name = NULL;
+
+                /* go through attributes to find method name */
+                for (attr = child->attr; attr != NULL; attr = attr->next) {
+                    if (attr->ns == AP_XML_NS_DAV_ID
+                        && strcmp(attr->name, "name") == 0)
+                            name = attr->value;
+                }
+
+                if (name == NULL) {
+                    return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
+                                         "A DAV:supported-method element "
+                                         "does not have a \"name\" attribute");
+                }
+
+                /* see if method is supported */
+                if (apr_table_get(methods, name) != NULL) {
+                    s = apr_psprintf(r->pool,
+                                    "<D:supported-method D:name=\"%s\"/>" DEBUG_CR,
+                                    name);
+                    ap_text_append(r->pool, body, s);
+                }
+            }
+        }
+    }
+
+    ap_text_append(r->pool, body, "</D:supported-method-set>" DEBUG_CR);
+    return NULL;
+}
+
+/* generate DAV:supported-live-property-set OPTIONS response */
+static dav_error *dav_gen_supported_live_props(request_rec *r,
+                                               const dav_resource *resource,
+                                               const ap_xml_elem *elem,
+                                               ap_text_header *body)
+{
+    dav_lockdb *lockdb;
+    dav_propdb *propdb;
+    ap_xml_elem *child;
+    ap_xml_attr *attr;
+    dav_error *err;
+
+    /* open lock database, to report on supported lock properties */
+    /* ### should open read-only */
+    if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) {
+       return dav_push_error(r->pool, err->status, 0,
+                             "The lock database could not be opened, "
+                             "preventing report of supported lock properties.",
+                             err);
+    }
+
+    /* open the property database (readonly) for the resource */
+    if ((err = dav_open_propdb(r, lockdb,
+                              (dav_resource *)resource, 1,
+                               NULL, &propdb)) != NULL) {
+        if (lockdb != NULL)
+            (*lockdb->hooks->close_lockdb)(lockdb);
+
+       return dav_push_error(r->pool, err->status, 0,
+                             "The property database could not be opened, "
+                             "preventing report of supported properties.",
+                             err);
+    }
+
+    ap_text_append(r->pool, body, "<D:supported-live-property-set>" DEBUG_CR);
+
+    if (elem->first_child == NULL) {
+        /* show all supported live properties */
+        dav_get_props_result props = dav_get_allprops(propdb, DAV_PROP_INSERT_SUPPORTED);
+        body->last->next = props.propstats;
+        while (body->last->next != NULL)
+            body->last = body->last->next;
+    }
+    else {
+        /* check for support of specific live property */
+        for (child = elem->first_child; child != NULL; child = child->next) {
+            if (child->ns == AP_XML_NS_DAV_ID
+                && strcmp(child->name, "supported-live-property") == 0) {
+                const char *name = NULL;
+                const char *nmspace = NULL;
+
+                /* go through attributes to find name and namespace */
+                for (attr = child->attr; attr != NULL; attr = attr->next) {
+                    if (attr->ns == AP_XML_NS_DAV_ID) {
+                        if (strcmp(attr->name, "name") == 0)
+                            name = attr->value;
+                        else if (strcmp(attr->name, "namespace") == 0)
+                            nmspace = attr->value;
+                    }
+                }
+
+                if (name == NULL) {
+                    err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
+                                        "A DAV:supported-live-property element "
+                                        "does not have a \"name\" attribute");
+                    break;
+                }
+
+                /* default namespace to DAV: */
+                if (nmspace == NULL)
+                    nmspace = "DAV:";
+
+                /* check for support of property */
+                dav_get_liveprop_supported(propdb, nmspace, name, body);
+            }
+        }
+    }
+
+    ap_text_append(r->pool, body, "</D:supported-live-property-set>" DEBUG_CR);
+
+    dav_close_propdb(propdb);
+
+    if (lockdb != NULL)
+        (*lockdb->hooks->close_lockdb)(lockdb);
+
+    return err;
+}
+
+/* generate DAV:supported-report-set OPTIONS response */
+static dav_error *dav_gen_supported_reports(request_rec *r,
+                                            const dav_resource *resource,
+                                            const ap_xml_elem *elem,
+                                            const dav_hooks_vsn *vsn_hooks,
+                                            ap_text_header *body)
+{
+    ap_xml_elem *child;
+    ap_xml_attr *attr;
+    dav_error *err;
+    char *s;
+
+    ap_text_append(r->pool, body, "<D:supported-report-set>" DEBUG_CR);
+
+    if (vsn_hooks != NULL) {
+        const dav_report_elem *reports;
+        const dav_report_elem *rp;
+
+        if ((err = (*vsn_hooks->avail_reports)(resource, &reports)) != NULL) {
+           return dav_push_error(r->pool, err->status, 0,
+                                "DAV:supported-report-set could not be determined "
+                                 "due to a problem fetching the available reports "
+                                 "for this resource.",
+                                err);
+        }
+
+        if (reports != NULL) {
+            if (elem->first_child == NULL) {
+                /* show all supported reports */
+                for (rp = reports; rp->nmspace != NULL; ++rp) {
+                    /* Note: we presume reports->namespace is properly XML/URL quoted */
+                    s = apr_psprintf(r->pool,
+                                    "<D:supported-report D:name=\"%s\" D:namespace=\"%s\"/>" DEBUG_CR,
+                                    rp->name, rp->nmspace);
+                    ap_text_append(r->pool, body, s);
+                }
+            }
+            else {
+                /* check for support of specific report */
+                for (child = elem->first_child; child != NULL; child = child->next) {
+                    if (child->ns == AP_XML_NS_DAV_ID
+                        && strcmp(child->name, "supported-report") == 0) {
+                        const char *name = NULL;
+                        const char *nmspace = NULL;
+
+                        /* go through attributes to find name and namespace */
+                        for (attr = child->attr; attr != NULL; attr = attr->next) {
+                            if (attr->ns == AP_XML_NS_DAV_ID) {
+                                if (strcmp(attr->name, "name") == 0)
+                                    name = attr->value;
+                                else if (strcmp(attr->name, "namespace") == 0)
+                                    nmspace = attr->value;
+                            }
+                        }
+
+                        if (name == NULL) {
+                            return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
+                                                 "A DAV:supported-report element "
+                                                 "does not have a \"name\" attribute");
+                        }
+
+                        /* default namespace to DAV: */
+                        if (nmspace == NULL)
+                            nmspace = "DAV:";
+
+                        for (rp = reports; rp->nmspace != NULL; ++rp) {
+                            if (strcmp(name, rp->name) == 0
+                                && strcmp(nmspace, rp->nmspace) == 0) {
+                                /* Note: we presume reports->nmspace is properly XML/URL quoted */
+                                s = apr_psprintf(r->pool,
+                                                "<D:supported-report D:name=\"%s\" D:namespace=\"%s\"/>" DEBUG_CR,
+                                                rp->name, rp->nmspace);
+                                ap_text_append(r->pool, body, s);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    ap_text_append(r->pool, body, "</D:supported-report-set>" DEBUG_CR);
+    return NULL;
+}
+
 /* handle the OPTIONS method */
 static int dav_method_options(request_rec *r)
 {
@@ -1268,153 +1506,271 @@ static int dav_method_options(request_rec *r)
     const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
     const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r);
     dav_resource *resource;
-    const char *options;
     const char *dav_level;
-    const char *vsn_level;
+    char *allow;
+    char *s;
+    apr_array_header_t *arr;
+    apr_table_entry_t *elts;
+    apr_table_t *methods = apr_make_table(r->pool, 12);
+    ap_text_header vsn_options = { 0 };
+    ap_text_header body = { 0 };
+    ap_text *t;
+    int text_size;
     int result;
+    int i;
     apr_array_header_t *uri_ary;
-    const char *uris;
-
-    /* per HTTP/1.1 S9.2, we can discard this body */
-    if ((result = ap_discard_request_body(r)) != OK) {
-       return result;
-    }
-
-    /* no body */
-    ap_set_content_length(r, 0);
+    ap_xml_doc *doc;
+    const ap_xml_elem *elem;
 
     /* resolve the resource */
     result = dav_get_resource(r, 0 /*target_allowed*/, NULL, &resource);
     if (result != OK)
         return result;
 
+    /* parse any request body */
+    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
+       return result;
+    }
+    /* note: doc == NULL if no request body */
+
+    if (doc && !dav_validate_root(doc, "options")) {
+       ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
+                     "The \"options\" element was not found.");
+       return HTTP_BAD_REQUEST;
+    }
+
     /* determine which providers are available */
     dav_level = "1";
-    vsn_level = NULL;
 
     if (locks_hooks != NULL) {
         dav_level = "1,2";
     }
 
-    if (vsn_hooks != NULL
-        && (vsn_level = (*vsn_hooks->get_vsn_header)()) != NULL) {
-       dav_level = apr_pstrcat(r->pool, dav_level, ",", vsn_level, NULL);
+    if (binding_hooks != NULL)
+       dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
+
+    /* ###
+    ** MSFT Web Folders chokes if length of DAV header value > 63 characters!
+    ** To workaround that, we use separate DAV headers for versioning and
+    ** live prop provider namespace URIs.
+    ** ###
+    */
+    apr_table_setn(r->headers_out, "DAV", dav_level);
+
+    /*
+    ** If there is a versioning provider, generate DAV headers
+    ** for versioning options.
+    */
+    if (vsn_hooks != NULL) {
+        (*vsn_hooks->get_vsn_options)(r->pool, &vsn_options);
+
+        for (t = vsn_options.first; t != NULL; t = t->next)
+            apr_table_addn(r->headers_out, "DAV", t->text);
     }
 
-    /* gather property set URIs from all the liveprop providers */
+    /*
+    ** Gather property set URIs from all the liveprop providers,
+    ** and generate a separate DAV header for each URI, to avoid
+    ** problems with long header lengths.
+    */
     uri_ary = apr_make_array(r->pool, 5, sizeof(const char *));
     dav_run_gather_propsets(uri_ary);
-    uris = apr_array_pstrcat(r->pool, uri_ary, ',');
-    if (*uris) {
-        dav_level = apr_pstrcat(r->pool, dav_level, ",", uris, NULL);
+    for (i = 0; i < uri_ary->nelts; ++i) {
+        if (((char **)uri_ary->elts)[i] != NULL)
+            apr_table_addn(r->headers_out, "DAV", ((char **)uri_ary->elts)[i]);
     }
 
     /* this tells MSFT products to skip looking for FrontPage extensions */
     apr_table_setn(r->headers_out, "MS-Author-Via", "DAV");
 
     /*
+    ** Determine which methods are allowed on the resource.
     ** Three cases:  resource is null (3), is lock-null (7.4), or exists.
     **
-    ** All cases support OPTIONS and LOCK.
+    ** All cases support OPTIONS, and if there is a lock provider, LOCK.
     ** (Lock-) null resources also support MKCOL and PUT.
-    ** Lock-null support PROPFIND and UNLOCK.
+    ** Lock-null supports PROPFIND and UNLOCK.
     ** Existing resources support lots of stuff.
     */
 
+    apr_table_addn(methods, "OPTIONS", "");
+
     /* ### take into account resource type */
     switch (dav_get_resource_state(r, resource))
     {
     case DAV_RESOURCE_EXISTS:
        /* resource exists */
-       if (resource->collection) {
-           options = apr_pstrcat(r->pool,
-               "OPTIONS, "
-               "GET, HEAD, POST, DELETE, TRACE, "
-               "PROPFIND, PROPPATCH, COPY, MOVE",
-                locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
-                NULL);
-       }
-       else {
-           /* files also support PUT */
-           options = apr_pstrcat(r->pool,
-               "OPTIONS, "
-               "GET, HEAD, POST, DELETE, TRACE, "
-               "PROPFIND, PROPPATCH, COPY, MOVE, PUT",
-                locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
-                NULL);
-       }
-       break;
+        apr_table_addn(methods, "GET", "");
+        apr_table_addn(methods, "HEAD", "");
+        apr_table_addn(methods, "POST", "");
+        apr_table_addn(methods, "DELETE", "");
+        apr_table_addn(methods, "TRACE", "");
+        apr_table_addn(methods, "PROPFIND", "");
+        apr_table_addn(methods, "PROPPATCH", "");
+        apr_table_addn(methods, "COPY", "");
+        apr_table_addn(methods, "MOVE", "");
+
+       if (!resource->collection)
+            apr_table_addn(methods, "PUT", "");
+
+        if (locks_hooks != NULL) {
+            apr_table_addn(methods, "LOCK", "");
+            apr_table_addn(methods, "UNLOCK", "");
+        }
+
+        break;
 
     case DAV_RESOURCE_LOCK_NULL:
        /* resource is lock-null. */
-       options = apr_pstrcat(r->pool, "OPTIONS, MKCOL, PUT, PROPFIND",
-                             locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
-                             NULL);
-       break;
+        apr_table_addn(methods, "MKCOL", "");
+        apr_table_addn(methods, "PROPFIND", "");
+        apr_table_addn(methods, "PUT", "");
+
+        if (locks_hooks != NULL) {
+            apr_table_addn(methods, "LOCK", "");
+            apr_table_addn(methods, "UNLOCK", "");
+        }
+
+        break;
 
     case DAV_RESOURCE_NULL:
        /* resource is null. */
-       options = apr_pstrcat(r->pool, "OPTIONS, MKCOL, PUT",
-                             locks_hooks != NULL ? ", LOCK" : "",
-                             NULL);
-       break;
+        apr_table_addn(methods, "MKCOL", "");
+        apr_table_addn(methods, "PUT", "");
+
+        if (locks_hooks != NULL)
+            apr_table_addn(methods, "LOCK", "");
+
+        break;
 
     default:
        /* ### internal error! */
-       options = "OPTIONS";
        break;
     }
 
-    /* If there is a versioning provider, add versioning options */
+    /* If there is a versioning provider, add versioning methods */
     if (vsn_hooks != NULL) {
-        const char *vsn_options = NULL;
-
         if (!resource->exists) {
-            int vsn_control = (*vsn_hooks->versionable)(resource);
-            int mkworkspace = vsn_hooks->can_be_workspace != NULL
-                              && (*vsn_hooks->can_be_workspace)(resource);
+            if ((*vsn_hooks->versionable)(resource))
+                apr_table_addn(methods, "VERSION-CONTROL", "");
 
-            if (vsn_control && mkworkspace) {
-                vsn_options = ", VERSION-CONTROL, MKWORKSPACE";
-            }
-            else if (vsn_control)
-                vsn_options = ", VERSION-CONTROL";
-            else if (mkworkspace) {
-                vsn_options = ", MKWORKSPACE";
-            }
+            if (vsn_hooks->can_be_workspace != NULL
+                && (*vsn_hooks->can_be_workspace)(resource))
+                apr_table_addn(methods, "MKWORKSPACE", "");
         }
         else if (!resource->versioned) {
-            if ((*vsn_hooks->versionable)(resource)) {
-                vsn_options = ", VERSION-CONTROL";
-            }
+            if ((*vsn_hooks->versionable)(resource))
+                apr_table_addn(methods, "VERSION-CONTROL", "");
+        }
+        else if (resource->working) {
+            apr_table_addn(methods, "CHECKIN", "");
+            apr_table_addn(methods, "UNCHECKOUT", "");
+        }
+        else if (vsn_hooks->add_label != NULL) {
+            apr_table_addn(methods, "CHECKOUT", "");
+            apr_table_addn(methods, "LABEL", "");
+        }
+        else {
+            apr_table_addn(methods, "CHECKOUT", "");
         }
-        else if (resource->working)
-            vsn_options = ", CHECKIN, UNCHECKOUT";
-        else if (vsn_hooks->add_label != NULL)
-            vsn_options = ", CHECKOUT, LABEL";
-        else
-            vsn_options = ", CHECKOUT";
-
-        if (vsn_options != NULL)
-            options = apr_pstrcat(r->pool, options, vsn_options, NULL);
     }
 
     /* If there is a bindings provider, see if resource is bindable */
-    if (binding_hooks != NULL) {
-       dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
-        if ((*binding_hooks->is_bindable)(resource))
-            options = apr_pstrcat(r->pool, options, ", BIND", NULL);
+    if (binding_hooks != NULL
+        && (*binding_hooks->is_bindable)(resource)) {
+        apr_table_addn(methods, "BIND", "");
     }
 
-    apr_table_setn(r->headers_out, "Allow", options);
-    apr_table_setn(r->headers_out, "DAV", dav_level);
+    /* Generate the Allow header */
+    arr = apr_table_elts(methods);
+    elts = (apr_table_entry_t *) arr->elts;
+    text_size = 0;
+
+    /* first, compute total length */
+    for (i = 0; i < arr->nelts; ++i) {
+        if (elts[i].key == NULL)
+            continue;
+
+        /* add 1 for comma or null */
+        text_size += strlen(elts[i].key) + 1;
+    }
+
+    s = allow = apr_palloc(r->pool, text_size);
+
+    for (i = 0; i < arr->nelts; ++i) {
+        if (elts[i].key == NULL)
+            continue;
 
-    /* ### this will send a Content-Type. the default OPTIONS does not. */
+        if (s != allow)
+            *s++ = ',';
+
+        strcpy(s, elts[i].key);
+        s += strlen(s);
+    }
+
+    apr_table_setn(r->headers_out, "Allow", allow);
+
+    /* if there was no request body, then there is no response body */
+    if (doc == NULL) {
+        ap_set_content_length(r, 0);
+
+        /* ### this will send a Content-Type. the default OPTIONS does not. */
+        ap_send_http_header(r);
+
+        /* ### the default (ap_send_http_options) returns OK, but I believe
+         * ### that is because it is the default handler and nothing else
+         * ### will run after the thing. */
+        return DONE;
+    }
+
+    /* handle each options request */
+    for (elem = doc->root->first_child; elem != NULL; elem = elem->next) {
+        /* check for something we recognize first */
+        int core_option = 0;
+        dav_error *err = NULL;
+
+        if (elem->ns == AP_XML_NS_DAV_ID) {
+            if (strcmp(elem->name, "supported-method-set") == 0) {
+                err = dav_gen_supported_methods(r, elem, methods, &body);
+                core_option = 1;
+            }
+            else if (strcmp(elem->name, "supported-live-property-set") == 0) {
+                err = dav_gen_supported_live_props(r, resource, elem, &body);
+                core_option = 1;
+            }
+            else if (strcmp(elem->name, "supported-report-set") == 0) {
+                err = dav_gen_supported_reports(r, resource, elem, vsn_hooks, &body);
+                core_option = 1;
+            }
+        }
+
+        if (err != NULL)
+            return dav_handle_err(r, err, NULL);
+
+        /* if unrecognized option, pass to versioning provider */
+        if (!core_option) {
+            if ((err = (*vsn_hooks->get_option)(resource, elem, &body))
+                != NULL) {
+                return dav_handle_err(r, err, NULL);
+            }
+        }
+    }
+
+    /* send the options response */
+    r->status = HTTP_OK;
+    r->content_type = DAV_XML_CONTENT_TYPE;
+
+    /* send the headers */
     ap_send_http_header(r);
 
-    /* ### the default (ap_send_http_options) returns OK, but I believe
-     * ### that is because it is the default handler and nothing else
-     * ### will run after the thing. */
+    /* send the response body */
+    ap_rputs(DAV_XML_HEADER DEBUG_CR
+             "<D:options-response xmlns:D=\"DAV:\">" DEBUG_CR, r);
+
+    for (t = body.first; t != NULL; t = t->next)
+        ap_rputs(t->text, r);
+
+    ap_rputs("</D:options-response>" DEBUG_CR, r);
 
     /* we've sent everything necessary to the client. */
     return DONE;
@@ -1489,8 +1845,10 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
        propstats = dav_get_props(propdb, ctx->doc);
     }
     else {
-       propstats = dav_get_allprops(propdb,
-                            ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP);
+        dav_prop_insert what = ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP
+                                 ? DAV_PROP_INSERT_VALUE
+                                 : DAV_PROP_INSERT_NAME;
+       propstats = dav_get_allprops(propdb, what);
     }
     dav_close_propdb(propdb);
 
@@ -4073,5 +4431,5 @@ APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, find_liveprop,
                                      (resource, ns_uri, name, hooks), 0);
 APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, insert_all_liveprops,
                                  (request_rec *r, const dav_resource *resource,
-                                  int insvalue, ap_text_header *phdr),
-                                 (r, resource, insvalue, phdr));
+                                  dav_prop_insert what, ap_text_header *phdr),
+                                 (r, resource, what, phdr));
index dce73ff8f9e958f804635dfe32c11cb355a13fcf..a58bb8acce033e3c42d3a164d083951e75e63ca7 100644 (file)
@@ -489,6 +489,25 @@ typedef struct
 
 dav_lookup_result dav_lookup_uri(const char *uri, request_rec *r);
 
+/* defines type of property info a provider is to return */
+typedef enum {
+    DAV_PROP_INSERT_NOTDEF,    /* property is defined by this provider,
+                                  but nothing was inserted because the
+                                  (live) property is not defined for this
+                                  resource (it may be present as a dead
+                                  property). */
+    DAV_PROP_INSERT_NOTSUPP,    /* property is recognized by this provider,
+                                 * but it is not supported, and cannot be
+                                 * treated as a dead property */
+    DAV_PROP_INSERT_NAME,      /* a property name (empty elem) was
+                                  inserted into the text block */
+    DAV_PROP_INSERT_VALUE,     /* a property name/value pair was inserted
+                                  into the text block */
+    DAV_PROP_INSERT_SUPPORTED   /* a supported live property was added to
+                                   the text block as a
+                                   <DAV:supported-live-property> element */
+} dav_prop_insert;
+
 /* ### this stuff is private to dav/fs/repos.c; move it... */
 /* format a time string (buf must be at least DAV_TIMEBUF_SIZE chars) */
 #define DAV_STYLE_ISO8601      1
@@ -578,7 +597,7 @@ APR_DECLARE_EXTERNAL_HOOK(dav, DAV, int, find_liveprop,
 */
 APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, insert_all_liveprops, 
                          (request_rec *r, const dav_resource *resource,
-                          int insvalue, ap_text_header *phdr))
+                          dav_prop_insert what, ap_text_header *phdr))
 
 /* ### make this internal to mod_dav.c ? */
 #define DAV_KEY_RESOURCE        "dav-resource"
@@ -670,35 +689,36 @@ dav_error * dav_get_locktoken_list(request_rec *r, dav_locktoken_list **ltl);
 ** LIVE PROPERTY HANDLING
 */
 
-typedef enum {
-    DAV_PROP_INSERT_NOTDEF,    /* property is defined by this provider,
-                                  but nothing was inserted because the
-                                  (live) property is not defined for this
-                                  resource (it may be present as a dead
-                                  property). */
-    DAV_PROP_INSERT_NAME,      /* a property name (empty elem) was
-                                  inserted into the text block */
-    DAV_PROP_INSERT_VALUE      /* a property name/value pair was inserted
-                                  into the text block */
-} dav_prop_insert;
-
 /* opaque type for PROPPATCH rollback information */
 typedef struct dav_liveprop_rollback dav_liveprop_rollback;
 
 struct dav_hooks_liveprop
 {
     /*
-    ** Insert a property name/value into a text block. The property to
-    ** insert is identified by the propid value. If insvalue is true,
-    ** then the property's value should be inserted; otherwise, an empty
-    ** element (ie. just the prop's name) should be inserted.
+    ** Insert property information into a text block. The property to
+    ** insert is identified by the propid value. The information to insert
+    ** is identified by the "what" argument, as follows:
+    **   DAV_PROP_INSERT_NAME
+    **      property name, as an empty XML element
+    **   DAV_PROP_INSERT_VALUE
+    **      property name/value, as an XML element
+    **   DAV_PROP_INSERT_SUPPORTED
+    **      if the property is defined on the resource, then
+    **      a DAV:supported-live-property element, as defined
+    **      by the DeltaV extensions to RFC2518.
+    **                      
+    ** Providers should return DAV_PROP_INSERT_NOTDEF if they do not define
+    ** the specified propid, but allow the property to be handled as a
+    ** dead property. If a provider recognizes, but does not support,
+    ** a property, and does not want it handled as a dead property, it should
+    ** return DAV_PROP_INSERT_NOTSUPP.
     **
     ** Returns one of DAV_PROP_INSERT_* based on what happened.
     **
     ** ### we may need more context... ie. the lock database
     */
     dav_prop_insert (*insert_prop)(const dav_resource *resource,
-                                  int propid, int insvalue,
+                                  int propid, dav_prop_insert what,
                                   ap_text_header *phdr);
 
     /*
@@ -840,7 +860,7 @@ int dav_core_find_liveprop(const dav_resource *resource,
                            const dav_hooks_liveprop **hooks);
 void dav_core_insert_all_liveprops(request_rec *r,
                                    const dav_resource *resource,
-                                   int insvalue, ap_text_header *phdr);
+                                   dav_prop_insert what, ap_text_header *phdr);
 void dav_core_register_uris(apr_pool_t *p);
 
 
@@ -880,9 +900,6 @@ enum {
     DAV_PROPID_resourcetype,
     DAV_PROPID_source,
     DAV_PROPID_supportedlock,
-    DAV_PROPID_supported_method_set,            /* from DeltaV I-D */
-    DAV_PROPID_supported_live_property_set,     /* from DeltaV I-D */
-    DAV_PROPID_supported_report_set,            /* from DeltaV I-D */
 
     /* DeltaV properties (from the I-D) */
     DAV_PROPID_activity_collection_set,
@@ -1351,7 +1368,13 @@ dav_get_props_result dav_get_props(
 
 dav_get_props_result dav_get_allprops(
     dav_propdb *db,
-    int getvals);
+    dav_prop_insert what);
+
+void dav_get_liveprop_supported(
+    dav_propdb *propdb,
+    const char *ns_uri,
+    const char *propname,
+    ap_text_header *body);
 
 /*
 ** 3-phase property modification.
@@ -1851,10 +1874,22 @@ struct dav_hooks_vsn
     ** they define the functionality needed to implement "core" versioning.
     */
 
-    /* Return supported versioning level
-     * for the Versioning header
+    /* Return supported versioning options.
+     * Each dav_text item in the list will be returned as a separate
+     * DAV header. Providers are advised to limit the length of an
+     * individual text item to 63 characters, to conform to the limit
+     * used by MS Web Folders.
+     */
+    void (*get_vsn_options)(apr_pool_t *p, ap_text_header *phdr);
+
+    /* Get the value of a specific option for an OPTIONS request.
+     * The option being requested is given by the parsed XML
+     * element object "elem". The value of the option should be
+     * appended to the "option" text object.
      */
-    const char * (*get_vsn_header)(void);
+    dav_error * (*get_option)(const dav_resource *resource,
+                              const ap_xml_elem *elem,
+                              ap_text_header *option);
 
     /* Put a resource under version control. If the resource already
      * exists unversioned, then it becomes the initial version of the
@@ -1947,7 +1982,7 @@ struct dav_hooks_vsn
     ** must be passed to this routine.
     **
     ** The dav_xml_doc structure contains the parsed report request
-    ** body. The report response is generated into the dav_text_header
+    ** body. The report response is generated into the ap_text_header
     ** structure.
     **
     ** ### shouldn't generate large responses to memory ###
index 2278bedf9be5f8e13850089433e1fc4ea6d07e5a..e529deac3b203e3db3541f55a9512dd4b6b4da51 100644 (file)
@@ -324,46 +324,60 @@ static const char *dav_get_ns_table_uri(dav_propdb *propdb, int ns)
 }
 #endif
 
-static void dav_find_liveprop(dav_propdb *propdb, ap_xml_elem *elem)
+static int dav_find_liveprop_provider(dav_propdb *propdb,
+                                      const char *ns_uri,
+                                      const char *propname,
+                                      const dav_hooks_liveprop **provider)
 {
     int propid;
-    const char *ns_uri;
-    dav_elem_private *priv = elem->private;
-    const dav_hooks_liveprop *hooks;
 
-    if (elem->ns == AP_XML_NS_DAV_ID) {
+    *provider = NULL;
+
+    if (ns_uri == NULL) {
+       /* policy: liveprop providers cannot define no-namespace properties */
+       return DAV_PROPID_CORE_UNKNOWN;
+    }
+    else if (strcmp(ns_uri, "DAV:") == 0) {
        const char * const *p = dav_core_props;
 
        for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
-           if (strcmp(elem->name, *p) == 0) {
-               priv->propid = propid;
-                /* priv->provider == NULL */
-               return;
+           if (strcmp(propname, *p) == 0) {
+               return propid;
            }
 
        /* didn't find it. fall thru. a provider can define DAV: props */
     }
-    else if (elem->ns == AP_XML_NS_NONE) {
-       /* policy: liveprop providers cannot define no-namespace properties */
-       priv->propid = DAV_PROPID_CORE_UNKNOWN;
-        /* priv->provider == NULL */
-       return;
-    }
-
-    /* get the URI for the element's namespace id */
-    ns_uri = AP_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
 
     /* is there a liveprop provider for this property? */
-    propid = dav_run_find_liveprop(propdb->resource, ns_uri, elem->name,
-                                  &hooks);
+    propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
+                                   provider);
     if (propid != 0) {
-        priv->propid = propid;
-        priv->provider = hooks;
-        return;
+        return propid;
     }
 
-    priv->propid = DAV_PROPID_CORE_UNKNOWN;
-    /* priv->provider == NULL */
+    /* no provider for this property */
+    return DAV_PROPID_CORE_UNKNOWN;
+}
+
+static void dav_find_liveprop(dav_propdb *propdb, ap_xml_elem *elem)
+{
+    const char *ns_uri;
+    dav_elem_private *priv = elem->private;
+    const dav_hooks_liveprop *hooks;
+
+
+    if (elem->ns == AP_XML_NS_NONE)
+        ns_uri = NULL;
+    else if (elem->ns == AP_XML_NS_DAV_ID)
+        ns_uri = "DAV:";
+    else
+        ns_uri = AP_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
+
+    priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name,
+                                              &hooks);
+    if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
+        priv->provider = hooks;
+    }
 }
 
 /* is the live property read/write? */
@@ -415,14 +429,14 @@ static void dav_do_prop_subreq(dav_propdb *propdb)
 
 static dav_error * dav_insert_coreprop(dav_propdb *propdb,
                                       int propid, const char *name,
-                                      int getvals,
+                                      dav_prop_insert what,
                                       ap_text_header *phdr,
-                                      int *inserted)
+                                      dav_prop_insert *inserted)
 {
     const char *value = NULL;
     dav_error *err;
 
-    *inserted = 0;
+    *inserted = DAV_PROP_INSERT_NOTDEF;
 
     /* fast-path the common case */
     if (propid == DAV_PROPID_CORE_UNKNOWN)
@@ -490,7 +504,6 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
        break;
     }
 
-    case DAV_PROPID_CORE_UNKNOWN:
     default:
        /* fall through to interpret as a dead property */
        break;
@@ -500,7 +513,15 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
     if (value != NULL) {
        const char *s;
 
-       if (getvals && *value != '\0') {
+        if (what == DAV_PROP_INSERT_SUPPORTED) {
+           /* use D: prefix to refer to the DAV: namespace URI,
+             * and let the namespace attribute default to "DAV:"
+             */
+            s = apr_psprintf(propdb->p,
+                            "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
+                            name);
+        }
+       else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
            /* use D: prefix to refer to the DAV: namespace URI */
            s = apr_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR,
                            name, value, name);
@@ -511,7 +532,7 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
        }
        ap_text_append(propdb->p, phdr, s);
 
-       *inserted = 1;
+       *inserted = what;
     }
 
     return NULL;
@@ -519,26 +540,23 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
 
 static dav_error * dav_insert_liveprop(dav_propdb *propdb,
                                       const ap_xml_elem *elem,
-                                      int getvals,
+                                      dav_prop_insert what,
                                       ap_text_header *phdr,
-                                      int *inserted)
+                                      dav_prop_insert *inserted)
 {
-    dav_prop_insert pi;
     dav_elem_private *priv = elem->private;
 
-    *inserted = 0;
+    *inserted = DAV_PROP_INSERT_NOTDEF;
 
     if (priv->provider == NULL) {
         /* this is a "core" property that we define */
        return dav_insert_coreprop(propdb, priv->propid, elem->name,
-                                  getvals, phdr, inserted);
+                                  what, phdr, inserted);
     }
 
     /* ask the provider (that defined this prop) to insert the prop */
-    pi = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
-                                       getvals, phdr);
-    if (pi != DAV_PROP_INSERT_NOTDEF)
-       *inserted = 1;
+    *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
+                                              what, phdr);
 
     return NULL;
 }
@@ -909,7 +927,6 @@ dav_error *dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
                           dav_propdb **p_propdb)
 {
     dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
-    dav_error *err;
 
     *p_propdb = NULL;
 
@@ -931,12 +948,10 @@ dav_error *dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
 
     propdb->lockdb = lockdb;
 
-    if (!ro) {
-       propdb->deferred = 1;
-    }
-    else if ((err = dav_really_open_db(propdb, 1 /* ro */)) != NULL) {
-       return err;
-    }
+    /* always defer actual open, to avoid expense of accessing db
+     * when only live properties are involved
+     */
+    propdb->deferred = 1;
 
     /* ### what to do about closing the propdb on server failure? */
 
@@ -975,7 +990,7 @@ void dav_close_propdb(dav_propdb *propdb)
     (*propdb->db_hooks->close)(propdb->db);
 }
 
-dav_get_props_result dav_get_allprops(dav_propdb *propdb, int getvals)
+dav_get_props_result dav_get_allprops(dav_propdb *propdb, dav_prop_insert what)
 {
     const dav_hooks_db *db_hooks = propdb->db_hooks;
     ap_text_header hdr = { 0 };
@@ -983,101 +998,111 @@ dav_get_props_result dav_get_allprops(dav_propdb *propdb, int getvals)
     dav_get_props_result result = { 0 };
     int found_contenttype = 0;
     int found_contentlang = 0;
-    int unused_inserted;
+    dav_prop_insert unused_inserted;
 
-    /* generate all the namespaces that are in the propdb */
-    dav_get_propdb_xmlns(propdb, &hdr_ns);
-
-    /* initialize the result with some start tags... */
-    ap_text_append(propdb->p, &hdr,
-                  "<D:propstat>" DEBUG_CR
-                  "<D:prop>" DEBUG_CR);
-
-    /* if there ARE properties, then scan them */
-    if (propdb->db != NULL) {
-       dav_datum key;
-       int dav_id = dav_find_dav_id(propdb);
+    /* if not just getting supported live properties,
+     * scan all properties in the dead prop database
+     */
+    if (what != DAV_PROP_INSERT_SUPPORTED) {
+        if (propdb->deferred) {
+            /* ### what to do with db open error? */
+            (void) dav_really_open_db(propdb, 1 /*ro*/);
+        }
 
-       (void) (*db_hooks->firstkey)(propdb->db, &key);
-       while (key.dptr) {
-           dav_datum prevkey;
+        /* generate all the namespaces that are in the propdb */
+        dav_get_propdb_xmlns(propdb, &hdr_ns);
 
-           /* any keys with leading capital letters should be skipped
-              (real keys start with a number or a colon) */
-           if (*key.dptr >= 'A' && *key.dptr <= 'Z')
-               goto next_key;
+        /* initialize the result with some start tags... */
+        ap_text_append(propdb->p, &hdr,
+                      "<D:propstat>" DEBUG_CR
+                      "<D:prop>" DEBUG_CR);
 
-           /*
-           ** We also look for <DAV:getcontenttype> and
-           ** <DAV:getcontentlanguage>. If they are not stored as dead
-           ** properties, then we need to perform a subrequest to get
-           ** their values (if any).
-           */
-           if (dav_id != -1
-               && *key.dptr != ':'
-               && dav_id == atoi(key.dptr)) {
+        /* if there ARE properties, then scan them */
+        if (propdb->db != NULL) {
+           dav_datum key;
+           int dav_id = dav_find_dav_id(propdb);
 
-               const char *colon;
+           (void) (*db_hooks->firstkey)(propdb->db, &key);
+           while (key.dptr) {
+               dav_datum prevkey;
 
-               /* find the colon */
-               if ( key.dptr[1] == ':' ) {
-                   colon = key.dptr + 1;
-               }
-               else {
-                   colon = strchr(key.dptr + 2, ':');
-               }
+               /* any keys with leading capital letters should be skipped
+                  (real keys start with a number or a colon) */
+               if (*key.dptr >= 'A' && *key.dptr <= 'Z')
+                   goto next_key;
 
-               if (colon[1] == 'g') {
-                   if (strcmp(colon + 1, "getcontenttype") == 0) {
-                       found_contenttype = 1;
+               /*
+               ** We also look for <DAV:getcontenttype> and
+               ** <DAV:getcontentlanguage>. If they are not stored as dead
+               ** properties, then we need to perform a subrequest to get
+               ** their values (if any).
+               */
+               if (dav_id != -1
+                   && *key.dptr != ':'
+                   && dav_id == atoi(key.dptr)) {
+
+                   const char *colon;
+
+                   /* find the colon */
+                   if ( key.dptr[1] == ':' ) {
+                       colon = key.dptr + 1;
                    }
-                   else if (strcmp(colon + 1, "getcontentlanguage") == 0) {
-                       found_contentlang = 1;
+                   else {
+                       colon = strchr(key.dptr + 2, ':');
                    }
-               }
-           }
 
-           if (getvals) {
-               dav_datum value;
+                   if (colon[1] == 'g') {
+                       if (strcmp(colon + 1, "getcontenttype") == 0) {
+                           found_contenttype = 1;
+                       }
+                       else if (strcmp(colon + 1, "getcontentlanguage") == 0) {
+                           found_contentlang = 1;
+                       }
+                   }
+               }
 
-               (void) (*db_hooks->fetch)(propdb->db, key, &value);
-               if (value.dptr == NULL) {
-                   /* ### anything better to do? */
-                   /* ### probably should enter a 500 error */
-                   goto next_key;
-               }
+               if (what == DAV_PROP_INSERT_VALUE) {
+                   dav_datum value;
 
-               /* put the prop name and value into the result */
-               dav_append_prop(propdb, key.dptr, value.dptr, &hdr);
+                   (void) (*db_hooks->fetch)(propdb->db, key, &value);
+                   if (value.dptr == NULL) {
+                       /* ### anything better to do? */
+                       /* ### probably should enter a 500 error */
+                       goto next_key;
+                   }
 
-               (*db_hooks->freedatum)(propdb->db, value);
-           }
-           else {
-               /* simple, empty element if a value isn't needed */
-               dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr);
+                   /* put the prop name and value into the result */
+                   dav_append_prop(propdb, key.dptr, value.dptr, &hdr);
+
+                   (*db_hooks->freedatum)(propdb->db, value);
+               }
+               else {
+                   /* simple, empty element if a value isn't needed */
+                   dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr);
+               }
+
+             next_key:
+               prevkey = key;
+               (void) (*db_hooks->nextkey)(propdb->db, &key);
+               (*db_hooks->freedatum)(propdb->db, prevkey);
            }
+        }
 
-         next_key:
-           prevkey = key;
-           (void) (*db_hooks->nextkey)(propdb->db, &key);
-           (*db_hooks->freedatum)(propdb->db, prevkey);
-       }
+        /* add namespaces for all the liveprop providers */
+        dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
     }
 
-    /* add namespaces for all the liveprop providers */
-    dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
-    
     /* ask the liveprop providers to insert their properties */
-    dav_run_insert_all_liveprops(propdb->r, propdb->resource, getvals, &hdr);
+    dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
 
     /* insert the standard properties */
     /* ### should be handling the return errors here */
     (void)dav_insert_coreprop(propdb,
                              DAV_PROPID_CORE_supportedlock, "supportedlock",
-                             getvals, &hdr, &unused_inserted);
+                             what, &hdr, &unused_inserted);
     (void)dav_insert_coreprop(propdb,
                              DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
-                             getvals, &hdr, &unused_inserted);
+                             what, &hdr, &unused_inserted);
 
     /* if we didn't find these, then do the whole subreq thing. */
     if (!found_contenttype) {
@@ -1085,21 +1110,24 @@ dav_get_props_result dav_get_allprops(dav_propdb *propdb, int getvals)
        (void)dav_insert_coreprop(propdb,
                                  DAV_PROPID_CORE_getcontenttype,
                                  "getcontenttype",
-                                 getvals, &hdr, &unused_inserted);
+                                 what, &hdr, &unused_inserted);
     }
     if (!found_contentlang) {
        /* ### should be handling the return error here */
        (void)dav_insert_coreprop(propdb,
                                  DAV_PROPID_CORE_getcontentlanguage,
                                  "getcontentlanguage",
-                                 getvals, &hdr, &unused_inserted);
+                                 what, &hdr, &unused_inserted);
     }
 
-    /* terminate the result */
-    ap_text_append(propdb->p, &hdr,
-                  "</D:prop>" DEBUG_CR
-                  "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
-                  "</D:propstat>" DEBUG_CR);
+    /* if not just reporting on supported live props,
+     * terminate the result */
+    if (what != DAV_PROP_INSERT_SUPPORTED) {
+        ap_text_append(propdb->p, &hdr,
+                      "</D:prop>" DEBUG_CR
+                      "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
+                      "</D:propstat>" DEBUG_CR);
+    }
 
     result.propstats = hdr.first;
     result.xmlns = hdr_ns.first;
@@ -1114,6 +1142,7 @@ dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
     ap_text_header hdr_bad = { 0 };
     ap_text_header hdr_ns = { 0 };
     int have_good = 0;
+    int propdb_xmlns_done = 0;
     dav_get_props_result result = { 0 };
     char *marks_input;
     char *marks_liveprop;
@@ -1126,9 +1155,6 @@ dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
                   "<D:propstat>" DEBUG_CR
                   "<D:prop>" DEBUG_CR);
 
-    /* generate all the namespaces that are in the propdb */
-    dav_get_propdb_xmlns(propdb, &hdr_ns);
-
     /* ### the marks should be in a buffer! */
     /* allocate zeroed-memory for the marks. These marks indicate which
        input namespaces we've generated into the output xmlns buffer */
@@ -1138,45 +1164,37 @@ dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
     marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
 
     for (elem = elem->first_child; elem; elem = elem->next) {
-       dav_datum key;
+        dav_datum key = { 0 };
        dav_datum value = { 0 };
        dav_elem_private *priv;
+       dav_error *err;
+       dav_prop_insert inserted;
+        int is_liveprop = 0;
 
-       /*
-       ** Note: the key may be NULL if we have no properties that are in
-       ** a namespace that matches the requested prop's namespace.
-       */
-       key = dav_gdbm_key(propdb, elem);
-
-       /* fetch IF we have a db and a key. otherwise, value is NULL */
-       if (propdb->db != NULL && key.dptr != NULL) {
-           (void) (*db_hooks->fetch)(propdb->db, key, &value);
-       }
+        /*
+        ** First try live property providers; if they don't handle
+        ** the property, then try looking it up in the propdb.
+        */
 
        if (elem->private == NULL) {
            elem->private = apr_pcalloc(propdb->p, sizeof(*priv));
        }
        priv = elem->private;
 
-       /*
-       ** If we did not find the property in the database, then it may
-       ** be a liveprop that we can handle specially.
-       */
-       if (value.dptr == NULL) {
-           dav_error *err;
-           int inserted;
+       /* cache the propid; dav_get_props() could be called many times */
+       if (priv->propid == 0)
+           dav_find_liveprop(propdb, elem);
 
-           /* cache the propid; dav_get_props() could be called many times */
-           if (priv->propid == 0)
-               dav_find_liveprop(propdb, elem);
+        if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
+            is_liveprop = 1;
 
            /* insert the property. returns 1 if an insertion was done. */
-           if ((err = dav_insert_liveprop(propdb, elem, 1, &hdr_good,
-                                          &inserted)) != NULL) {
+           if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
+                                           &hdr_good, &inserted)) != NULL) {
                /* ### need to propagate the error to the caller... */
                /* ### skip it for now, as if nothing was inserted */
            }
-           if (inserted) {
+           if (inserted == DAV_PROP_INSERT_VALUE) {
                have_good = 1;
 
                /*
@@ -1203,7 +1221,41 @@ dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
 
                continue;
            }
-       }
+            else if (inserted == DAV_PROP_INSERT_NOTDEF) {
+                /* allow property to be handled as a dead property */
+                is_liveprop = 0;
+            }
+        }
+
+       /*
+       ** If not handled as a live property, look in the dead property database
+       */
+        if (!is_liveprop) {
+            /* make sure propdb is really open */
+            if (propdb->deferred) {
+                /* ### what to do with db open error? */
+                (void) dav_really_open_db(propdb, 1 /*ro*/);
+            }
+
+            /* if not done yet,
+             * generate all the namespaces that are in the propdb
+             */
+            if (!propdb_xmlns_done) {
+                dav_get_propdb_xmlns(propdb, &hdr_ns);
+                propdb_xmlns_done = 1;
+            }
+
+           /*
+           ** Note: the key may be NULL if we have no properties that are in
+           ** a namespace that matches the requested prop's namespace.
+           */
+           key = dav_gdbm_key(propdb, elem);
+
+           /* fetch IF we have a db and a key. otherwise, value is NULL */
+           if (propdb->db != NULL && key.dptr != NULL) {
+               (void) (*db_hooks->fetch)(propdb->db, key, &value);
+           }
+        }
 
        if (value.dptr == NULL) {
            /* not found. add a record to the "bad" propstats */
@@ -1282,6 +1334,30 @@ dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
     return result;
 }
 
+void dav_get_liveprop_supported(dav_propdb *propdb,
+                                const char *ns_uri,
+                                const char *propname,
+                                ap_text_header *body)
+{
+    int propid;
+    const dav_hooks_liveprop *hooks;
+
+    propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
+
+    if (propid != DAV_PROPID_CORE_UNKNOWN) {
+        if (hooks == NULL) {
+            /* this is a "core" property that we define */
+            dav_prop_insert unused_inserted;
+            dav_insert_coreprop(propdb, propid, propname,
+                                DAV_PROP_INSERT_SUPPORTED, body, &unused_inserted);
+        }
+        else {
+           (*hooks->insert_prop)(propdb->resource, propid,
+                                  DAV_PROP_INSERT_SUPPORTED, body);
+        }
+    }
+}
+
 void dav_prop_validate(dav_prop_ctx *ctx)
 {
     dav_propdb *propdb = ctx->propdb;
index 8ae1782ebde97cc100a5df5f200d0fc8668393ee..01b6b925af9cb2f80917fdd50cbd6207b758f5fa 100644 (file)
@@ -82,10 +82,6 @@ static const dav_liveprop_spec dav_core_props[] =
     { 0, "displayname",          DAV_PROPID_displayname,          1 },
     { 0, "resourcetype",         DAV_PROPID_resourcetype,         0 },
     { 0, "source",               DAV_PROPID_source,               1 },
-    { 0, "supported-live-property-set",
-      DAV_PROPID_supported_live_property_set, 0 },
-    { 0, "supported-method-set", DAV_PROPID_supported_method_set, 0 },
-    { 0, "supported-report-set", DAV_PROPID_supported_report_set, 0 },
 
     { 0 }      /* sentinel */
 };
@@ -98,12 +94,11 @@ static const dav_liveprop_group dav_core_liveprop_group =
 };
 
 static dav_prop_insert dav_core_insert_prop(const dav_resource *resource,
-                                            int propid, int insvalue,
+                                            int propid, dav_prop_insert what,
                                             ap_text_header *phdr)
 {
     const char *value;
     const char *s;
-    dav_prop_insert which;
     apr_pool_t *p = resource->pool;
     const dav_liveprop_spec *info;
     int global_ns;
@@ -145,60 +140,6 @@ static dav_prop_insert dav_core_insert_prop(const dav_resource *resource,
         }
         break;
 
-    case DAV_PROPID_supported_live_property_set:
-        /* ### insert all live property names ### */
-        return DAV_PROP_INSERT_NOTDEF;
-        break;
-
-    case DAV_PROPID_supported_method_set:
-        /* ### leverage code from dav_method_options ### */
-        return DAV_PROP_INSERT_NOTDEF;
-        break;
-
-    case DAV_PROPID_supported_report_set:
-#if 0
-    {
-        /* ### where to get "r" ??? */
-        const dav_hooks_vsn *vsn_hooks = dav_get_vsn_hooks(r);
-
-        if (vsn_hooks != NULL) {
-            const dav_report_elem *reports;
-            dav_error *err;
-
-            if ((err = (*vsn_hooks->avail_reports)(resource,
-                                                   &reports)) != NULL) {
-               err = dav_push_error(p, err->status, 0,
-                                     "DAV:supported-report-set could not "
-                                     "be determined due to a problem "
-                                     "fetching the available reports "
-                                     "for this resource.",
-                                     err);
-                /* ### can't return err... sigh. punt for now. */
-                return DAV_PROP_INSERT_NOTDEF;
-            }
-
-            value = "";
-
-            if (reports == NULL) {
-                /* no reports are defined. break with value="" */
-                break;
-            }
-
-            for (; reports->nmspace != NULL; ++reports) {
-                /* Note: presume reports->namespace is XML/URL quoted */
-                const char *v = apr_psprintf(p, "<%s xmlns=\"%s\"/>" DEBUG_CR,
-                                             reports->name, reports->nmspace);
-
-                /* This isn't very memory-efficient, but there should only
-                   be a small number of reports */
-                value = apr_pstrcat(p, value, v, NULL);
-            }
-        }
-        break;
-    }
-#endif
-    /* above code disabled. FALLTHROUGH */
-
     case DAV_PROPID_comment:
     case DAV_PROPID_creator_displayname:
     case DAV_PROPID_displayname:
@@ -218,19 +159,22 @@ static dav_prop_insert dav_core_insert_prop(const dav_resource *resource,
 
     /* assert: info != NULL && info->name != NULL */
 
-    if (insvalue && *value != '\0') {
+    if (what == DAV_PROP_INSERT_SUPPORTED) {
+        s = apr_psprintf(p, "<supported-live-property name=\"%s\""
+                            " namespace=\"%s\" xmlns=\"DAV:\"/>" DEBUG_CR,
+                         info->name, dav_core_namespace_uris[info->ns]);
+    }
+       else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
         s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
                          global_ns, info->name, value, global_ns, info->name);
-        which = DAV_PROP_INSERT_VALUE;
     }
     else {
         s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name);
-        which = DAV_PROP_INSERT_NAME;
     }
     ap_text_append(p, phdr, s);
 
-    /* we inserted a name or value (this prop is done) */
-    return which;
+    /* we inserted what was asked for */
+    return what;
 }
 
 static int dav_core_is_writable(const dav_resource *resource, int propid)
@@ -271,17 +215,10 @@ int dav_core_find_liveprop(const dav_resource *resource,
 
 void dav_core_insert_all_liveprops(request_rec *r,
                                    const dav_resource *resource,
-                                   int insvalue, ap_text_header *phdr)
+                                   dav_prop_insert what, ap_text_header *phdr)
 {
     (void) dav_core_insert_prop(resource, DAV_PROPID_resourcetype,
-                                insvalue, phdr);
-    (void) dav_core_insert_prop(resource,
-                                DAV_PROPID_supported_live_property_set,
-                                insvalue, phdr);
-    (void) dav_core_insert_prop(resource, DAV_PROPID_supported_method_set,
-                                insvalue, phdr);
-    (void) dav_core_insert_prop(resource, DAV_PROPID_supported_report_set,
-                                insvalue, phdr);
+                                what, phdr);
 }
 
 void dav_core_register_uris(apr_pool_t *p)