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

« back to all changes in this revision

Viewing changes to modules/dav/main/props.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 extension module for Apache 2.0.*
 
19
**  - Property database handling (repository-independent)
 
20
**
 
21
** NOTES:
 
22
**
 
23
**   PROPERTY DATABASE
 
24
**
 
25
**   This version assumes that there is a per-resource database provider
 
26
**   to record properties. The database provider decides how and where to
 
27
**   store these databases.
 
28
**
 
29
**   The DBM keys for the properties have the following form:
 
30
**
 
31
**     namespace ":" propname
 
32
**
 
33
**   For example: 5:author
 
34
**
 
35
**   The namespace provides an integer index into the namespace table
 
36
**   (see below). propname is simply the property name, without a namespace
 
37
**   prefix.
 
38
**
 
39
**   A special case exists for properties that had a prefix starting with
 
40
**   "xml". The XML Specification reserves these for future use. mod_dav
 
41
**   stores and retrieves them unchanged. The keys for these properties
 
42
**   have the form:
 
43
**
 
44
**     ":" propname
 
45
**
 
46
**   The propname will contain the prefix and the property name. For
 
47
**   example, a key might be ":xmlfoo:name"
 
48
**
 
49
**   The ":name" style will also be used for properties that do not
 
50
**   exist within a namespace.
 
51
**
 
52
**   The DBM values consist of two null-terminated strings, appended
 
53
**   together (the null-terms are retained and stored in the database).
 
54
**   The first string is the xml:lang value for the property. An empty
 
55
**   string signifies that a lang value was not in context for the value.
 
56
**   The second string is the property value itself.
 
57
**
 
58
**
 
59
**   NAMESPACE TABLE
 
60
**
 
61
**   The namespace table is an array that lists each of the namespaces
 
62
**   that are in use by the properties in the given propdb. Each entry
 
63
**   in the array is a simple URI.
 
64
**
 
65
**   For example: http://www.foo.bar/standards/props/
 
66
**
 
67
**   The prefix used for the property is stripped and the URI for it
 
68
**   is entered into the namespace table. Also, any namespaces used
 
69
**   within the property value will be entered into the table (and
 
70
**   stripped from the child elements).
 
71
**
 
72
**   The namespaces are stored in the DBM database under the "METADATA" key.
 
73
**
 
74
**
 
75
**   STRIPPING NAMESPACES
 
76
**
 
77
**   Within the property values, the namespace declarations (xmlns...)
 
78
**   are stripped. Each element and attribute will have its prefix removed
 
79
**   and a new prefix inserted.
 
80
**
 
81
**   This must be done so that we can return multiple properties in a
 
82
**   PROPFIND which may have (originally) used conflicting prefixes. For
 
83
**   that case, we must bind all property value elements to new namespace
 
84
**   values.
 
85
**
 
86
**   This implies that clients must NOT be sensitive to the namespace
 
87
**   prefix used for their properties. It WILL change when the properties
 
88
**   are returned (we return them as "ns<index>", e.g. "ns5"). Also, the
 
89
**   property value can contain ONLY XML elements and CDATA. PI and comment
 
90
**   elements will be stripped. CDATA whitespace will be preserved, but
 
91
**   whitespace within element tags will be altered. Attribute ordering
 
92
**   may be altered. Element and CDATA ordering will be preserved.
 
93
**
 
94
**
 
95
**   ATTRIBUTES ON PROPERTY NAME ELEMENTS
 
96
**
 
97
**   When getting/setting properties, the XML used looks like:
 
98
**
 
99
**     <prop>
 
100
**       <propname1>value</propname1>
 
101
**       <propname2>value</propname1>
 
102
**     </prop>
 
103
**
 
104
**   This implementation (mod_dav) DOES NOT save any attributes that are
 
105
**   associated with the <propname1> element. The property value is deemed
 
106
**   to be only the contents ("value" in the above example).
 
107
**
 
108
**   We do store the xml:lang value (if any) that applies to the context
 
109
**   of the <propname1> element. Whether the xml:lang attribute is on
 
110
**   <propname1> itself, or from a higher level element, we will store it
 
111
**   with the property value.
 
112
**
 
113
**
 
114
**   VERSIONING
 
115
**
 
116
**   The DBM db contains a key named "METADATA" that holds database-level
 
117
**   information, such as the namespace table. The record also contains the
 
118
**   db's version number as the very first 16-bit value. This first number
 
119
**   is actually stored as two single bytes: the first byte is a "major"
 
120
**   version number. The second byte is a "minor" number.
 
121
**
 
122
**   If the major number is not what mod_dav expects, then the db is closed
 
123
**   immediately and an error is returned. A minor number change is
 
124
**   acceptable -- it is presumed that old/new dav_props.c can deal with
 
125
**   the database format. For example, a newer dav_props might update the
 
126
**   minor value and append information to the end of the metadata record
 
127
**   (which would be ignored by previous versions).
 
128
**
 
129
**
 
130
** ISSUES:
 
131
**
 
132
**   At the moment, for the dav_get_allprops() and dav_get_props() functions,
 
133
**   we must return a set of xmlns: declarations for ALL known namespaces
 
134
**   in the file. There isn't a way to filter this because we don't know
 
135
**   which are going to be used or not. Examining property names is not
 
136
**   sufficient because the property values could use entirely different
 
137
**   namespaces.
 
138
**
 
139
**   ==> we must devise a scheme where we can "garbage collect" the namespace
 
140
**       entries from the property database.
 
141
*/
 
142
 
 
143
#include "apr.h"
 
