📄 mod_dav.c
字号:
apr_off_t *range_start, apr_off_t *range_end)
{
const char *range_c;
char *range;
char *dash;
char *slash;
range_c = apr_table_get(r->headers_in, "content-range");
if (range_c == NULL)
return 0;
range = apr_pstrdup(r->pool, range_c);
if (strncasecmp(range, "bytes ", 6) != 0
|| (dash = ap_strchr(range, '-')) == NULL
|| (slash = ap_strchr(range, '/')) == NULL) {
/* malformed header. ignore it (per S14.16 of RFC2616) */
return 0;
}
*dash = *slash = '\0';
*range_start = apr_atoi64(range + 6);
*range_end = apr_atoi64(dash + 1);
if (*range_end < *range_start
|| (slash[1] != '*' && apr_atoi64(slash + 1) <= *range_end)) {
/* invalid range. ignore it (per S14.16 of RFC2616) */
return 0;
}
/* we now have a valid range */
return 1;
}
/* handle the GET method */
static int dav_method_get(request_rec *r)
{
dav_resource *resource;
dav_error *err;
/* This method should only be called when the resource is not
* visible to Apache. We will fetch the resource from the repository,
* then create a subrequest for Apache to handle.
*/
err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
if (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
}
/* set up the HTTP headers for the response */
if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
"Unable to set up HTTP headers.",
err);
return dav_handle_err(r, err, NULL);
}
if (r->header_only) {
return DONE;
}
/* okay... time to deliver the content */
if ((err = (*resource->hooks->deliver)(resource,
r->output_filters)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
"Unable to deliver content.",
err);
return dav_handle_err(r, err, NULL);
}
return DONE;
}
/* validate resource/locks on POST, then pass to the default handler */
static int dav_method_post(request_rec *r)
{
dav_resource *resource;
dav_error *err;
/* Ask repository module to 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);
/* Note: depth == 0. Implies no need for a multistatus response. */
if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
/* ### add a higher-level description? */
return dav_handle_err(r, err, NULL);
}
return DECLINED;
}
/* handle the PUT method */
static int dav_method_put(request_rec *r)
{
dav_resource *resource;
int resource_state;
dav_auto_version_info av_info;
const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
const char *body;
dav_error *err;
dav_error *err2;
dav_stream_mode mode;
dav_stream *stream;
dav_response *multi_response;
int has_range;
apr_off_t range_start;
apr_off_t range_end;
/* Ask repository module to 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);
/* If not a file or collection resource, PUT not allowed */
if (resource->type != DAV_RESOURCE_TYPE_REGULAR
&& resource->type != DAV_RESOURCE_TYPE_WORKING) {
body = apr_psprintf(r->pool,
"Cannot create resource %s with PUT.",
ap_escape_html(r->pool, r->uri));
return dav_error_response(r, HTTP_CONFLICT, body);
}
/* Cannot PUT a collection */
if (resource->collection) {
return dav_error_response(r, HTTP_CONFLICT,
"Cannot PUT to a collection.");
}
resource_state = dav_get_resource_state(r, resource);
/*
* Note: depth == 0 normally requires no multistatus response. However,
* if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
* other than the Request-URI, thereby requiring a multistatus.
*
* If the resource does not exist (DAV_RESOURCE_NULL), then we must
* check the resource *and* its parent. If the resource exists or is
* a locknull resource, then we check only the resource.
*/
if ((err = dav_validate_request(r, resource, 0, NULL, &multi_response,
resource_state == DAV_RESOURCE_NULL ?
DAV_VALIDATE_PARENT :
DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
/* ### add a higher-level description? */
return dav_handle_err(r, err, multi_response);
}
/* make sure the resource can be modified (if versioning repository) */
if ((err = dav_auto_checkout(r, resource,
0 /* not parent_only */,
&av_info)) != NULL) {
/* ### add a higher-level description? */
return dav_handle_err(r, err, NULL);
}
/* truncate and rewrite the file unless we see a Content-Range */
mode = DAV_MODE_WRITE_TRUNC;
has_range = dav_parse_range(r, &range_start, &range_end);
if (has_range) {
mode = DAV_MODE_WRITE_SEEKABLE;
}
/* Create the new file in the repository */
if ((err = (*resource->hooks->open_stream)(resource, mode,
&stream)) != NULL) {
/* ### assuming FORBIDDEN is probably not quite right... */
err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0,
apr_psprintf(r->pool,
"Unable to PUT new contents for %s.",
ap_escape_html(r->pool, r->uri)),
err);
}
if (err == NULL && has_range) {
/* a range was provided. seek to the start */
err = (*resource->hooks->seek_stream)(stream, range_start);
}
if (err == NULL) {
apr_bucket_brigade *bb;
apr_bucket *b;
int seen_eos = 0;
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
do {
apr_status_t rc;
rc = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, DAV_READ_BLOCKSIZE);
if (rc != APR_SUCCESS) {
err = dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not get next bucket brigade");
break;
}
for (b = APR_BRIGADE_FIRST(bb);
b != APR_BRIGADE_SENTINEL(bb);
b = APR_BUCKET_NEXT(b))
{
const char *data;
apr_size_t len;
if (APR_BUCKET_IS_EOS(b)) {
seen_eos = 1;
break;
}
if (APR_BUCKET_IS_METADATA(b)) {
continue;
}
rc = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
if (rc != APR_SUCCESS) {
err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
"An error occurred while reading "
"the request body.");
break;
}
if (err == NULL) {
/* write whatever we read, until we see an error */
err = (*resource->hooks->write_stream)(stream, data, len);
}
}
apr_brigade_cleanup(bb);
} while (!seen_eos);
apr_brigade_destroy(bb);
err2 = (*resource->hooks->close_stream)(stream,
err == NULL /* commit */);
if (err2 != NULL && err == NULL) {
/* no error during the write, but we hit one at close. use it. */
err = err2;
}
}
/*
* Ensure that we think the resource exists now.
* ### eek. if an error occurred during the write and we did not commit,
* ### then the resource might NOT exist (e.g. dav_fs_repos.c)
*/
if (err == NULL) {
resource->exists = 1;
}
/* restore modifiability of resources back to what they were */
err2 = dav_auto_checkin(r, resource, err != NULL /* undo if error */,
0 /*unlock*/, &av_info);
/* check for errors now */
if (err != NULL) {
return dav_handle_err(r, err, NULL);
}
if (err2 != NULL) {
/* just log a warning */
err2 = dav_push_error(r->pool, err2->status, 0,
"The PUT was successful, but there "
"was a problem automatically checking in "
"the resource or its parent collection.",
err2);
dav_log_err(r, err2, APLOG_WARNING);
}
/* ### place the Content-Type and Content-Language into the propdb */
if (locks_hooks != NULL) {
dav_lockdb *lockdb;
if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
/* The file creation was successful, but the locking failed. */
err = dav_push_error(r->pool, err->status, 0,
"The file was PUT successfully, but there "
"was a problem opening the lock database "
"which prevents inheriting locks from the "
"parent resources.",
err);
return dav_handle_err(r, err, NULL);
}
/* notify lock system that we have created/replaced a resource */
err = dav_notify_created(r, lockdb, resource, resource_state, 0);
(*locks_hooks->close_lockdb)(lockdb);
if (err != NULL) {
/* The file creation was successful, but the locking failed. */
err = dav_push_error(r->pool, err->status, 0,
"The file was PUT successfully, but there "
"was a problem updating its lock "
"information.",
err);
return dav_handle_err(r, err, NULL);
}
}
/* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
/* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
}
/* Use POOL to temporarily construct a dav_response object (from WRES
STATUS, and PROPSTATS) and stream it via WRES's ctx->brigade. */
static void dav_stream_response(dav_walk_resource *wres,
int status,
dav_get_props_result *propstats,
apr_pool_t *pool)
{
dav_response resp = { 0 };
dav_walker_ctx *ctx = wres->walk_ctx;
resp.href = wres->resource->uri;
resp.status = status;
if (propstats) {
resp.propresult = *propstats;
}
dav_send_one_response(&resp, ctx->bb, ctx->r->output_filters, pool);
}
/* ### move this to dav_util? */
DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres,
int status, dav_get_props_result *propstats)
{
dav_response *resp;
/* just drop some data into an dav_response */
resp = apr_pcalloc(wres->pool, sizeof(*resp));
resp->href = apr_pstrdup(wres->pool, wres->resource->uri);
resp->status = status;
if (propstats) {
resp->propresult = *propstats;
}
resp->next = wres->response;
wres->response = resp;
}
/* handle the DELETE method */
static int dav_method_delete(request_rec *r)
{
dav_resource *resource;
dav_auto_version_info av_info;
dav_error *err;
dav_error *err2;
dav_response *multi_response;
int result;
int depth;
/* We don't use the request body right now, so torch it. */
if ((result = ap_discard_request_body(r)) != OK) {
return result;
}
/* Ask repository module to 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);
if (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -