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