144
#include "apr_strings.h"
 
145
 
 
146
#define APR_WANT_STDIO
 
147
#define APR_WANT_BYTEFUNC
 
148
#include "apr_want.h"
 
149
 
 
150
#include "mod_dav.h"
 
151
 
 
152
#include "http_log.h"
 
153
#include "http_request.h"
 
154
 
 
155
/*
 
156
** There is some rough support for writable DAV:getcontenttype and
 
157
** DAV:getcontentlanguage properties. If this #define is (1), then
 
158
** this support is disabled.
 
159
**
 
160
** We are disabling it because of a lack of support in GET and PUT
 
161
** operations. For GET, it would be "expensive" to look for a propdb,
 
162
** open it, and attempt to extract the Content-Type and Content-Language
 
163
** values for the response.
 
164
** (Handling the PUT would not be difficult, though)
 
165
*/
 
166
#define DAV_DISABLE_WRITABLE_PROPS     1
 
167
 
 
168
#define DAV_EMPTY_VALUE                "\0"    /* TWO null terms */
 
169
 
 
170
struct dav_propdb {
 
171
    apr_pool_t *p;                /* the pool we should use */
 
172
    request_rec *r;               /* the request record */
 
173
 
 
174
    const dav_resource *resource; /* the target resource */
 
175
 
 
176
    int deferred;                 /* open of db has been deferred */
 
177
    dav_db *db;                   /* underlying database containing props */
 
178
 
 
179
    apr_array_header_t *ns_xlate; /* translation of an elem->ns to URI */
 
180
    dav_namespace_map *mapping;   /* namespace mapping */
 
181
 
 
182
    dav_lockdb *lockdb;           /* the lock database */
 
183
 
 
184
    dav_buffer wb_lock;           /* work buffer for lockdiscovery property */
 
185
 
 
186
    /* if we ever run a GET subreq, it will be stored here */
 
187
    request_rec *subreq;
 
188
 
 
189
    /* hooks we should use for processing (based on the target resource) */
 
190
    const dav_hooks_db *db_hooks;
 
191
};
 
192
 
 
193
/* NOTE: dav_core_props[] and the following enum must stay in sync. */
 
194
/* ### move these into a "core" liveprop provider? */
 
195
static const char * const dav_core_props[] =
 
196
{
 
197
    "getcontenttype",
 
198
    "getcontentlanguage",
 
199
    "lockdiscovery",
 
200
    "supportedlock",
 
201
 
 
202
    NULL        /* sentinel */
 
203
};
 
204
enum {
 
205
    DAV_PROPID_CORE_getcontenttype = DAV_PROPID_CORE,
 
206
    DAV_PROPID_CORE_getcontentlanguage,
 
207
    DAV_PROPID_CORE_lockdiscovery,
 
208
    DAV_PROPID_CORE_supportedlock,
 
209
 
 
210
    DAV_PROPID_CORE_UNKNOWN
 
211
};
 
212
 
 
213
/*
 
214
** This structure is used to track information needed for a rollback.
 
215
*/
 
216
typedef struct dav_rollback_item {
 
217
    /* select one of the two rollback context structures based on the
 
218
       value of dav_prop_ctx.is_liveprop */
 
219
    dav_deadprop_rollback *deadprop;
 
220
    dav_liveprop_rollback *liveprop;
 
221
 
 
222
} dav_rollback_item;
 
223
 
 
224
 
 
225
static int dav_find_liveprop_provider(dav_propdb *propdb,
 
226
                                      const char *ns_uri,
 
227
                                      const char *propname,
 
228
                                      const dav_hooks_liveprop **provider)
 
229
{
 
230
    int propid;
 
231
 
 
232
    *provider = NULL;
 
233
 
 
234
    if (ns_uri == NULL) {
 
235
        /* policy: liveprop providers cannot define no-namespace properties */
 
236
        return DAV_PROPID_CORE_UNKNOWN;
 
237
    }
 
238
 
 
239
    /* check liveprop providers first, so they can define core properties */
 
240
    propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
 
241
                                   provider);
 
242
    if (propid != 0) {
 
243
        return propid;
 
244
    }
 
245
 
 
246
    /* check for core property */
 
247
    if (strcmp(ns_uri, "DAV:") == 0) {
 
248
        const char * const *p = dav_core_props;
 
249
 
 
250
        for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
 
251
            if (strcmp(propname, *p) == 0) {
 
252
                return propid;
 
253
            }
 
254
    }
 
255
 
 
256
    /* no provider for this property */
 
257
    return DAV_PROPID_CORE_UNKNOWN;
 
258
}
 
259
 
 
260
static void dav_find_liveprop(dav_propdb *propdb, apr_xml_elem *elem)
 
261
{
 
262
    const char *ns_uri;
 
263
    dav_elem_private *priv = elem->priv;
 
264
    const dav_hooks_liveprop *hooks;
 
265
 
 
266
 
 
267
    if (elem->ns == APR_XML_NS_NONE)
 
268
        ns_uri = NULL;
 
269
    else if (elem->ns == APR_XML_NS_DAV_ID)
 
270
        ns_uri = "DAV:";
 
271
    else
 
272
        ns_uri = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
 
273
 
 
274
    priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name,
 
275
                                              &hooks);
 
276
 
 
277
    /* ### this test seems redundant... */
 
278
    if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
 
279
        priv->provider = hooks;
 
280
    }
 
281
}
 
282
 
 
283
/* is the live property read/write? */
 
284
static int dav_rw_liveprop(dav_propdb *propdb, dav_elem_private *priv)
 
