/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
/* src error doesn't exist so nothing to join just return dest */ if (src == NULL) { return dest;
}
/* dest error doesn't exist so nothing to join just return src */ if (curr == NULL) { return src;
}
/* find last error in dest stack */ while (curr->prev != NULL) {
curr = curr->prev;
}
/* add the src error onto end of dest stack and return it */
curr->prev = src; return dest;
}
/* ### Unclear if this was designed to be used with an uninitialized *dav_bufferstruct,butisusedonbydav_lock_get_activelock().
* Hence check for pbuf->buf. */
DAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf,
apr_size_t extra_needed)
{ /* grow the buffer if necessary */ if (pbuf->cur_len + extra_needed > pbuf->alloc_len) { char *newbuf;
DAV_DECLARE(void) dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf,
apr_size_t size)
{ /* NOTE: this does not retain prior contents */
/* NOTE: this function is used to init the first pointer, too, since
the PAD will be larger than alloc_len (0) for zeroed structures */
/* grow if we don't have enough for the requested size plus padding */ if (size + DAV_BUFFER_PAD > pbuf->alloc_len) { /* set the new length; min of MINSIZE */
pbuf->alloc_len = size + DAV_BUFFER_PAD; if (pbuf->alloc_len < DAV_BUFFER_MINSIZE)
pbuf->alloc_len = DAV_BUFFER_MINSIZE;
/* initialize a buffer and copy the specified (null-term'd) string into it */
DAV_DECLARE(void) dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, constchar *str)
{
dav_set_bufsize(p, pbuf, strlen(str));
memcpy(pbuf->buf, str, pbuf->cur_len + 1);
}
/* append a string to the end of the buffer, adjust length */
DAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, constchar *str)
{
apr_size_t len = strlen(str);
dav_check_bufsize(p, pbuf, len + 1);
memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
pbuf->cur_len += len;
}
/* place a string on the end of the buffer, do NOT adjust length */
DAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, constchar *str)
{
apr_size_t len = strlen(str);
dav_check_bufsize(p, pbuf, len + 1);
memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
}
/* place some memory on the end of a buffer; do NOT adjust length */
DAV_DECLARE(void) dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, constvoid *mem, apr_size_t amt,
apr_size_t pad)
{
dav_check_bufsize(p, pbuf, amt + pad);
memcpy(pbuf->buf + pbuf->cur_len, mem, amt);
}
/* first thing to do is parse the URI into various components */ if (apr_uri_parse(r->pool, uri, &comp) != APR_SUCCESS) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc = "Invalid syntax in Destination URI."; return result;
}
/* the URI must be an absoluteURI (WEBDAV S9.3) */ if (comp.scheme == NULL && must_be_absolute) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc = "Destination URI must be an absolute URI."; return result;
}
/* the URI must not have a query (args) or a fragment */ if (comp.query != NULL || comp.fragment != NULL) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc = "Destination URI contains invalid components " "(a query or a fragment)."; return result;
}
/* If the scheme or port was provided, then make sure that it matches thescheme/portofthisrequest.Iftherequestmustbeabsolute, thenrequirethe(explicit/implicit)scheme/portbematching.
###hmm.ifaportwasn'tprovided(doestheparsereturnport==0?), ###butwe'reonanon-standardport,thenwewon'tdetectthatthe ###URI'sportimpliesthewrongone.
*/ if (comp.scheme != NULL || comp.port != 0 || must_be_absolute)
{ /* ### not sure this works if the current request came in via https: */
scheme = r->parsed_uri.scheme; if (scheme == NULL)
scheme = ap_http_scheme(r);
/* insert a port if the URI did not contain one */ if (comp.port == 0)
comp.port = apr_uri_port_of_scheme(comp.scheme);
/* now, verify that the URI uses the same scheme as the current. request.theportmustmatchourport.
*/
port = r->connection->local_addr->port; if (ap_cstr_casecmp(comp.scheme, scheme) != 0 #ifdef APACHE_PORT_HANDLING_IS_BUSTED
|| comp.port != port #endif
) {
result.err.status = HTTP_BAD_GATEWAY;
result.err.desc = apr_psprintf(r->pool, "Destination URI refers to " "different scheme or port " "(%s://hostname:%d)" APR_EOL_STR "(want: %s://hostname:%d)",
comp.scheme ? comp.scheme : scheme,
comp.port ? comp.port : port,
scheme, port); return result;
}
}
/* we have verified the scheme, port, and general structure */
/* now, if a hostname was provided, then verify that it represents the sameserverasthecurrentconnection.notethatwejustuseour
port, since we've verified the URI matches ours */ #ifdef APACHE_PORT_HANDLING_IS_BUSTED if (comp.hostname != NULL &&
!ap_matches_request_vhost(r, comp.hostname, port)) {
result.err.status = HTTP_BAD_GATEWAY;
result.err.desc = "Destination URI refers to a different server."; return result;
} #endif
/* we have verified that the requested URI denotes the same server as
the current request. Therefore, we can use ap_sub_req_lookup_uri() */
/* reconstruct a URI as just the path */
new_file = apr_uri_unparse(r->pool, &comp, APR_URI_UNP_OMITSITEPART);
/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
DAV_DECLARE(int) dav_validate_root_ns(const apr_xml_doc *doc, int ns, constchar *tagname)
{ return doc->root &&
doc->root->ns == ns &&
strcmp(doc->root->name, tagname) == 0;
}
/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc, constchar *tagname)
{ return dav_validate_root_ns(doc, APR_XML_NS_DAV_ID, tagname);
}
/* find and return the next child with a tagname in the given namespace */
DAV_DECLARE(apr_xml_elem *) dav_find_next_ns(const apr_xml_elem *elem, int ns, constchar *tagname)
{
apr_xml_elem *child = elem->next;
for (; child; child = child->next) if (child->ns == ns && !strcmp(child->name, tagname)) return child; return NULL;
}
/* find and return the (unique) child with a tagname in the given namespace */
DAV_DECLARE(apr_xml_elem *) dav_find_child_ns(const apr_xml_elem *elem, int ns, constchar *tagname)
{
apr_xml_elem *child = elem->first_child;
for (; child; child = child->next) if (child->ns == ns && !strcmp(child->name, tagname)) return child; return NULL;
}
/* find and return the (unique) child with a given DAV: tagname */
DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem, constchar *tagname)
{ return dav_find_child_ns(elem, APR_XML_NS_DAV_ID, tagname);
}
/* find and return the attribute with a name in the given namespace */
DAV_DECLARE(apr_xml_attr *) dav_find_attr_ns(const apr_xml_elem *elem, int ns, constchar *attrname)
{
apr_xml_attr *attr = elem->attr;
for (; attr; attr = attr->next) if (attr->ns == ns && !strcmp(attr->name, attrname)) return attr; return NULL;
}
/* find and return the attribute with a given DAV: tagname */
DAV_DECLARE(apr_xml_attr *) dav_find_attr(const apr_xml_elem *elem, constchar *attrname)
{ return dav_find_attr_ns(elem, APR_XML_NS_DAV_ID, attrname);
}
/* gather up all the CDATA into a single string */
DAV_DECLARE(constchar *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool, int strip_white)
{
apr_size_t len = 0;
apr_text *scan; const apr_xml_elem *child; char *cdata; char *s;
apr_size_t tlen; constchar *found_text = NULL; /* initialize to avoid gcc warning */ int found_count = 0;
for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
found_text = scan->text;
++found_count;
len += strlen(found_text);
}
DAV_DECLARE(void) dav_xmlns_add(dav_xmlns_info *xi, constchar *prefix, constchar *uri)
{ /* this "should" not overwrite a prefix mapping */
apr_hash_set(xi->prefix_uri, prefix, APR_HASH_KEY_STRING, uri);
/* note: this may overwrite an existing URI->prefix mapping, but it
doesn't matter -- any prefix is usable to specify the URI. */
apr_hash_set(xi->uri_prefix, uri, APR_HASH_KEY_STRING, prefix);
}
/* Use the first thing we understand, or infinity if *wedon'tunderstandanything.
*/
while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) { if (!strncmp(val, "Infinite", 8)) { return DAV_TIMEOUT_INFINITE;
}
if (!strncmp(val, "Second-", 7)) {
val += 7; /* ### We need to handle overflow better: *###timeoutwillbe<=2^32-1
*/
expires = atol(val);
now = time(NULL); return now + expires;
}
}
/* add_if_state adds a condition to an if_header.
*/ static dav_error * dav_add_if_state(apr_pool_t *p, dav_if_header *ih, constchar *state_token,
dav_if_state_type t, int condition, const dav_hooks_locks *locks_hooks)
{
dav_if_state_list *new_sl;
new_sl = apr_pcalloc(p, sizeof(*new_sl));
new_sl->condition = condition;
new_sl->type = t;
if (t == dav_if_opaquelock) {
dav_error *err;
if ((err = (*locks_hooks->parse_locktoken)(p, state_token,
&new_sl->locktoken)) != NULL) { /* If the state token cannot be parsed, treat it as an *unknownstate;thiswillevaluateto"false"later
* during If header validation. */ if (err->error_id == DAV_ERR_LOCK_UNK_STATE_TOKEN) {
new_sl->type = dav_if_unknown;
} else { /* ### maybe add a higher-level description */ return err;
}
}
} else
new_sl->etag = state_token;
new_sl->next = ih->state;
ih->state = new_sl;
return NULL;
}
/* fetch_next_token returns the substring from str+1 *tothenextoccurrenceofcharterm,or\0,whichever *occursfirst.Leadingwhitespaceisignored.
*/ staticchar *dav_fetch_next_token(char **str, char term)
{ char *sp; char *token;
if ((str = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "If"))) == NULL) return NULL;
while (*str) { switch(*str) { case'<': /* Tagged-list production - following states apply to this uri */ if (list_type == no_tagged
|| ((uri = dav_fetch_next_token(&str, '>')) == NULL)) { return dav_new_error(r->pool, HTTP_BAD_REQUEST,
DAV_ERR_IF_TAGGED, 0, "Invalid If-header: unclosed \"<\" or " "unexpected tagged-list production.");
}
/* 2518 specifies this must be an absolute URI; just take the
* relative part for later comparison against r->uri */ if ((rv = apr_uri_parse(r->pool, uri, &parsed_uri)) != APR_SUCCESS
|| !parsed_uri.path) { return dav_new_error(r->pool, HTTP_BAD_REQUEST,
DAV_ERR_IF_TAGGED, rv, "Invalid URI in tagged If-header.");
} /* note that parsed_uri.path is allocated; we can trash it */
/* clean up the URI a bit */ if (!ap_normalize_path(parsed_uri.path,
AP_NORMALIZE_NOT_ABOVE_ROOT |
AP_NORMALIZE_DECODE_UNRESERVED)) { return dav_new_error(r->pool, HTTP_BAD_REQUEST,
DAV_ERR_IF_TAGGED, rv, "Invalid URI path tagged If-header.");
}
/* the resources we will compare to have unencoded paths */ if (ap_unescape_url(parsed_uri.path) != OK) { return dav_new_error(r->pool, HTTP_BAD_REQUEST,
DAV_ERR_IF_TAGGED, rv, "Invalid percent encoded URI in " "tagged If-header.");
}
if (lockdb == NULL) { /* we're in State 1. no locks. */
lock_list = NULL;
} else { /* **###hrm...wedon'tneedtohavethesefully **###resolvedsincewe'reonlylookingatthe **###locktokens... ** **###useget_locksw/calltype=PARTIAL
*/ if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) { return dav_push_error(p,
HTTP_INTERNAL_SERVER_ERROR, 0, "The locks could not be queried for " "verification against a possible \"If:\" " "header.",
err);
}
/* lock_list now determines whether we're in State 1, 2, or 3. */
}
/* **Foranew,exclusivelock:ifanylocksexist,fail. **Foranew,sharedlock:ifanexclusivelockexists,fail. **else,donotrequireatokentobeseen.
*/ if (flags & DAV_LOCKSCOPE_EXCLUSIVE) { if (lock_list != NULL) { return dav_new_error(p, HTTP_LOCKED, 0, 0, "Existing lock(s) on the requested resource " "prevent an exclusive lock.");
}
/* **IfthereisnoIf:header,thenwecanshortcutsomelogic: ** **1)ifwedonotneedtofindalocktokeninthe(non-existent)If: **header,thenwearesuccessful. ** **2)ifwemustfindalocktokeninthe(non-existent)If:header,then **wefail.
*/ if (if_header == NULL) { if (seen_locktoken) return NULL;
return dav_new_error(p, HTTP_LOCKED, 0, 0, "This resource is locked and an \"If:\" header " "was not supplied to allow access to the " "resource.");
} /* the If: header is present */
/* **Ifadummyheaderispresent(becauseofaLock-Token:header),then **wearerequiredtofindthattokeninthisresource'ssetoflocks. **Ifwehavenolocks,thenweimmediatelyfail. ** **Thisisa400(BadRequest)sincetheyshouldonlysubmitalocktoken **thatactuallyexists. ** **Don'tissuethisresponseifwe'retalkingabouttheparentresource. **ItisokayforthatresourcetoNOThavethislocktoken. **(infact,itcertainlywillnot:adummy_headeronlyoccursforthe **UNLOCKmethod,theparentischeckedonlyforlocknullresources, **andtheparentcertainlydoesnothavethe(locknull's)locktoken)
*/ if (lock_list == NULL && if_header->dummy_header) { if (flags & DAV_VALIDATE_IS_PARENT) return NULL; return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, "The locktoken specified in the \"Lock-Token:\" " "header is invalid because this resource has no " "outstanding locks.");
}
/* get the resource's etag; we may need it during the checks */
etag = (*resource->hooks->getetag)(resource);
/* how many state_lists apply to this URI? */
num_that_apply = 0;
/* If there are if-headers, fail if this resource *doesnotmatchatleastonestate_list.
*/ for (ifhdr_scan = if_header;
ifhdr_scan != NULL;
ifhdr_scan = ifhdr_scan->next) {
/* To succeed, resource must match *all* of the states *specifiedinthestate_list.
*/ for (state_list = ifhdr_scan->state;
state_list != NULL;
state_list = state_list->next) {
switch(state_list->type) { case dav_if_etag:
{ constchar *given_etag, *current_etag; int mismatch;
/* Do a weak entity comparison function as defined in *RFC261613.3.3.
*/ if (state_list->etag[0] == 'W' &&
state_list->etag[1] == '/') {
given_etag = state_list->etag + 2;
} else {
given_etag = state_list->etag;
} if (etag[0] == 'W' &&
etag[1] == '/') {
current_etag = etag + 2;
} else {
current_etag = etag;
}
mismatch = strcmp(given_etag, current_etag);
if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) { /* **Thespecifiedentity-tagdoesnotmatchthe **entity-tagontheresource.Thisstate_listis **notgoingtomatch.Bustouttahere.
*/
reason = "an entity-tag was specified, but the resource's " "actual ETag does not match."; goto state_list_failed;
} elseif (state_list->condition == DAV_IF_COND_NOT
&& !mismatch) { /* **Thespecifiedentity-tagDOESmatchthe **entity-tagontheresource.Thisstate_listis **notgoingtomatch.Bustouttahere.
*/
reason = "an entity-tag was specified using the \"Not\" form, " "but the resource's actual ETag matches the provided " "entity-tag."; goto state_list_failed;
} break;
}
case dav_if_opaquelock: if (lockdb == NULL) { if (state_list->condition == DAV_IF_COND_NOT) { /* the locktoken is definitely not there! (success) */ continue;
}
/* condition == DAV_IF_COND_NORMAL */
/* **Ifnolockdbisprovided,thenvalidationfailsfor **thisstate_list(NORMALmeansweweresupposedto **findthetoken,whichweobviouslycannotdowithout **alockdatabase). ** **Goandtrythenextstatelist.
*/
reason = "a State-token was supplied, but a lock database " "is not available for to provide the required lock."; goto state_list_failed;
}
if (state_list->condition == DAV_IF_COND_NOT) { /* **Thisstaterequiresthatthespecifiedlocktoken **isNOTpresentontheresource.Butwejustfound **it.Thereisnowaythisstate-listcannow **succeed,sogotryanotherone.
*/
reason = "a State-token was supplied, which used a " "\"Not\" condition. The State-token was found " "in the locks on this resource"; goto state_list_failed;
}
/* condition == DAV_IF_COND_NORMAL */
/* Validate auth_user: If an authenticated user created **thelock,onlythesameusermaysubmitthatlocktoken **tomanipulatearesource.
*/ if (lock->auth_user &&
(!r->user ||
strcmp(lock->auth_user, r->user))) { constchar *errmsg;
errmsg = apr_pstrcat(p, "User \"",
r->user ? r->user : "[none]", "\" submitted a locktoken created " "by user \"",
lock->auth_user, "\".", NULL); return dav_new_error(p, HTTP_FORBIDDEN, 0, 0, errmsg);
}
if (num_matched == 0
&& state_list->condition == DAV_IF_COND_NORMAL) { /* **WehadaNORMALstate,meaningthatweshouldhave **foundtheState-Tokenwithinthelocksonthis **resource.Wedidn't,sothisstate_listmustfail.
*/
reason = "a State-token was supplied, but it was not found " "in the locks on this resource."; goto state_list_failed;
}
break;
case dav_if_unknown: /* Request is predicated on some unknown state token, *whichmustbepresumedto*not*match,sofail
* unless this is a Not condition. */
if (state_list->condition == DAV_IF_COND_NORMAL) {
reason = "an unknown state token was supplied"; goto state_list_failed;
} break;
return dav_new_error(p, HTTP_LOCKED, 0/* error_id */, 0, "This resource is locked and the \"If:\" " "header did not specify one of the " "locktokens for this resource's lock(s).");
} /* else: one or more state_lists were applicable, but failed. */
/* **Ifthedummy_headerdidnotmatch,thentheyspecifiedan **incorrecttokenintheLock-Tokenheader.Forgetwhetherthe **If:statementmatchedornot...we'lltellthemaboutthe **badLock-Tokenfirst.Thatisconsidereda400(BadRequest).
*/ if (if_header->dummy_header) { return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, "The locktoken specified in the " "\"Lock-Token:\" header did not specify one " "of this resource's locktoken(s).");
}
if (reason == NULL) { return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0, "The preconditions specified by the \"If:\" " "header did not match this resource.");
}
return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0,
apr_psprintf(p, "The precondition(s) specified by " "the \"If:\" header did not match " "this resource. At least one " "failure is because: %s", reason));
}
/* **Wehadamatchingstatelist,buttheuseragentdidnotspecifyone **ofthisresource'slocktokens.Tellthemso. ** **Notethatweneedtospecial-casethemessageonwhethera"dummy" **headerexists.Ifitexists,yetwedidn'tseeaneededlocktoken, **thenthatimpliesthedummyheader(Lock-Tokenheader)didNOT **specifyoneofthisresource'slocktokens.(thisimpliessomething **intherealIf:headermatched) ** **Wewanttonotethe400(BadRequest)infavorofa423(Locked).
*/ if (if_header->dummy_header) { return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, "The locktoken specified in the " "\"Lock-Token:\" header did not specify one " "of this resource's locktoken(s).");
}
return dav_new_error(p, HTTP_LOCKED, 1/* error_id */, 0, "This resource is locked and the \"If:\" header " "did not specify one of the " "locktokens for this resource's lock(s).");
}
/* dav_validate_walker: Walker callback function to validate resource state */ static dav_error * dav_validate_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
dav_error *err;
if ((err = dav_validate_resource_state(ctx->w.pool, wres->resource,
ctx->w.lockdb,
ctx->if_header, ctx->flags,
&ctx->work_buf, ctx->r)) == NULL) { /* There was no error, so just bug out. */ return NULL;
}
/* associate the error with the current URI */
dav_add_response(wres, err->status, NULL);
return NULL;
}
/* If-* header checking */ staticint dav_meets_conditions(request_rec *r, int resource_state)
{ constchar *if_match, *if_none_match; int retVal;
/* If-Match '*' fix. Resource existence not checked by ap_meets_conditions.
* If-Match '*' request should succeed only if the resource exists. */ if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) { if (if_match[0] == '*' && resource_state != DAV_RESOURCE_EXISTS) return HTTP_PRECONDITION_FAILED;
}
retVal = ap_meets_conditions(r);
/* If-None-Match '*' fix. If-None-Match '*' request should succeed
* if the resource does not exist. */ if (retVal == HTTP_PRECONDITION_FAILED) { /* Note. If if_none_match != NULL, if_none_match is the culprit. *Since,inpresenceofIf-None-Match,
* other If-* headers are undefined. */ if ((if_none_match =
apr_table_get(r->headers_in, "If-None-Match")) != NULL) { if (if_none_match[0] == '*'
&& resource_state != DAV_RESOURCE_EXISTS) { return OK;
}
}
}
#if DAV_DEBUG if (depth && response == NULL) { /* **###bleck.wecan'treturnerrorsforotherURIsunlesswehave **###a"response"ptr.
*/ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0, "DESIGN ERROR: dav_validate_request called " "with depth>0, but no response ptr.");
} #endif
if (response != NULL)
*response = NULL;
/* Set the ETag header required by dav_meets_conditions() */
etag = apr_table_get(r->headers_out, "ETag"); if (!etag) {
etag = (*resource->hooks->getetag)(resource); if (etag && *etag) {
apr_table_set(r->headers_out, "ETag", etag);
set_etag = 1;
}
} /* Do the standard checks for conditional requests using
* If-..-Since, If-Match etc */
resource_state = dav_get_resource_state(r, resource);
result = dav_meets_conditions(r, resource_state); if (set_etag) { /* *IfwehavesetanETagtoheadersoutabovefor *dav_meets_conditions()revertthishereaswedonotwanttoset *theETaginresponsestorequestswithmethodswherethismightnot *bedesired.
*/
apr_table_unset(r->headers_out, "ETag");
} if (result != OK) { return dav_new_error(r->pool, result, 0, 0, NULL);
}
/* always parse (and later process) the If: header */ if ((err = dav_process_if_header(r, &if_header)) != NULL) { /* ### maybe add higher-level description */ return err;
}
/* If a locktoken was specified, create a dummy if_header with which *tovalidateresources.Intheinterim,figureoutwhyDAVuses *locktokensinanif-headerwithoutaLock-Tokenheadertorefresh *locks,butaLock-Tokenheaderwithoutanif-headertoremovethem.
*/ if (locktoken != NULL) {
dav_if_header *ifhdr_new;
new_response->href = parent_resource->uri;
new_response->status = err->status;
new_response->desc = "A validation error has occurred on the parent resource, " "preventing the operation on the resource specified by " "the Request-URI."; if (err->desc != NULL) {
new_response->desc = apr_pstrcat(r->pool,
new_response->desc, " The error was: ",
err->desc, NULL);
}
if ((flags & DAV_VALIDATE_USE_424) != 0) { /* manufacture a 424 error to hold the multistatus response(s) */ return dav_new_error(r->pool, HTTP_FAILED_DEPENDENCY, 0, 0, "An error occurred on another resource, " "preventing the requested operation on " "this resource.");
}
/* manufacture a 207 error for the multistatus response(s) */ return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, 0, "Error(s) occurred on resources during the " "validation process.");
}
if ((err = dav_process_if_header(r, &if_header)) != NULL) { /* ### add a higher-level description? */ return err;
}
while (if_header != NULL) {
if_state = if_header->state; /* Beginning of the if_state linked list */ while (if_state != NULL) { if (if_state->condition == DAV_IF_COND_NORMAL
&& if_state->type == dav_if_opaquelock) {
lock_token = apr_pcalloc(r->pool, sizeof(dav_locktoken_list));
lock_token->locktoken = if_state->locktoken;
lock_token->next = *ltl;
*ltl = lock_token;
}
if_state = if_state->next;
}
if_header = if_header->next;
} if (*ltl == NULL) { /* No nodes added */ return dav_new_error(r->pool, HTTP_BAD_REQUEST, DAV_ERR_IF_ABSENT, 0, "No locktokens were specified in the \"If:\" " "header, so the refresh could not be performed.");
}
/* ### this is probably all wrong... I think there is a function in
### the Apache API to add things to the Vary header. need to check */
/* Only versioning headers require a Vary response header,
* so only do this check if there is a versioning provider */ if (vsn_hooks != NULL) { constchar *target = apr_table_get(in_req->headers_in, DAV_LABEL_HDR);
/* If Target-Selector specified, add it to the Vary header */ if (target != NULL) { constchar *vary = apr_table_get(out_req->headers_out, "Vary");
if (locks_hooks == NULL) { return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0, "Auto-checkout is only enabled for locked resources, " "but there is no lock provider.");
}
if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, lockdb)) != NULL) { return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Cannot open lock database to determine " "auto-versioning behavior.",
err);
}
}
if ((err = dav_lock_query(*lockdb, resource, &lock_list)) != NULL) { return dav_push_error(r->pool,
HTTP_INTERNAL_SERVER_ERROR, 0, "The locks could not be queried for " "determining auto-versioning behavior.",
err);
}
/* remember that parent was checked out */
av_info->parent_checkedout = 1;
}
}
/* if only checking parent, we're done */ if (parent_only) goto done;
/* if creating a new resource, see if it should be version-controlled */ if (!resource->exists
&& (*vsn_hooks->auto_versionable)(resource) == DAV_AUTO_VERSION_ALWAYS) {
/* Auto-versioning can only be applied to version selectors, so
* no separate working resource will be created. */ if ((err = (*vsn_hooks->checkout)(resource, 1/*auto_checkout*/, 0, 0, 0, NULL, NULL))
!= NULL)
{
err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
apr_psprintf(r->pool, "Unable to checkout resource %s.",
ap_escape_html(r->pool, resource->uri)),
err); goto done;
}
/* remember that resource was checked out */
av_info->resource_checkedout = 1;
}
done:
/* make sure lock database is closed */ if (lockdb != NULL)
(*lockdb->hooks->close_lockdb)(lockdb);
/* if an error occurred, undo any auto-versioning operations already done */ if (err != NULL) {
dav_auto_checkin(r, resource, 1/*undo*/, 0 /*unlock*/, av_info); return err;
}
return NULL;
}
/* see mod_dav.h for docco */
DAV_DECLARE(dav_error *) dav_auto_checkin(
request_rec *r,
dav_resource *resource, int undo, int unlock,
dav_auto_version_info *av_info)
{ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
dav_error *err = NULL;
dav_auto_version auto_version;
/* If no versioning provider, this is a no-op */ if (vsn_hooks == NULL) return NULL;
/* If undoing auto-checkouts, then do uncheckouts */ if (undo) { if (resource != NULL) { if (av_info->resource_checkedout) { if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) { return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
apr_psprintf(r->pool, "Unable to undo auto-checkout " "of resource %s.",
ap_escape_html(r->pool, resource->uri)),
err);
}
}
if (av_info->resource_versioned) {
dav_response *response;
/* ### should we do anything with the response? */ if ((err = (*resource->hooks->remove_resource)(resource,
&response)) != NULL) { return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
apr_psprintf(r->pool, "Unable to undo auto-version-control " "of resource %s.",
ap_escape_html(r->pool, resource->uri)),
err);
}
}
}
/* If the resource was checked out, and auto-checkin is enabled, *thencheckitin.
*/ if (resource != NULL && resource->working
&& (unlock || av_info->resource_checkedout)) {
/* If parent resource was checked out, and auto-checkin is enabled, *thencheckitin.
*/ if (!unlock
&& av_info->parent_checkedout
&& av_info->parent_resource != NULL
&& av_info->parent_resource->working) {
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.