in modules/dav/main/mod_dav.c [1769:2109]
static int dav_method_options(request_rec *r)
{
const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r);
const dav_hooks_search *search_hooks = DAV_GET_HOOKS_SEARCH(r);
dav_resource *resource;
const char *dav_level;
char *allow;
char *s;
const apr_array_header_t *arr;
const apr_table_entry_t *elts;
apr_table_t *methods = apr_table_make(r->pool, 12);
apr_text_header vsn_options = { 0 };
apr_text_header body = { 0 };
apr_text *t;
int text_size;
int result;
int i;
apr_array_header_t *uri_ary;
apr_xml_doc *doc;
const apr_xml_elem *elem;
dav_error *err;
apr_array_header_t *extensions;
ap_list_provider_names_t *entry;
/* resolve the resource */
err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* parse any request body */
if ((result = ap_xml_parse_input(r, &doc)) != OK) {
return result;
}
/* note: doc == NULL if no request body */
/* check for any method preconditions */
if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
if (doc && !dav_validate_root(doc, "options")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00584)
"The \"options\" element was not found.");
return HTTP_BAD_REQUEST;
}
/* determine which providers are available */
dav_level = "1";
if (locks_hooks != NULL) {
dav_level = "1,2";
}
if (binding_hooks != NULL)
dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
/* DAV header additions registered by external modules */
extensions = ap_list_provider_names(r->pool, DAV_OPTIONS_EXTENSION_GROUP, "0");
entry = (ap_list_provider_names_t *)extensions->elts;
for (i = 0; i < extensions->nelts; i++, entry++) {
const dav_options_provider *options =
dav_get_options_providers(entry->provider_name);
if (options && options->dav_header) {
apr_text_header hoptions = { 0 };
options->dav_header(r, resource, &hoptions);
for (t = hoptions.first; t && t->text; t = t->next)
dav_level = apr_pstrcat(r->pool, dav_level, ",", t->text, 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,
* and generate a separate DAV header for each URI, to avoid
* problems with long header lengths.
*/
uri_ary = apr_array_make(r->pool, 5, sizeof(const char *));
dav_run_gather_propsets(uri_ary);
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 if there is a lock provider, LOCK.
* (Lock-) null resources also support MKCOL and PUT.
* 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 */
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. */
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. */
apr_table_addn(methods, "MKCOL", "");
apr_table_addn(methods, "PUT", "");
if (locks_hooks != NULL)
apr_table_addn(methods, "LOCK", "");
break;
default:
/* ### internal error! */
break;
}
/* If there is a versioning provider, add versioning methods */
if (vsn_hooks != NULL) {
if (!resource->exists) {
if ((*vsn_hooks->versionable)(resource))
apr_table_addn(methods, "VERSION-CONTROL", "");
if (vsn_hooks->can_be_workspace != NULL
&& (*vsn_hooks->can_be_workspace)(resource))
apr_table_addn(methods, "MKWORKSPACE", "");
if (vsn_hooks->can_be_activity != NULL
&& (*vsn_hooks->can_be_activity)(resource))
apr_table_addn(methods, "MKACTIVITY", "");
}
else if (!resource->versioned) {
if ((*vsn_hooks->versionable)(resource))
apr_table_addn(methods, "VERSION-CONTROL", "");
}
else if (resource->working) {
apr_table_addn(methods, "CHECKIN", "");
/* ### we might not support this DeltaV option */
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
&& (*binding_hooks->is_bindable)(resource)) {
apr_table_addn(methods, "BIND", "");
}
/* If there is a search provider, set SEARCH in option */
if (search_hooks != NULL) {
apr_table_addn(methods, "SEARCH", "");
}
/* additional methods registered by external modules */
extensions = ap_list_provider_names(r->pool, DAV_OPTIONS_EXTENSION_GROUP, "0");
entry = (ap_list_provider_names_t *)extensions->elts;
for (i = 0; i < extensions->nelts; i++, entry++) {
const dav_options_provider *options =
dav_get_options_providers(entry->provider_name);
if (options && options->dav_method) {
apr_text_header hoptions = { 0 };
options->dav_method(r, resource, &hoptions);
for (t = hoptions.first; t && t->text; t = t->next)
apr_table_addn(methods, t->text, "");
}
}
/* Generate the Allow header */
arr = apr_table_elts(methods);
elts = (const 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;
if (s != allow)
*s++ = ',';
strcpy(s, elts[i].key);
s += strlen(s);
}
apr_table_setn(r->headers_out, "Allow", allow);
/* If there is search set_option_head function, set head */
/* DASL: <DAV:basicsearch>
* DASL: <http://foo.bar.com/syntax1>
* DASL: <http://akuma.com/syntax2>
*/
if (search_hooks != NULL
&& *search_hooks->set_option_head != NULL) {
if ((err = (*search_hooks->set_option_head)(r)) != NULL) {
return dav_handle_err(r, err, NULL);
}
}
/* if there was no request body, then there is no response body */
if (doc == NULL) {
ap_set_content_length(r, 0);
/* ### this sends a Content-Type. the default OPTIONS does not. */
/* ### 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 == APR_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, &body);
core_option = 1;
}
}
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* if unrecognized option, pass to versioning provider */
if (!core_option && vsn_hooks != NULL) {
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;
ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
/* send the headers and 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;
}