285
{
 
286
    int propid = priv->propid;
 
287
 
 
288
    /*
 
289
    ** Check the liveprop provider (if this is a provider-defined prop)
 
290
    */
 
291
    if (priv->provider != NULL) {
 
292
        return (*priv->provider->is_writable)(propdb->resource, propid);
 
293
    }
 
294
 
 
295
    /* these are defined as read-only */
 
296
    if (propid == DAV_PROPID_CORE_lockdiscovery
 
297
#if DAV_DISABLE_WRITABLE_PROPS
 
298
        || propid == DAV_PROPID_CORE_getcontenttype
 
299
        || propid == DAV_PROPID_CORE_getcontentlanguage
 
300
#endif
 
301
        || propid == DAV_PROPID_CORE_supportedlock
 
302
        ) {
 
303
 
 
304
        return 0;
 
305
    }
 
306
 
 
307
    /* these are defined as read/write */
 
308
    if (propid == DAV_PROPID_CORE_getcontenttype
 
309
        || propid == DAV_PROPID_CORE_getcontentlanguage
 
310
        || propid == DAV_PROPID_CORE_UNKNOWN) {
 
311
 
 
312
        return 1;
 
313
    }
 
314
 
 
315
    /*
 
316
    ** We don't recognize the property, so it must be dead (and writable)
 
317
    */
 
318
    return 1;
 
319
}
 
320
 
 
321
/* do a sub-request to fetch properties for the target resource's URI. */
 
322
static void dav_do_prop_subreq(dav_propdb *propdb)
 
323
{
 
324
    /* perform a "GET" on the resource's URI (note that the resource
 
325
       may not correspond to the current request!). */
 
326
    propdb->subreq = ap_sub_req_lookup_uri(propdb->resource->uri, propdb->r,
 
327
                                           NULL);
 
328
}
 
329
 
 
330
static dav_error * dav_insert_coreprop(dav_propdb *propdb,
 
331
                                       int propid, const char *name,
 
332
                                       dav_prop_insert what,
 
333
                                       apr_text_header *phdr,
 
334
                                       dav_prop_insert *inserted)
 
335
{
 
336
    const char *value = NULL;
 
337
    dav_error *err;
 
338
 
 
339
    *inserted = DAV_PROP_INSERT_NOTDEF;
 
340
 
 
341
    /* fast-path the common case */
 
342
    if (propid == DAV_PROPID_CORE_UNKNOWN)
 
343
        return NULL;
 
344
 
 
345
    switch (propid) {
 
346
 
 
347
    case DAV_PROPID_CORE_lockdiscovery:
 
348
        if (propdb->lockdb != NULL) {
 
349
            dav_lock *locks;
 
350
 
 
351
            if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
 
352
                                      &locks)) != NULL) {
 
353
                return dav_push_error(propdb->p, err->status, 0,
 
354
                                      "DAV:lockdiscovery could not be "
 
355
                                      "determined due to a problem fetching "
 
356
                                      "the locks for this resource.",
 
357
                                      err);
 
358
            }
 
359
 
 
360
            /* fast-path the no-locks case */
 
361
            if (locks == NULL) {
 
362
                value = "";
 
363
            }
 
364
            else {
 
365
                /*
 
366
                ** This may modify the buffer. value may point to
 
367
                ** wb_lock.pbuf or a string constant.
 
368
                */
 
369
                value = dav_lock_get_activelock(propdb->r, locks,
 
370
                                                &propdb->wb_lock);
 
371
 
 
372
                /* make a copy to isolate it from changes to wb_lock */
 
373
                value = apr_pstrdup(propdb->p, propdb->wb_lock.buf);
 
374
            }
 
375
        }
 
376
        break;
 
377
 
 
378
    case DAV_PROPID_CORE_supportedlock:
 
379
        if (propdb->lockdb != NULL) {
 
380
            value = (*propdb->lockdb->hooks->get_supportedlock)(propdb->resource);
 
381
        }
 
382
        break;
 
383
 
 
384
    case DAV_PROPID_CORE_getcontenttype:
 
385
        if (propdb->subreq == NULL) {
 
386
            dav_do_prop_subreq(propdb);
 
387
        }
 
388
        if (propdb->subreq->content_type != NULL) {
 
389
            value = propdb->subreq->content_type;
 
390
        }
 
391
        break;
 
392
 
 
393
    case DAV_PROPID_CORE_getcontentlanguage:
 
394
    {
 
395
        const char *lang;
 
396
 
 
397
        if (propdb->subreq == NULL) {
 
398
            dav_do_prop_subreq(propdb);
 
399
        }
 
400
        if ((lang = apr_table_get(propdb->subreq->headers_out,
 
401
                                 "Content-Language")) != NULL) {
 
402
            value = lang;
 
403
        }
 
404
        break;
 
405
    }
 
406
 
 
407
    default:
 
408
        /* fall through to interpret as a dead property */
 
409
        break;
 
410
    }
 
411
 
 
412
    /* if something was supplied, then insert it */
 
413
    if (value != NULL) {
 
414
        const char *s;
 
415
 
 
416
        if (what == DAV_PROP_INSERT_SUPPORTED) {
 
417
            /* use D: prefix to refer to the DAV: namespace URI,
 
418
             * and let the namespace attribute default to "DAV:"
 
419
             */
 
420
            s = apr_psprintf(propdb->p,
 
421
                            "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
 
422
                            name);
 
423
        }
 
424
        else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
 
425
            /* use D: prefix to refer to the DAV: namespace URI */
 
426
            s = apr_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR,
 
427
                            name, value, name);
 
