1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18** DAV repository-independent lock functions 19*/ 20 21#include "apr.h" 22#include "apr_strings.h" 23 24#include "mod_dav.h" 25#include "http_log.h" 26#include "http_config.h" 27#include "http_protocol.h" 28#include "http_core.h" 29 30APLOG_USE_MODULE(dav); 31 32/* --------------------------------------------------------------- 33** 34** Property-related lock functions 35** 36*/ 37 38/* 39** dav_lock_get_activelock: Returns a <lockdiscovery> containing 40** an activelock element for every item in the lock_discovery tree 41*/ 42DAV_DECLARE(const char *) dav_lock_get_activelock(request_rec *r, 43 dav_lock *lock, 44 dav_buffer *pbuf) 45{ 46 dav_lock *lock_scan; 47 const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); 48 int count = 0; 49 dav_buffer work_buf = { 0 }; 50 apr_pool_t *p = r->pool; 51 52 /* If no locks or no lock provider, there are no locks */ 53 if (lock == NULL || hooks == NULL) { 54 /* 55 ** Since resourcediscovery is defined with (activelock)*, 56 ** <D:activelock/> shouldn't be necessary for an empty lock. 57 */ 58 return ""; 59 } 60 61 /* 62 ** Note: it could be interesting to sum the lengths of the owners 63 ** and locktokens during this loop. However, the buffer 64 ** mechanism provides some rough padding so that we don't 65 ** really need to have an exact size. Further, constructing 66 ** locktoken strings could be relatively expensive. 67 */ 68 for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next) 69 count++; 70 71 /* if a buffer was not provided, then use an internal buffer */ 72 if (pbuf == NULL) 73 pbuf = &work_buf; 74 75 /* reset the length before we start appending stuff */ 76 pbuf->cur_len = 0; 77 78 /* prep the buffer with a "good" size */ 79 dav_check_bufsize(p, pbuf, count * 300); 80 81 for (; lock != NULL; lock = lock->next) { 82 char tmp[100]; 83 84#if DAV_DEBUG 85 if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) { 86 /* ### crap. design error */ 87 dav_buffer_append(p, pbuf, 88 "DESIGN ERROR: attempted to product an " 89 "activelock element from a partial, indirect " 90 "lock record. Creating an XML parsing error " 91 "to ease detection of this situation: <"); 92 } 93#endif 94 95 dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>"); 96 switch (lock->type) { 97 case DAV_LOCKTYPE_WRITE: 98 dav_buffer_append(p, pbuf, "<D:write/>"); 99 break; 100 default: 101 /* ### internal error. log something? */ 102 break; 103 } 104 dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>"); 105 switch (lock->scope) { 106 case DAV_LOCKSCOPE_EXCLUSIVE: 107 dav_buffer_append(p, pbuf, "<D:exclusive/>"); 108 break; 109 case DAV_LOCKSCOPE_SHARED: 110 dav_buffer_append(p, pbuf, "<D:shared/>"); 111 break; 112 default: 113 /* ### internal error. log something? */ 114 break; 115 } 116 dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR); 117 apr_snprintf(tmp, sizeof(tmp), "<D:depth>%s</D:depth>" DEBUG_CR, 118 lock->depth == DAV_INFINITY ? "infinity" : "0"); 119 dav_buffer_append(p, pbuf, tmp); 120 121 if (lock->owner) { 122 /* 123 ** This contains a complete, self-contained <DAV:owner> element, 124 ** with namespace declarations and xml:lang handling. Just drop 125 ** it in. 126 */ 127 dav_buffer_append(p, pbuf, lock->owner); 128 } 129 130 dav_buffer_append(p, pbuf, "<D:timeout>"); 131 if (lock->timeout == DAV_TIMEOUT_INFINITE) { 132 dav_buffer_append(p, pbuf, "Infinite"); 133 } 134 else { 135 time_t now = time(NULL); 136 apr_snprintf(tmp, sizeof(tmp), "Second-%lu", (long unsigned int)(lock->timeout - now)); 137 dav_buffer_append(p, pbuf, tmp); 138 } 139 140 dav_buffer_append(p, pbuf, 141 "</D:timeout>" DEBUG_CR 142 "<D:locktoken>" DEBUG_CR 143 "<D:href>"); 144 dav_buffer_append(p, pbuf, 145 (*hooks->format_locktoken)(p, lock->locktoken)); 146 dav_buffer_append(p, pbuf, 147 "</D:href>" DEBUG_CR 148 "</D:locktoken>" DEBUG_CR 149 "</D:activelock>" DEBUG_CR); 150 } 151 152 return pbuf->buf; 153} 154 155/* 156** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a 157** lockinfo XML element, then populates a dav_lock structure 158** with its contents. 159*/ 160DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r, 161 const dav_resource *resource, 162 dav_lockdb *lockdb, 163 const apr_xml_doc *doc, 164 dav_lock **lock_request) 165{ 166 apr_pool_t *p = r->pool; 167 dav_error *err; 168 apr_xml_elem *child; 169 dav_lock *lock; 170 171 if (!dav_validate_root(doc, "lockinfo")) { 172 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, 173 "The request body contains an unexpected " 174 "XML root element."); 175 } 176 177 if ((err = (*lockdb->hooks->create_lock)(lockdb, resource, 178 &lock)) != NULL) { 179 return dav_push_error(p, err->status, 0, 180 "Could not parse the lockinfo due to an " 181 "internal problem creating a lock structure.", 182 err); 183 } 184 185 lock->depth = dav_get_depth(r, DAV_INFINITY); 186 if (lock->depth == -1) { 187 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, 188 "An invalid Depth header was specified."); 189 } 190 lock->timeout = dav_get_timeout(r); 191 192 /* Parse elements in the XML body */ 193 for (child = doc->root->first_child; child; child = child->next) { 194 if (strcmp(child->name, "locktype") == 0 195 && child->first_child 196 && lock->type == DAV_LOCKTYPE_UNKNOWN) { 197 if (strcmp(child->first_child->name, "write") == 0) { 198 lock->type = DAV_LOCKTYPE_WRITE; 199 continue; 200 } 201 } 202 if (strcmp(child->name, "lockscope") == 0 203 && child->first_child 204 && lock->scope == DAV_LOCKSCOPE_UNKNOWN) { 205 if (strcmp(child->first_child->name, "exclusive") == 0) 206 lock->scope = DAV_LOCKSCOPE_EXCLUSIVE; 207 else if (strcmp(child->first_child->name, "shared") == 0) 208 lock->scope = DAV_LOCKSCOPE_SHARED; 209 if (lock->scope != DAV_LOCKSCOPE_UNKNOWN) 210 continue; 211 } 212 213 if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) { 214 const char *text; 215 216 /* quote all the values in the <DAV:owner> element */ 217 apr_xml_quote_elem(p, child); 218 219 /* 220 ** Store a full <DAV:owner> element with namespace definitions 221 ** and an xml:lang definition, if applicable. 222 */ 223 apr_xml_to_text(p, child, APR_XML_X2T_FULL_NS_LANG, doc->namespaces, 224 NULL, &text, NULL); 225 lock->owner = text; 226 227 continue; 228 } 229 230 return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0, 231 apr_psprintf(p, 232 "The server cannot satisfy the " 233 "LOCK request due to an unknown XML " 234 "element (\"%s\") within the " 235 "DAV:lockinfo element.", 236 child->name)); 237 } 238 239 *lock_request = lock; 240 return NULL; 241} 242 243/* --------------------------------------------------------------- 244** 245** General lock functions 246** 247*/ 248 249/* dav_lock_walker: Walker callback function to record indirect locks */ 250static dav_error * dav_lock_walker(dav_walk_resource *wres, int calltype) 251{ 252 dav_walker_ctx *ctx = wres->walk_ctx; 253 dav_error *err; 254 255 /* We don't want to set indirects on the target */ 256 if ((*wres->resource->hooks->is_same_resource)(wres->resource, 257 ctx->w.root)) 258 return NULL; 259 260 if ((err = (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb, 261 wres->resource, 1, 262 ctx->lock)) != NULL) { 263 if (ap_is_HTTP_SERVER_ERROR(err->status)) { 264 /* ### add a higher-level description? */ 265 return err; 266 } 267 268 /* add to the multistatus response */ 269 dav_add_response(wres, err->status, NULL); 270 271 /* 272 ** ### actually, this is probably wrong: we want to fail the whole 273 ** ### LOCK process if something goes bad. maybe the caller should 274 ** ### do a dav_unlock() (e.g. a rollback) if any errors occurred. 275 */ 276 } 277 278 return NULL; 279} 280 281/* 282** dav_add_lock: Add a direct lock for resource, and indirect locks for 283** all children, bounded by depth. 284** ### assume request only contains one lock 285*/ 286DAV_DECLARE(dav_error *) dav_add_lock(request_rec *r, 287 const dav_resource *resource, 288 dav_lockdb *lockdb, dav_lock *lock, 289 dav_response **response) 290{ 291 dav_error *err; 292 int depth = lock->depth; 293 294 *response = NULL; 295 296 /* Requested lock can be: 297 * Depth: 0 for null resource, existing resource, or existing collection 298 * Depth: Inf for existing collection 299 */ 300 301 /* 302 ** 2518 9.2 says to ignore depth if target is not a collection (it has 303 ** no internal children); pretend the client gave the correct depth. 304 */ 305 if (!resource->collection) { 306 depth = 0; 307 } 308 309 /* In all cases, first add direct entry in lockdb */ 310 311 /* 312 ** Append the new (direct) lock to the resource's existing locks. 313 ** 314 ** Note: this also handles locknull resources 315 */ 316 if ((err = (*lockdb->hooks->append_locks)(lockdb, resource, 0, 317 lock)) != NULL) { 318 /* ### maybe add a higher-level description */ 319 return err; 320 } 321 322 if (depth > 0) { 323 /* Walk existing collection and set indirect locks */ 324 dav_walker_ctx ctx = { { 0 } }; 325 dav_response *multi_status; 326 327 ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH; 328 ctx.w.func = dav_lock_walker; 329 ctx.w.walk_ctx = &ctx; 330 ctx.w.pool = r->pool; 331 ctx.w.root = resource; 332 ctx.w.lockdb = lockdb; 333 334 ctx.r = r; 335 ctx.lock = lock; 336 337 err = (*resource->hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); 338 if (err != NULL) { 339 /* implies a 5xx status code occurred. screw the multistatus */ 340 return err; 341 } 342 343 if (multi_status != NULL) { 344 /* manufacture a 207 error for the multistatus response */ 345 *response = multi_status; 346 return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, 0, 347 "Error(s) occurred on resources during the " 348 "addition of a depth lock."); 349 } 350 } 351 352 return NULL; 353} 354 355/* 356** dav_lock_query: Opens the lock database. Returns a linked list of 357** dav_lock structures for all direct locks on path. 358*/ 359DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb, 360 const dav_resource *resource, 361 dav_lock **locks) 362{ 363 /* If no lock database, return empty result */ 364 if (lockdb == NULL) { 365 *locks = NULL; 366 return NULL; 367 } 368 369 /* ### insert a higher-level description? */ 370 return (*lockdb->hooks->get_locks)(lockdb, resource, 371 DAV_GETLOCKS_RESOLVED, 372 locks); 373} 374 375/* dav_unlock_walker: Walker callback function to remove indirect locks */ 376static dav_error * dav_unlock_walker(dav_walk_resource *wres, int calltype) 377{ 378 dav_walker_ctx *ctx = wres->walk_ctx; 379 dav_error *err; 380 381 /* Before removing the lock, do any auto-checkin required */ 382 if (wres->resource->working) { 383 /* ### get rid of this typecast */ 384 if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource, 385 0 /*undo*/, 1 /*unlock*/, NULL)) 386 != NULL) { 387 return err; 388 } 389 } 390 391 if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb, 392 wres->resource, 393 ctx->locktoken)) != NULL) { 394 /* ### should we stop or return a multistatus? looks like STOP */ 395 /* ### add a higher-level description? */ 396 return err; 397 } 398 399 return NULL; 400} 401 402/* 403** dav_get_direct_resource: 404** 405** Find a lock on the specified resource, then return the resource the 406** lock was applied to (in other words, given a (possibly) indirect lock, 407** return the direct lock's corresponding resource). 408** 409** If the lock is an indirect lock, this usually means traversing up the 410** namespace [repository] hierarchy. Note that some lock providers may be 411** able to return this information with a traversal. 412*/ 413static dav_error * dav_get_direct_resource(apr_pool_t *p, 414 dav_lockdb *lockdb, 415 const dav_locktoken *locktoken, 416 const dav_resource *resource, 417 const dav_resource **direct_resource) 418{ 419 if (lockdb->hooks->lookup_resource != NULL) { 420 return (*lockdb->hooks->lookup_resource)(lockdb, locktoken, 421 resource, direct_resource); 422 } 423 424 *direct_resource = NULL; 425 426 /* Find the top of this lock- 427 * If r->filename's direct locks include locktoken, use r->filename. 428 * If r->filename's indirect locks include locktoken, retry r->filename/.. 429 * Else fail. 430 */ 431 while (resource != NULL) { 432 dav_error *err; 433 dav_lock *lock; 434 dav_resource *parent; 435 436 /* 437 ** Find the lock specified by <locktoken> on <resource>. If it is 438 ** an indirect lock, then partial results are okay. We're just 439 ** trying to find the thing and know whether it is a direct or 440 ** an indirect lock. 441 */ 442 if ((err = (*lockdb->hooks->find_lock)(lockdb, resource, locktoken, 443 1, &lock)) != NULL) { 444 /* ### add a higher-level desc? */ 445 return err; 446 } 447 448 /* not found! that's an error. */ 449 if (lock == NULL) { 450 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0, 451 "The specified locktoken does not correspond " 452 "to an existing lock on this resource."); 453 } 454 455 if (lock->rectype == DAV_LOCKREC_DIRECT) { 456 /* we found the direct lock. return this resource. */ 457 458 *direct_resource = resource; 459 return NULL; 460 } 461 462 /* the lock was indirect. move up a level in the URL namespace */ 463 if ((err = (*resource->hooks->get_parent_resource)(resource, 464 &parent)) != NULL) { 465 /* ### add a higher-level desc? */ 466 return err; 467 } 468 resource = parent; 469 } 470 471 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 0, 472 "The lock database is corrupt. A direct lock could " 473 "not be found for the corresponding indirect lock " 474 "on this resource."); 475} 476 477/* 478** dav_unlock: Removes all direct and indirect locks for r->filename, 479** with given locktoken. If locktoken == null_locktoken, all locks 480** are removed. If r->filename represents an indirect lock, 481** we must unlock the appropriate direct lock. 482** Returns OK or appropriate HTTP_* response and logs any errors. 483** 484** ### We've already crawled the tree to ensure everything was locked 485** by us; there should be no need to incorporate a rollback. 486*/ 487DAV_DECLARE(int) dav_unlock(request_rec *r, const dav_resource *resource, 488 const dav_locktoken *locktoken) 489{ 490 int result; 491 dav_lockdb *lockdb; 492 const dav_resource *lock_resource = resource; 493 const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); 494 const dav_hooks_repository *repos_hooks = resource->hooks; 495 dav_walker_ctx ctx = { { 0 } }; 496 dav_response *multi_status; 497 dav_error *err; 498 499 /* If no locks provider, then there is nothing to unlock. */ 500 if (hooks == NULL) { 501 return OK; 502 } 503 504 /* 2518 requires the entire lock to be removed if resource/locktoken 505 * point to an indirect lock. We need resource of the _direct_ 506 * lock in order to walk down the tree and remove the locks. So, 507 * If locktoken != null_locktoken, 508 * Walk up the resource hierarchy until we see a direct lock. 509 * Or, we could get the direct lock's db/key, pick out the URL 510 * and do a subrequest. I think walking up is faster and will work 511 * all the time. 512 * Else 513 * Just start removing all locks at and below resource. 514 */ 515 516 if ((err = (*hooks->open_lockdb)(r, 0, 1, &lockdb)) != NULL) { 517 /* ### return err! maybe add a higher-level desc */ 518 /* ### map result to something nice; log an error */ 519 return HTTP_INTERNAL_SERVER_ERROR; 520 } 521 522 if (locktoken != NULL 523 && (err = dav_get_direct_resource(r->pool, lockdb, 524 locktoken, resource, 525 &lock_resource)) != NULL) { 526 /* ### add a higher-level desc? */ 527 /* ### should return err! */ 528 return err->status; 529 } 530 531 /* At this point, lock_resource/locktoken refers to a direct lock (key), ie 532 * the root of a depth > 0 lock, or locktoken is null. 533 */ 534 ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL; 535 ctx.w.func = dav_unlock_walker; 536 ctx.w.walk_ctx = &ctx; 537 ctx.w.pool = r->pool; 538 ctx.w.root = lock_resource; 539 ctx.w.lockdb = lockdb; 540 541 ctx.r = r; 542 ctx.locktoken = locktoken; 543 544 err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); 545 546 /* ### fix this! */ 547 /* ### do something with multi_status */ 548 result = err == NULL ? OK : err->status; 549 550 (*hooks->close_lockdb)(lockdb); 551 552 return result; 553} 554 555/* dav_inherit_walker: Walker callback function to inherit locks */ 556static dav_error * dav_inherit_walker(dav_walk_resource *wres, int calltype) 557{ 558 dav_walker_ctx *ctx = wres->walk_ctx; 559 560 if (ctx->skip_root 561 && (*wres->resource->hooks->is_same_resource)(wres->resource, 562 ctx->w.root)) { 563 return NULL; 564 } 565 566 /* ### maybe add a higher-level desc */ 567 return (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb, 568 wres->resource, 1, 569 ctx->lock); 570} 571 572/* 573** dav_inherit_locks: When a resource or collection is added to a collection, 574** locks on the collection should be inherited to the resource/collection. 575** (MOVE, MKCOL, etc) Here we propagate any direct or indirect locks from 576** parent of resource to resource and below. 577*/ 578static dav_error * dav_inherit_locks(request_rec *r, dav_lockdb *lockdb, 579 const dav_resource *resource, 580 int use_parent) 581{ 582 dav_error *err; 583 const dav_resource *which_resource; 584 dav_lock *locks; 585 dav_lock *scan; 586 dav_lock *prev; 587 dav_walker_ctx ctx = { { 0 } }; 588 const dav_hooks_repository *repos_hooks = resource->hooks; 589 dav_response *multi_status; 590 591 if (use_parent) { 592 dav_resource *parent; 593 if ((err = (*repos_hooks->get_parent_resource)(resource, 594 &parent)) != NULL) { 595 /* ### add a higher-level desc? */ 596 return err; 597 } 598 if (parent == NULL) { 599 /* ### map result to something nice; log an error */ 600 return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0, 601 "Could not fetch parent resource. Unable to " 602 "inherit locks from the parent and apply " 603 "them to this resource."); 604 } 605 which_resource = parent; 606 } 607 else { 608 which_resource = resource; 609 } 610 611 if ((err = (*lockdb->hooks->get_locks)(lockdb, which_resource, 612 DAV_GETLOCKS_PARTIAL, 613 &locks)) != NULL) { 614 /* ### maybe add a higher-level desc */ 615 return err; 616 } 617 618 if (locks == NULL) { 619 /* No locks to propagate, just return */ 620 return NULL; 621 } 622 623 /* 624 ** (1) Copy all indirect locks from our parent; 625 ** (2) Create indirect locks for the depth infinity, direct locks 626 ** in our parent. 627 ** 628 ** The append_locks call in the walker callback will do the indirect 629 ** conversion, but we need to remove any direct locks that are NOT 630 ** depth "infinity". 631 */ 632 for (scan = locks, prev = NULL; 633 scan != NULL; 634 prev = scan, scan = scan->next) { 635 636 if (scan->rectype == DAV_LOCKREC_DIRECT 637 && scan->depth != DAV_INFINITY) { 638 639 if (prev == NULL) 640 locks = scan->next; 641 else 642 prev->next = scan->next; 643 } 644 } 645 646 /* <locks> has all our new locks. Walk down and propagate them. */ 647 648 ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL; 649 ctx.w.func = dav_inherit_walker; 650 ctx.w.walk_ctx = &ctx; 651 ctx.w.pool = r->pool; 652 ctx.w.root = resource; 653 ctx.w.lockdb = lockdb; 654 655 ctx.r = r; 656 ctx.lock = locks; 657 ctx.skip_root = !use_parent; 658 659 /* ### do something with multi_status */ 660 return (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status); 661} 662 663/* --------------------------------------------------------------- 664** 665** Functions dealing with lock-null resources 666** 667*/ 668 669/* 670** dav_get_resource_state: Returns the state of the resource 671** r->filename: DAV_RESOURCE_NULL, DAV_RESOURCE_LOCK_NULL, 672** or DAV_RESOURCE_EXIST. 673** 674** Returns DAV_RESOURCE_ERROR if an error occurs. 675*/ 676DAV_DECLARE(int) dav_get_resource_state(request_rec *r, 677 const dav_resource *resource) 678{ 679 const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r); 680 681 if (resource->exists) 682 return DAV_RESOURCE_EXISTS; 683 684 if (hooks != NULL) { 685 dav_error *err; 686 dav_lockdb *lockdb; 687 int locks_present; 688 689 /* 690 ** A locknull resource has the form: 691 ** 692 ** known-dir "/" locknull-file 693 ** 694 ** It would be nice to look into <resource> to verify this form, 695 ** but it does not have enough information for us. Instead, we 696 ** can look at the path_info. If the form does not match, then 697 ** there is no way we could have a locknull resource -- it must 698 ** be a plain, null resource. 699 ** 700 ** Apache sets r->filename to known-dir/unknown-file and r->path_info 701 ** to "" for the "proper" case. If anything is in path_info, then 702 ** it can't be a locknull resource. 703 ** 704 ** ### I bet this path_info hack doesn't work for repositories. 705 ** ### Need input from repository implementors! What kind of 706 ** ### restructure do we need? New provider APIs? 707 */ 708 if (r->path_info != NULL && *r->path_info != '\0') { 709 return DAV_RESOURCE_NULL; 710 } 711 712 if ((err = (*hooks->open_lockdb)(r, 1, 1, &lockdb)) == NULL) { 713 /* note that we might see some expired locks... *shrug* */ 714 err = (*hooks->has_locks)(lockdb, resource, &locks_present); 715 (*hooks->close_lockdb)(lockdb); 716 } 717 718 if (err != NULL) { 719 /* ### don't log an error. return err. add higher-level desc. */ 720 721 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00623) 722 "Failed to query lock-null status for %s", 723 r->filename); 724 725 return DAV_RESOURCE_ERROR; 726 } 727 728 if (locks_present) 729 return DAV_RESOURCE_LOCK_NULL; 730 } 731 732 return DAV_RESOURCE_NULL; 733} 734 735DAV_DECLARE(dav_error *) dav_notify_created(request_rec *r, 736 dav_lockdb *lockdb, 737 const dav_resource *resource, 738 int resource_state, 739 int depth) 740{ 741 dav_error *err; 742 743 if (resource_state == DAV_RESOURCE_LOCK_NULL) { 744 745 /* 746 ** The resource is no longer a locknull resource. This will remove 747 ** the special marker. 748 ** 749 ** Note that a locknull resource has already inherited all of the 750 ** locks from the parent. We do not need to call dav_inherit_locks. 751 ** 752 ** NOTE: some lock providers record locks for locknull resources using 753 ** a different key than for regular resources. this will shift 754 ** the lock information between the two key types. 755 */ 756 (void)(*lockdb->hooks->remove_locknull_state)(lockdb, resource); 757 758 /* 759 ** There are resources under this one, which are new. We must 760 ** propagate the locks down to the new resources. 761 */ 762 if (depth > 0 && 763 (err = dav_inherit_locks(r, lockdb, resource, 0)) != NULL) { 764 /* ### add a higher level desc? */ 765 return err; 766 } 767 } 768 else if (resource_state == DAV_RESOURCE_NULL) { 769 770 /* ### should pass depth to dav_inherit_locks so that it can 771 ** ### optimize for the depth==0 case. 772 */ 773 774 /* this resource should inherit locks from its parent */ 775 if ((err = dav_inherit_locks(r, lockdb, resource, 1)) != NULL) { 776 777 err = dav_push_error(r->pool, err->status, 0, 778 "The resource was created successfully, but " 779 "there was a problem inheriting locks from " 780 "the parent resource.", 781 err); 782 return err; 783 } 784 } 785 /* else the resource already exists and its locks are correct. */ 786 787 return NULL; 788} 789