You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by gs...@apache.org on 2001/01/20 03:00:01 UTC

cvs commit: httpd-2.0/modules/dav/main mod_dav.c mod_dav.h props.c std_liveprop.c

gstein      01/01/19 18:00:01

  Modified:    modules/dav/fs repos.c repos.h
               modules/dav/main mod_dav.c mod_dav.h props.c std_liveprop.c
  Log:
  - implement DeltaV OPTIONS extensions
  - let live prop providers get first crack at PROPFIND
  - work around MS Web Folders limit on DAV header length
  
  Submitted by: John Vasta <jv...@rational.com>
  Reviewed by: Greg Stein
  
  Revision  Changes    Path
  1.42      +17 -14    httpd-2.0/modules/dav/fs/repos.c
  
  Index: repos.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/fs/repos.c,v
  retrieving revision 1.41
  retrieving revision 1.42
  diff -u -u -r1.41 -r1.42
  --- repos.c	2001/01/12 12:18:10	1.41
  +++ repos.c	2001/01/20 02:00:00	1.42
  @@ -1717,12 +1717,11 @@
   };
   
   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 @@
   
       /* 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 @@
   }
   
   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_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 @@
       **       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 */
  
  
  
  1.13      +1 -1      httpd-2.0/modules/dav/fs/repos.h
  
  Index: repos.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/fs/repos.h,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -u -r1.12 -r1.13
  --- repos.h	2000/11/27 12:54:08	1.12
  +++ repos.h	2001/01/20 02:00:00	1.13
  @@ -103,7 +103,7 @@
                            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);
   
  
  
  
  1.38      +446 -88   httpd-2.0/modules/dav/main/mod_dav.c
  
  Index: mod_dav.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/mod_dav.c,v
  retrieving revision 1.37
  retrieving revision 1.38
  diff -u -u -r1.37 -r1.38
  --- mod_dav.c	2001/01/19 07:04:17	1.37
  +++ mod_dav.c	2001/01/20 02:00:01	1.38
  @@ -1261,6 +1261,244 @@
       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 @@
       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)
  -            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);
  +        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", "");
  +        }
       }
   
       /* 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);
  +
  +    /* 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);
   
  -    /* ### 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. */
  +    ap_rputs("</D:options-response>" DEBUG_CR, r);
   
       /* we've sent everything necessary to the client. */
       return DONE;
  @@ -1489,8 +1845,10 @@
   	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 @@
                                        (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));
  
  
  
  1.37      +63 -28    httpd-2.0/modules/dav/main/mod_dav.h
  
  Index: mod_dav.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/mod_dav.h,v
  retrieving revision 1.36
  retrieving revision 1.37
  diff -u -u -r1.36 -r1.37
  --- mod_dav.h	2001/01/19 07:04:17	1.36
  +++ mod_dav.h	2001/01/20 02:00:01	1.37
  @@ -489,6 +489,25 @@
   
   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, 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 @@
   ** 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 @@
                              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 @@
       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_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 @@
       ** they define the functionality needed to implement "core" versioning.
       */
   
  -    /* Return supported versioning level
  -     * for the Versioning header
  -     */
  -    const char * (*get_vsn_header)(void);
  +    /* 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.
  +     */
  +    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 @@
       ** 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 ###
  
  
  
  1.22      +232 -156  httpd-2.0/modules/dav/main/props.c
  
  Index: props.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/props.c,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -u -r1.21 -r1.22
  --- props.c	2001/01/19 07:04:17	1.21
  +++ props.c	2001/01/20 02:00:01	1.22
  @@ -324,46 +324,60 @@
   }
   #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;
  +
  +    *provider = NULL;
   
  -    if (elem->ns == AP_XML_NS_DAV_ID) {
  +    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;
       }
  +
  +    /* no provider for this property */
  +    return DAV_PROPID_CORE_UNKNOWN;
  +}
   
  -    priv->propid = DAV_PROPID_CORE_UNKNOWN;
  -    /* priv->provider == NULL */
  +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 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 @@
   	break;
       }
   
  -    case DAV_PROPID_CORE_UNKNOWN:
       default:
   	/* fall through to interpret as a dead property */
   	break;
  @@ -500,7 +513,15 @@
       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 @@
   	}
   	ap_text_append(propdb->p, phdr, s);
   
  -	*inserted = 1;
  +	*inserted = what;
       }
   
       return NULL;
  @@ -519,26 +540,23 @@
   
   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_propdb **p_propdb)
   {
       dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
  -    dav_error *err;
   
       *p_propdb = NULL;
   
  @@ -931,12 +948,10 @@
   
       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 @@
       (*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 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;
  -
  -	    /* 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;
  +        /* generate all the namespaces that are in the propdb */
  +        dav_get_propdb_xmlns(propdb, &hdr_ns);
   
  -	    /*
  -	    ** 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 {
  -		    colon = strchr(key.dptr + 2, ':');
  -		}
  +        /* 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);
  +
  +	    (void) (*db_hooks->firstkey)(propdb->db, &key);
  +	    while (key.dptr) {
  +	        dav_datum prevkey;
  +
  +	        /* 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 @@
   	(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 @@
       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 @@
   		   "<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 @@
       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;
  -
  -	/*
  -	** 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);
  +	dav_error *err;
  +	dav_prop_insert inserted;
  +        int is_liveprop = 0;
   
  -	/* 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 @@
   
   		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 */
  @@ -1280,6 +1332,30 @@
   
       result.xmlns = hdr_ns.first;
       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)
  
  
  
  1.4       +11 -74    httpd-2.0/modules/dav/main/std_liveprop.c
  
  Index: std_liveprop.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/std_liveprop.c,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -u -r1.3 -r1.4
  --- std_liveprop.c	2000/11/27 12:54:28	1.3
  +++ std_liveprop.c	2001/01/20 02:00:01	1.4
  @@ -82,10 +82,6 @@
       { 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 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 @@
           }
           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 @@
   
       /* 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 @@
   
   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)