428
        }
 
429
        else {
 
430
            /* use D: prefix to refer to the DAV: namespace URI */
 
431
            s = apr_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name);
 
432
        }
 
433
        apr_text_append(propdb->p, phdr, s);
 
434
 
 
435
        *inserted = what;
 
436
    }
 
437
 
 
438
    return NULL;
 
439
}
 
440
 
 
441
static dav_error * dav_insert_liveprop(dav_propdb *propdb,
 
442
                                       const apr_xml_elem *elem,
 
443
                                       dav_prop_insert what,
 
444
                                       apr_text_header *phdr,
 
445
                                       dav_prop_insert *inserted)
 
446
{
 
447
    dav_elem_private *priv = elem->priv;
 
448
 
 
449
    *inserted = DAV_PROP_INSERT_NOTDEF;
 
450
 
 
451
    if (priv->provider == NULL) {
 
452
        /* this is a "core" property that we define */
 
453
        return dav_insert_coreprop(propdb, priv->propid, elem->name,
 
454
                                   what, phdr, inserted);
 
455
    }
 
456
 
 
457
    /* ask the provider (that defined this prop) to insert the prop */
 
458
    *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
 
459
                                               what, phdr);
 
460
 
 
461
    return NULL;
 
462
}
 
463
 
 
464
static void dav_output_prop_name(apr_pool_t *pool,
 
465
                                 const dav_prop_name *name,
 
466
                                 dav_xmlns_info *xi,
 
467
                                 apr_text_header *phdr)
 
468
{
 
469
    const char *s;
 
470
 
 
471
    if (*name->ns == '\0')
 
472
        s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name->name);
 
473
    else {
 
474
        const char *prefix = dav_xmlns_add_uri(xi, name->ns);
 
475
 
 
476
        s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name);
 
477
    }
 
478
 
 
479
    apr_text_append(pool, phdr, s);
 
480
}
 
481
 
 
482
static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, long ns,
 
483
                             const char *ns_uri, apr_text_header *phdr)
 
484
{
 
485
    const char *s;
 
486
 
 
487
    s = apr_psprintf(p, " xmlns:%s%ld=\"%s\"", pre_prefix, ns, ns_uri);
 
488
    apr_text_append(p, phdr, s);
 
489
}
 
490
 
 
491
static dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
 
492
{
 
493
    dav_error *err;
 
494
 
 
495
    /* we're trying to open the db; turn off the 'deferred' flag */
 
496
    propdb->deferred = 0;
 
497
 
 
498
    /* ask the DB provider to open the thing */
 
499
    err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro,
 
500
                                    &propdb->db);
 
501
    if (err != NULL) {
 
502
        return dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
 
503
                              DAV_ERR_PROP_OPENING,
 
504
                              "Could not open the property database.",
 
505
                              err);
 
506
    }
 
507
 
 
508
    /*
 
509
    ** NOTE: propdb->db could be NULL if we attempted to open a readonly
 
510
    **       database that doesn't exist. If we require read/write
 
511
    **       access, then a database was created and opened.
 
512
    */
 
513
 
 
514
    return NULL;
 
515
}
 
516
 
 
517
DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
 
518
                                        const dav_resource *resource,
 
519
                                        int ro,
 
520
                                        apr_array_header_t * ns_xlate,
 
521
                                        dav_propdb **p_propdb)
 
522
{
 
523
    dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
 
524
 
 
525
    *p_propdb = NULL;
 
526
 
 
527
#if DAV_DEBUG
 
528
    if (resource->uri == NULL) {
 
529
        return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
 
530
                             "INTERNAL DESIGN ERROR: resource must define "
 
531
                             "its URI.");
 
532
    }
 
533
#endif
 
534
 
 
535
    propdb->r = r;
 
536
    apr_pool_create(&propdb->p, r->pool);
 
537
    propdb->resource = resource;
 
538
    propdb->ns_xlate = ns_xlate;
 
539
 
 
540
    propdb->db_hooks = DAV_GET_HOOKS_PROPDB(r);
 
541
 
 
542
    propdb->lockdb = lockdb;
 
543
 
 
544
    /* always defer actual open, to avoid expense of accessing db
 
545
     * when only live properties are involved
 
546
     */
 
547
    propdb->deferred = 1;
 
548
 
 
549
    /* ### what to do about closing the propdb on server failure? */
 
550
 
 
551
    *p_propdb = propdb;
 
552
    return NULL;
 
553
}
 
554
 
 
555
DAV_DECLARE(void) dav_close_propdb(dav_propdb *propdb)
 
556
{
 
557
    if (propdb->db != NULL) {
 
558
        (*propdb->db_hooks->close)(propdb->db);
 
559
    }
 
560
 
 
561
    /* Currently, mod_dav's pool usage doesn't allow clearing this pool. */
 
562
#if 0
 
563
    apr_pool_destroy(propdb->p);
 
564
#endif
 
565
    return;
 
566
}
 
567
 
 
568
DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
 
569
                                                   dav_prop_insert what)
 
570
{
 
571
    const dav_hooks_db *db_hooks = propdb->db_hooks;
 
572
    apr_text_header hdr = { 0 };
 
573
    apr_text_header hdr_ns = { 0 };
 
574
    dav_get_props_result result = { 0 };
 
575
    int found_contenttype = 0;
 
576
    int found_contentlang = 0;
 
577
    dav_prop_insert unused_inserted;
 
578
 
 
579
    /* if not just getting supported live properties,
 
580
     * scan all properties in the dead prop database
 
581
     */
 
582
    if (what != DAV_PROP_INSERT_SUPPORTED) {
 
583
        if (propdb->deferred) {
 
584
            /* ### what to do with db open error? */
 
585
            (void) dav_really_open_db(propdb, 1 /*ro*/);
 
586
        }
 
587
 
 
588
        /* initialize the result with some start tags... */
 
589
        apr_text_append(propdb->p, &hdr,
 
590
                        "<D:propstat>" DEBUG_CR
 
591
                        "<D:prop>" DEBUG_CR);
 
592
 
 
593
        /* if there ARE properties, then scan them */
 
594
        if (propdb->db != NULL) {
 
595
            dav_xmlns_info *xi = dav_xmlns_create(propdb->p);
 
596
            dav_prop_name name;
 
597
 
 
598
            /* define (up front) any namespaces the db might need */
 
599
            (void) (*db_hooks->define_namespaces)(propdb->db, xi);
 
600
 
 
601
            /* get the first property name, beginning the scan */
 
602
            (void) (*db_hooks->first_name)(propdb->db, &name);
 
603
            while (name.ns != NULL) {
 
604
 
 
605
                /*
 
606
                ** We also look for <DAV:getcontenttype> and
 
607
                ** <DAV:getcontentlanguage>. If they are not stored as dead
 
608
                ** properties, then we need to perform a subrequest to get
 
609
                ** their values (if any).
 
610
                */
 
611
                if (*name.ns == 'D' && strcmp(name.ns, "DAV:") == 0
 
612
                    && *name.name == 'g') {
 
613
                    if (strcmp(name.name, "getcontenttype") == 0) {
 
614
                        found_contenttype = 1;
 
615
                    }
 
616
                    else if (strcmp(name.name, "getcontentlanguage") == 0) {
 
617
                        found_contentlang = 1;
 
618
                    }
 
619
                }
 
620
 
 
621
                if (what == DAV_PROP_INSERT_VALUE) {
 
622
                    dav_error *err;
 
623
                    int found;
 
624
 
 
625
                    if ((err = (*db_hooks->output_value)(propdb->db, &name,
 
626
                                                         xi, &hdr,
 
627
                                                         &found)) != NULL) {
 
628
                        /* ### anything better to do? */
 
629
                        /* ### probably should enter a 500 error */
 
630
                        goto next_key;
 
631
                    }
 
632
                    /* assert: found == 1 */
 
633
                }
 
634
                else {
 
635
                    /* the value was not requested, so just add an empty
 
636
                       tag specifying the property name. */
 
637
                    dav_output_prop_name(propdb->p, &name, xi, &hdr);
 
638
                }
 
639
 
 
640
              next_key:
 
641
                (void) (*db_hooks->next_name)(propdb->db, &name);
 
642
            }
 
643
 
 
644
            /* all namespaces have been entered into xi. generate them into
 
645
               the output now. */
 
646
            dav_xmlns_generate(xi, &hdr_ns);
 
647
 
 
648
        } /* propdb->db != NULL */
 
649
 
 
650
        /* add namespaces for all the liveprop providers */
 
651
        dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
 
652
    }
 
653
 
 
654
    /* ask the liveprop providers to insert their properties */
 
655
    dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
 
656
 
 
657
    /* insert the standard properties */
 
658
    /* ### should be handling the return errors here */
 
659
    (void)dav_insert_coreprop(propdb,
 
660
                              DAV_PROPID_CORE_supportedlock, "supportedlock",
 
661
                              what, &hdr, &unused_inserted);
 
662
    (void)dav_insert_coreprop(propdb,
 
663
                              DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
 
664
                              what, &hdr, &unused_inserted);
 
665
 
 
666
    /* if we didn't find these, then do the whole subreq thing. */
 
667
    if (!found_contenttype) {
 
668
        /* ### should be handling the return error here */
 
669
        (void)dav_insert_coreprop(propdb,
 
670
                                  DAV_PROPID_CORE_getcontenttype,
 
671
                                  "getcontenttype",
 
672
                                  what, &hdr, &unused_inserted);
 
673
    }
 
674
    if (!found_contentlang) {
 
675
        /* ### should be handling the return error here */
 
676
        (void)dav_insert_coreprop(propdb,
 
677
                                  DAV_PROPID_CORE_getcontentlanguage,
 
678
                                  "getcontentlanguage",
 
679
                                  what, &hdr, &unused_inserted);
 
680
    }
 
681
 
 
682
    /* if not just reporting on supported live props,
 
683
     * terminate the result */
 
684
    if (what != DAV_PROP_INSERT_SUPPORTED) {
 
685
        apr_text_append(propdb->p, &hdr,
 
686
                        "</D:prop>" DEBUG_CR
 
687
                        "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
 
688
                        "</D:propstat>" DEBUG_CR);
 
689
    }
 
690
 
 
691
    result.propstats = hdr.first;
 
692
    result.xmlns = hdr_ns.first;
 
693
    return result;
 
694
}
 
695
 
 
696
DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
 
697
                                                apr_xml_doc *doc)
 
