~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/dav/main/util_lock.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}