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