698
{
 
699
    const dav_hooks_db *db_hooks = propdb->db_hooks;
 
700
    apr_xml_elem *elem = dav_find_child(doc->root, "prop");
 
701
    apr_text_header hdr_good = { 0 };
 
702
    apr_text_header hdr_bad = { 0 };
 
703
    apr_text_header hdr_ns = { 0 };
 
704
    int have_good = 0;
 
705
    dav_get_props_result result = { 0 };
 
706
    char *marks_liveprop;
 
707
    dav_xmlns_info *xi;
 
708
    int xi_filled = 0;
 
709
 
 
710
    /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
 
711
       the marks */
 
712
 
 
713
    /* we will ALWAYS provide a "good" result, even if it is EMPTY */
 
714
    apr_text_append(propdb->p, &hdr_good,
 
715
                   "<D:propstat>" DEBUG_CR
 
716
                   "<D:prop>" DEBUG_CR);
 
717
 
 
718
    /* ### the marks should be in a buffer! */
 
719
    /* allocate zeroed-memory for the marks. These marks indicate which
 
720
       liveprop namespaces we've generated into the output xmlns buffer */
 
721
 
 
722
    /* same for the liveprops */
 
723
    marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
 
724
 
 
725
    xi = dav_xmlns_create(propdb->p);
 
726
 
 
727
    for (elem = elem->first_child; elem; elem = elem->next) {
 
728
        dav_elem_private *priv;
 
729
        dav_error *err;
 
730
        dav_prop_insert inserted;
 
731
        dav_prop_name name;
 
732
 
 
733
        /*
 
734
        ** First try live property providers; if they don't handle
 
735
        ** the property, then try looking it up in the propdb.
 
736
        */
 
737
 
 
738
        if (elem->priv == NULL) {
 
739
            elem->priv = apr_pcalloc(propdb->p, sizeof(*priv));
 
740
        }
 
741
        priv = elem->priv;
 
742
 
 
743
        /* cache the propid; dav_get_props() could be called many times */
 
744
        if (priv->propid == 0)
 
745
            dav_find_liveprop(propdb, elem);
 
746
 
 
747
        if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
 
748
 
 
749
            /* insert the property. returns 1 if an insertion was done. */
 
750
            if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
 
751
                                           &hdr_good, &inserted)) != NULL) {
 
752
                /* ### need to propagate the error to the caller... */
 
753
                /* ### skip it for now, as if nothing was inserted */
 
754
            }
 
755
            if (inserted == DAV_PROP_INSERT_VALUE) {
 
756
                have_good = 1;
 
757
 
 
758
                /*
 
759
                ** Add the liveprop's namespace URIs. Note that provider==NULL
 
760
                ** for core properties.
 
761
                */
 
762
                if (priv->provider != NULL) {
 
763
                    const char * const * scan_ns_uri;
 
764
 
 
765
                    for (scan_ns_uri = priv->provider->namespace_uris;
 
766
                         *scan_ns_uri != NULL;
 
767
                         ++scan_ns_uri) {
 
768
                        long ns;
 
769
 
 
770
                        ns = dav_get_liveprop_ns_index(*scan_ns_uri);
 
771
                        if (marks_liveprop[ns])
 
772
                            continue;
 
773
                        marks_liveprop[ns] = 1;
 
774
 
 
775
                        dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri,
 
776
                                         &hdr_ns);
 
777
                    }
 
778
                }
 
779
 
 
780
                /* property added. move on to the next property. */
 
781
                continue;
 
782
            }
 
783
            else if (inserted == DAV_PROP_INSERT_NOTDEF) {
 
784
                /* nothing to do. fall thru to allow property to be handled
 
785
                   as a dead property */
 
786
            }
 
787
#if DAV_DEBUG
 
788
            else {
 
789
#if 0
 
790
                /* ### need to change signature to return an error */
 
791
                return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0,
 
792
                                     "INTERNAL DESIGN ERROR: insert_liveprop "
 
793
                                     "did not insert what was asked for.");
 
794
#endif
 
795
            }
 
796
#endif
 
797
        }
 
798
 
 
799
        /* The property wasn't a live property, so look in the dead property
 
800
           database. */
 
801
 
 
802
        /* make sure propdb is really open */
 
803
        if (propdb->deferred) {
 
804
            /* ### what to do with db open error? */
 
805
            (void) dav_really_open_db(propdb, 1 /*ro*/);
 
806
        }
 
807
 
 
808
        if (elem->ns == APR_XML_NS_NONE)
 
809
            name.ns = "";
 
810
        else
 
811
            name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
 
812
        name.name = elem->name;
 
813
 
 
814
        /* only bother to look if a database exists */
 
815
        if (propdb->db != NULL) {
 
816
            int found;
 
817
 
 
818
            if ((err = (*db_hooks->output_value)(propdb->db, &name,
 
819
                                                 xi, &hdr_good,
 
820
                                                 &found)) != NULL) {
 
821
                /* ### what to do? continue doesn't seem right... */
 
822
                continue;
 
823
            }
 
824
 
 
825
            if (found) {
 
826
                have_good = 1;
 
827
 
 
828
                /* if we haven't added the db's namespaces, then do so... */
 
829
                if (!xi_filled) {
 
830
                    (void) (*db_hooks->define_namespaces)(propdb->db, xi);
 
831
                    xi_filled = 1;
 
832
                }
 
833
                continue;
 
834
            }
 
835
        }
 
836
 
 
837
        /* not found as a live OR dead property. add a record to the "bad"
 
838
           propstats */
 
839
 
 
840
        /* make sure we've started our "bad" propstat */
 
841
        if (hdr_bad.first == NULL) {
 
842
            apr_text_append(propdb->p, &hdr_bad,
 
843
                            "<D:propstat>" DEBUG_CR
 
844
                            "<D:prop>" DEBUG_CR);
 
845
        }
 
846
 
 
847
        /* output this property's name (into the bad propstats) */
 
848
        dav_output_prop_name(propdb->p, &name, xi, &hdr_bad);
 
