📄 util_lock.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
** DAV repository-independent lock functions
*/
#include "apr.h"
#include "apr_strings.h"
#if APR_HAVE_STDIO_H
#include <stdio.h> /* for sprintf() */
#endif
#include "mod_dav.h"
#include "http_log.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_core.h"
/* ---------------------------------------------------------------
**
** Property-related lock functions
**
*/
/*
** dav_lock_get_activelock: Returns a <lockdiscovery> containing
** an activelock element for every item in the lock_discovery tree
*/
DAV_DECLARE(const char *) dav_lock_get_activelock(request_rec *r,
dav_lock *lock,
dav_buffer *pbuf)
{
dav_lock *lock_scan;
const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
int count = 0;
dav_buffer work_buf = { 0 };
apr_pool_t *p = r->pool;
/* If no locks or no lock provider, there are no locks */
if (lock == NULL || hooks == NULL) {
/*
** Since resourcediscovery is defined with (activelock)*,
** <D:activelock/> shouldn't be necessary for an empty lock.
*/
return "";
}
/*
** Note: it could be interesting to sum the lengths of the owners
** and locktokens during this loop. However, the buffer
** mechanism provides some rough padding so that we don't
** really need to have an exact size. Further, constructing
** locktoken strings could be relatively expensive.
*/
for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next)
count++;
/* if a buffer was not provided, then use an internal buffer */
if (pbuf == NULL)
pbuf = &work_buf;
/* reset the length before we start appending stuff */
pbuf->cur_len = 0;
/* prep the buffer with a "good" size */
dav_check_bufsize(p, pbuf, count * 300);
for (; lock != NULL; lock = lock->next) {
char tmp[100];
#if DAV_DEBUG
if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) {
/* ### crap. design error */
dav_buffer_append(p, pbuf,
"DESIGN ERROR: attempted to product an "
"activelock element from a partial, indirect "
"lock record. Creating an XML parsing error "
"to ease detection of this situation: <");
}
#endif
dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>");
switch (lock->type) {
case DAV_LOCKTYPE_WRITE:
dav_buffer_append(p, pbuf, "<D:write/>");
break;
default:
/* ### internal error. log something? */
break;
}
dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>");
switch (lock->scope) {
case DAV_LOCKSCOPE_EXCLUSIVE:
dav_buffer_append(p, pbuf, "<D:exclusive/>");
break;
case DAV_LOCKSCOPE_SHARED:
dav_buffer_append(p, pbuf, "<D:shared/>");
break;
default:
/* ### internal error. log something? */
break;
}
dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR);
sprintf(tmp, "<D:depth>%s</D:depth>" DEBUG_CR,
lock->depth == DAV_INFINITY ? "infinity" : "0");
dav_buffer_append(p, pbuf, tmp);
if (lock->owner) {
/*
** This contains a complete, self-contained <DAV:owner> element,
** with namespace declarations and xml:lang handling. Just drop
** it in.
*/
dav_buffer_append(p, pbuf, lock->owner);
}
dav_buffer_append(p, pbuf, "<D:timeout>");
if (lock->timeout == DAV_TIMEOUT_INFINITE) {
dav_buffer_append(p, pbuf, "Infinite");
}
else {
time_t now = time(NULL);
sprintf(tmp, "Second-%lu", (long unsigned int)(lock->timeout - now));
dav_buffer_append(p, pbuf, tmp);
}
dav_buffer_append(p, pbuf,
"</D:timeout>" DEBUG_CR
"<D:locktoken>" DEBUG_CR
"<D:href>");
dav_buffer_append(p, pbuf,
(*hooks->format_locktoken)(p, lock->locktoken));
dav_buffer_append(p, pbuf,
"</D:href>" DEBUG_CR
"</D:locktoken>" DEBUG_CR
"</D:activelock>" DEBUG_CR);
}
return pbuf->buf;
}
/*
** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a
** lockinfo XML element, then populates a dav_lock structure
** with its contents.
*/
DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r,
const dav_resource *resource,
dav_lockdb *lockdb,
const apr_xml_doc *doc,
dav_lock **lock_request)
{
apr_pool_t *p = r->pool;
dav_error *err;
apr_xml_elem *child;
dav_lock *lock;
if (!dav_validate_root(doc, "lockinfo")) {
return dav_new_error(p, HTTP_BAD_REQUEST, 0,
"The request body contains an unexpected "
"XML root element.");
}
if ((err = (*lockdb->hooks->create_lock)(lockdb, resource,
&lock)) != NULL) {
return dav_push_error(p, err->status, 0,
"Could not parse the lockinfo due to an "
"internal problem creating a lock structure.",
err);
}
lock->depth = dav_get_depth(r, DAV_INFINITY);
if (lock->depth == -1) {
return dav_new_error(p, HTTP_BAD_REQUEST, 0,
"An invalid Depth header was specified.");
}
lock->timeout = dav_get_timeout(r);
/* Parse elements in the XML body */
for (child = doc->root->first_child; child; child = child->next) {
if (strcmp(child->name, "locktype") == 0
&& child->first_child
&& lock->type == DAV_LOCKTYPE_UNKNOWN) {
if (strcmp(child->first_child->name, "write") == 0) {
lock->type = DAV_LOCKTYPE_WRITE;
continue;
}
}
if (strcmp(child->name, "lockscope") == 0
&& child->first_child
&& lock->scope == DAV_LOCKSCOPE_UNKNOWN) {
if (strcmp(child->first_child->name, "exclusive") == 0)
lock->scope = DAV_LOCKSCOPE_EXCLUSIVE;
else if (strcmp(child->first_child->name, "shared") == 0)
lock->scope = DAV_LOCKSCOPE_SHARED;
if (lock->scope != DAV_LOCKSCOPE_UNKNOWN)
continue;
}
if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) {
const char *text;
/* quote all the values in the <DAV:owner> element */
apr_xml_quote_elem(p, child);
/*
** Store a full <DAV:owner> element with namespace definitions
** and an xml:lang definition, if applicable.
*/
apr_xml_to_text(p, child, APR_XML_X2T_FULL_NS_LANG, doc->namespaces,
NULL, &text, NULL);
lock->owner = text;
continue;
}
return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0,
apr_psprintf(p,
"The server cannot satisfy the "
"LOCK request due to an unknown XML "
"element (\"%s\") within the "
"DAV:lockinfo element.",
child->name));
}
*lock_request = lock;
return NULL;
}
/* ---------------------------------------------------------------
**
** General lock functions
**
*/
/* dav_lock_walker: Walker callback function to record indirect locks */
static dav_error * dav_lock_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
dav_error *err;
/* We don't want to set indirects on the target */
if ((*wres->resource->hooks->is_same_resource)(wres->resource,
ctx->w.root))
return NULL;
if ((err = (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb,
wres->resource, 1,
ctx->lock)) != NULL) {
if (ap_is_HTTP_SERVER_ERROR(err->status)) {
/* ### add a higher-level description? */
return err;
}
/* add to the multistatus response */
dav_add_response(wres, err->status, NULL);
/*
** ### actually, this is probably wrong: we want to fail the whole
** ### LOCK process if something goes bad. maybe the caller should
** ### do a dav_unlock() (e.g. a rollback) if any errors occurred.
*/
}
return NULL;
}
/*
** dav_add_lock: Add a direct lock for resource, and indirect locks for
** all children, bounded by depth.
** ### assume request only contains one lock
*/
DAV_DECLARE(dav_error *) dav_add_lock(request_rec *r,
const dav_resource *resource,
dav_lockdb *lockdb, dav_lock *lock,
dav_response **response)
{
dav_error *err;
int depth = lock->depth;
*response = NULL;
/* Requested lock can be:
* Depth: 0 for null resource, existing resource, or existing collection
* Depth: Inf for existing collection
*/
/*
** 2518 9.2 says to ignore depth if target is not a collection (it has
** no internal children); pretend the client gave the correct depth.
*/
if (!resource->collection) {
depth = 0;
}
/* In all cases, first add direct entry in lockdb */
/*
** Append the new (direct) lock to the resource's existing locks.
**
** Note: this also handles locknull resources
*/
if ((err = (*lockdb->hooks->append_locks)(lockdb, resource, 0,
lock)) != NULL) {
/* ### maybe add a higher-level description */
return err;
}
if (depth > 0) {
/* Walk existing collection and set indirect locks */
dav_walker_ctx ctx = { { 0 } };
dav_response *multi_status;
ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH;
ctx.w.func = dav_lock_walker;
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
ctx.w.root = resource;
ctx.w.lockdb = lockdb;
ctx.r = r;
ctx.lock = lock;
err = (*resource->hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
if (err != NULL) {
/* implies a 5xx status code occurred. screw the multistatus */
return err;
}
if (multi_status != NULL) {
/* manufacture a 207 error for the multistatus response */
*response = multi_status;
return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
"Error(s) occurred on resources during the "
"addition of a depth lock.");
}
}
return NULL;
}
/*
** dav_lock_query: Opens the lock database. Returns a linked list of
** dav_lock structures for all direct locks on path.
*/
DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb,
const dav_resource *resource,
dav_lock **locks)
{
/* If no lock database, return empty result */
if (lockdb == NULL) {
*locks = NULL;
return NULL;
}
/* ### insert a higher-level description? */
return (*lockdb->hooks->get_locks)(lockdb, resource,
DAV_GETLOCKS_RESOLVED,
locks);
}
/* dav_unlock_walker: Walker callback function to remove indirect locks */
static dav_error * dav_unlock_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
dav_error *err;
/* Before removing the lock, do any auto-checkin required */
if (wres->resource->working) {
/* ### get rid of this typecast */
if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource,
0 /*undo*/, 1 /*unlock*/, NULL))
!= NULL) {
return err;
}
}
if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb,
wres->resource,
ctx->locktoken)) != NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -