--- httpd-2.2.4/modules/dav/main/util.c 2006-07-12 05:38:44.000000000 +0200 +++ httpd-2.2.4-new/modules/dav/main/util.c 2007-07-26 18:30:01.000000000 +0200 @@ -19,6 +19,7 @@ ** - various utilities, repository-independent */ +#include "apr_date.h" #include "apr_strings.h" #include "apr_lib.h" @@ -1404,6 +1405,141 @@ return NULL; } +/** This is a copy of ap_meets_conditions with this changes: + * - additional parameter resource + * - use (*resource->hooks->getetag)(resource) to get etag, as + * apr_table_get(r->headers_out, "ETag") does not work in this context. + * - "If-None-Match: *" evaluates to TRUE if the resource does not exist. + * - "If-Match: *" evaluates to TRUE if the resource exists. + */ +static int dav_meets_conditions(request_rec *r, dav_resource *resource) +{ + const char *etag; + const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch; + apr_time_t tmp_time; + apr_int64_t mtime; + int not_modified = 0; + + /* Check for conditional requests --- note that we only want to do + * this if we are successful so far and we are not processing a + * subrequest or an ErrorDocument. + * + * The order of the checks is important, since ETag checks are supposed + * to be more accurate than checks relative to the modification time. + * However, not all documents are guaranteed to *have* ETags, and some + * might have Last-Modified values w/o ETags, so this gets a little + * complicated. + */ + + if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) { + return OK; + } + + etag = (*resource->hooks->getetag)(resource); + + /* All of our comparisons must be in seconds, because that's the + * highest time resolution the HTTP specification allows. + */ + /* XXX: we should define a "time unset" constant */ + tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now()); + mtime = apr_time_sec(tmp_time); + + /* If an If-Match request-header field was given + * AND ( the field value is not "*" (meaning match anything) + * OR the resource does not exist ) + * AND if our strong ETag does not match any entity tag in that field, + * respond with a status of 412 (Precondition Failed). + */ + if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) { + if (!(if_match[0] == '*' && resource->exists) + && (etag == NULL || etag[0] == 'W' + || !ap_find_list_item(r->pool, if_match, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + else { + /* Else if a valid If-Unmodified-Since request-header field was given + * AND the requested resource has been modified since the time + * specified in this field, then the server MUST + * respond with a status of 412 (Precondition Failed). + */ + if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since"); + if (if_unmodified != NULL) { + apr_time_t ius = apr_date_parse_http(if_unmodified); + + if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) { + return HTTP_PRECONDITION_FAILED; + } + } + } + + /* If an If-None-Match request-header field was given + * AND the field value is "*" (meaning match anything) + * AND the resource exists + * OR our ETag matches any of the entity tags in that field, fail. + * + * If the request method was GET or HEAD, failure means the server + * SHOULD respond with a 304 (Not Modified) response. + * For all other request methods, failure means the server MUST + * respond with a status of 412 (Precondition Failed). + * + * GET or HEAD allow weak etag comparison, all other methods require + * strong comparison. We can only use weak if it's not a range request. + */ + if_nonematch = apr_table_get(r->headers_in, "If-None-Match"); + if (if_nonematch != NULL) { + if (r->method_number == M_GET) { + if (if_nonematch[0] == '*') { + not_modified = 1; + } + else if (etag != NULL) { + if (apr_table_get(r->headers_in, "Range")) { + not_modified = etag[0] != 'W' + && ap_find_list_item(r->pool, + if_nonematch, etag); + } + else { + not_modified = ap_find_list_item(r->pool, + if_nonematch, etag); + } + } + } + else if ((if_nonematch[0] == '*' && resource->exists) + || (etag != NULL + && ap_find_list_item(r->pool, if_nonematch, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + + /* If a valid If-Modified-Since request-header field was given + * AND it is a GET or HEAD request + * AND the requested resource has not been modified since the time + * specified in this field, then the server MUST + * respond with a status of 304 (Not Modified). + * A date later than the server's current request time is invalid. + */ + if (r->method_number == M_GET + && (not_modified || !if_nonematch) + && (if_modified_since = + apr_table_get(r->headers_in, + "If-Modified-Since")) != NULL) { + apr_time_t ims_time; + apr_int64_t ims, reqtime; + + ims_time = apr_date_parse_http(if_modified_since); + ims = apr_time_sec(ims_time); + reqtime = apr_time_sec(r->request_time); + + not_modified = ims >= mtime && ims <= reqtime; + } + + if (not_modified) { + return HTTP_NOT_MODIFIED; + } + + return OK; +} + /* ** dav_validate_request: Validate if-headers (and check for locks) on: ** (1) r->filename @ depth; @@ -1450,9 +1586,9 @@ *response = NULL; /* Do the standard checks for conditional requests using - * If-..-Since, If-Match etc */ - if ((result = ap_meets_conditions(r)) != OK) { - /* ### fix this up... how? */ + * If-..-Since, If-Match etc + * use dav_meets_conditions instead of ap_meets_conditions. */ + if ((result = dav_meets_conditions(r, resource)) != OK) { return dav_new_error(r->pool, result, 0, NULL); }