849
    }
 
850
 
 
851
    apr_text_append(propdb->p, &hdr_good,
 
852
                    "</D:prop>" DEBUG_CR
 
853
                    "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
 
854
                    "</D:propstat>" DEBUG_CR);
 
855
 
 
856
    /* default to start with the good */
 
857
    result.propstats = hdr_good.first;
 
858
 
 
859
    /* we may not have any "bad" results */
 
860
    if (hdr_bad.first != NULL) {
 
861
        /* "close" the bad propstat */
 
862
        apr_text_append(propdb->p, &hdr_bad,
 
863
                        "</D:prop>" DEBUG_CR
 
864
                        "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
 
865
                        "</D:propstat>" DEBUG_CR);
 
866
 
 
867
        /* if there are no good props, then just return the bad */
 
868
        if (!have_good) {
 
869
            result.propstats = hdr_bad.first;
 
870
        }
 
871
        else {
 
872
            /* hook the bad propstat to the end of the good one */
 
873
            hdr_good.last->next = hdr_bad.first;
 
874
        }
 
875
    }
 
876
 
 
877
    /* add in all the various namespaces, and return them */
 
878
    dav_xmlns_generate(xi, &hdr_ns);
 
879
    result.xmlns = hdr_ns.first;
 
880
 
 
881
    return result;
 
882
}
 
883
 
 
884
DAV_DECLARE(void) dav_get_liveprop_supported(dav_propdb *propdb,
 
885
                                             const char *ns_uri,
 
886
                                             const char *propname,
 
887
                                             apr_text_header *body)
 
888
{
 
889
    int propid;
 
890
    const dav_hooks_liveprop *hooks;
 
891
 
 
892
    propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
 
893
 
 
894
    if (propid != DAV_PROPID_CORE_UNKNOWN) {
 
895
        if (hooks == NULL) {
 
896
            /* this is a "core" property that we define */
 
897
            dav_prop_insert unused_inserted;
 
898
            dav_insert_coreprop(propdb, propid, propname,
 
899
                                DAV_PROP_INSERT_SUPPORTED, body, &unused_inserted);
 
900
        }
 
901
        else {
 
902
            (*hooks->insert_prop)(propdb->resource, propid,
 
903
                                  DAV_PROP_INSERT_SUPPORTED, body);
 
904
        }
 
905
    }
 
906
}
 
907
 
 
908
DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx)
 
909
{
 
910
    dav_propdb *propdb = ctx->propdb;
 
911
    apr_xml_elem *prop = ctx->prop;
 
912
    dav_elem_private *priv;
 
913
 
 
914
    priv = ctx->prop->priv = apr_pcalloc(propdb->p, sizeof(*priv));
 
915
 
 
916
    /*
 
917
    ** Check to see if this is a live property, and fill the fields
 
918
    ** in the XML elem, as appropriate.
 
919
    **
 
920
    ** Verify that the property is read/write. If not, then it cannot
 
921
    ** be SET or DELETEd.
 
922
    */
 
923
    if (priv->propid == 0) {
 
924
        dav_find_liveprop(propdb, prop);
 
925
 
 
926
        /* it's a liveprop if a provider was found */
 
927
        /* ### actually the "core" props should really be liveprops, but
 
928
           ### there is no "provider" for those and the r/w props are
 
929
           ### treated as dead props anyhow */
 
930
        ctx->is_liveprop = priv->provider != NULL;
 
931
    }
 
932
 
 
933
    if (!dav_rw_liveprop(propdb, priv)) {
 
934
        ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT,
 
935
                                 DAV_ERR_PROP_READONLY,
 
936
                                 "Property is read-only.");
 
937
        return;
 
938
    }
 
939
 
 
940
    if (ctx->is_liveprop) {
 
941
        int defer_to_dead = 0;
 
942
 
 
943
        ctx->err = (*priv->provider->patch_validate)(propdb->resource,
 
944
                                                     prop, ctx->operation,
 
945
                                                     &ctx->liveprop_ctx,
 
946
                                                     &defer_to_dead);
 
947
        if (ctx->err != NULL || !defer_to_dead)
 
948
            return;
 
949
 
 
950
        /* clear is_liveprop -- act as a dead prop now */
 
951
        ctx->is_liveprop = 0;
 
952
    }
 
953
 
 
954
    /*
 
955
    ** The property is supposed to be stored into the dead-property
 
956
    ** database. Make sure the thing is truly open (and writable).
 
957
    */
 
958
    if (propdb->deferred
 
959
        && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
 
960
        return;
 
961
    }
 
962
 
 
963
    /*
 
964
    ** There should be an open, writable database in here!
 
965
    **
 
966
    ** Note: the database would be NULL if it was opened readonly and it
 
967
    **       did not exist.
 
968
    */
 
969
    if (propdb->db == NULL) {
 
970
        ctx->err = dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
 
971
                                 DAV_ERR_PROP_NO_DATABASE,
 
972
                                 "Attempted to set/remove a property "
 
973
                                 "without a valid, open, read/write "
 
974
                                 "property database.");
 
975
        return;
 
976
    }
 
977
 
 
978
    if (ctx->operation == DAV_PROP_OP_SET) {
 
979
        /*
 
980
        ** Prep the element => propdb namespace index mapping, inserting
 
981
        ** namespace URIs into the propdb that don't exist.
 
982
        */
 
983
        (void) (*propdb->db_hooks->map_namespaces)(propdb->db,
 
984
                                                   propdb->ns_xlate,
 
985
                                                   &propdb->mapping);
 
986
    }
 
987
    else if (ctx->operation == DAV_PROP_OP_DELETE) {
 
988
        /*
 
989
        ** There are no checks to perform here. If a property exists, then
 
990
        ** we will delete it. If it does not exist, then it does not matter
 
991
        ** (see S12.13.1).
 
992
        **
 
993
        ** Note that if a property does not exist, that does not rule out
 
994
        ** that a SET will occur during this PROPPATCH (thusly creating it).
 
995
        */
 
996
    }
 
997
}
 
998
 
 
999
DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx)
 
1000
{
 
1001
    dav_propdb *propdb = ctx->propdb;
 
1002
    dav_error *err = NULL;
 
1003
    dav_elem_private *priv = ctx->prop->priv;
 
1004
 
 
1005
    ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback));
 
1006
 
 
1007
    if (ctx->is_liveprop) {
 
1008
        err = (*priv->provider->patch_exec)(propdb->resource,
 
1009
                                            ctx->prop, ctx->operation,
 
1010
                                            ctx->liveprop_ctx,
 
1011
                                            &ctx->rollback->liveprop);
 
1012
    }
 
1013
    else {
 
1014
        dav_prop_name name;
 
1015
 
 
1016
        if (ctx->prop->ns == APR_XML_NS_NONE)
 
1017
            name.ns = "";
 
1018
        else
 
1019
            name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns);
 
1020
        name.name = ctx->prop->name;
 
1021
 
 
1022
        /* save the old value so that we can do a rollback. */
 
1023
        if ((err = (*propdb->db_hooks
 
1024
                    ->get_rollback)(propdb->db, &name,
 
1025
                                    &ctx->rollback->deadprop)) != NULL)
 
1026
            goto error;
 
1027
 
 
1028
        if (ctx->operation == DAV_PROP_OP_SET) {
 
1029
 
 
1030
            /* Note: propdb->mapping was set in dav_prop_validate() */
 
1031
            err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
 
1032
                                             propdb->mapping);
 
1033
 
 
1034
            /*
 
1035
            ** If an error occurred, then assume that we didn't change the
 
1036
            ** value. Remove the rollback item so that we don't try to set
 
1037
            ** its value during the rollback.
 
1038
            */
 
1039
            /* ### euh... where is the removal? */
 
1040
        }
 
1041
        else if (ctx->operation == DAV_PROP_OP_DELETE) {
 
1042
 
 
1043
            /*
 
1044
            ** Delete the property. Ignore errors -- the property is there, or
 
1045
            ** we are deleting it for a second time.
 
1046
            */
 
1047
            /* ### but what about other errors? */
 
1048
            (void) (*propdb->db_hooks->remove)(propdb->db, &name);
 
1049
        }
 
1050
    }
 
1051
 
 
1052
  error:
 
1053
    /* push a more specific error here */
 
1054
    if (err != NULL) {
 
1055
        /*
 
1056
        ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
 
1057
        ** any errors at this point.
 
1058
        */
 
1059
        ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
 
1060
                                  DAV_ERR_PROP_EXEC,
 
1061
                                  "Could not execute PROPPATCH.", err);
 
1062
    }
 
1063
}
 
1064
 
 
1065
DAV_DECLARE_NONSTD(void) dav_prop_commit(dav_prop_ctx *ctx)
 
1066
{
 
1067
    dav_elem_private *priv = ctx->prop->priv;
 
1068
 
 
1069
    /*
 
1070
    ** Note that a commit implies ctx->err is NULL. The caller should assume
 
1071
    ** a status of HTTP_OK for this case.
 
1072
    */
 
1073
 
 
1074
    if (ctx->is_liveprop) {
 
1075
        (*priv->provider->patch_commit)(ctx->propdb->resource,
 
1076
                                        ctx->operation,
 
1077
                                        ctx->liveprop_ctx,
 
1078
                                        ctx->rollback->liveprop);
 
1079
    }
 
1080
}
 
1081
 
 
1082
DAV_DECLARE_NONSTD(void) dav_prop_rollback(dav_prop_ctx *ctx)
 
1083
{
 
1084
    dav_error *err = NULL;
 
1085
    dav_elem_private *priv = ctx->prop->priv;
 
1086
 
 
1087
    /* do nothing if there is no rollback information. */
 
1088
    if (ctx->rollback == NULL)
 
1089
        return;
 
1090
 
 
1091
    /*
 
1092
    ** ### if we have an error, and a rollback occurs, then the namespace
 
1093
    ** ### mods should not happen at all. Basically, the namespace management
 
1094
    ** ### is simply a bitch.
 
1095
    */
 
1096
 
 
1097
    if (ctx->is_liveprop) {
 
1098
        err = (*priv->provider->patch_rollback)(ctx->propdb->resource,
 
1099
                                                ctx->operation,
 
1100
                                                ctx->liveprop_ctx,
 
1101
                                                ctx->rollback->liveprop);
 
1102
    }
 
1103
    else {
 
1104
        err = (*ctx->propdb->db_hooks
 
1105
               ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop);
 
1106
    }
 
1107
 
 
1108
    if (err != NULL) {
 
1109
        if (ctx->err == NULL)
 
1110
            ctx->err = err;
 
1111
        else {
 
1112
            dav_error *scan = err;
 
1113
 
 
1114
            /* hook previous errors at the end of the rollback error */
 
1115
            while (scan->prev != NULL)
 
1116
                scan = scan->prev;
 
1117
            scan->prev = ctx->err;
 
1118
            ctx->err = err;
 
1119
        }
 
1120
    }
 
1121
}