]> granicus.if.org Git - apache/blob - modules/dav/main/props.c
Update copyright to 2001
[apache] / modules / dav / main / props.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */
54
55 /*
56 ** DAV extension module for Apache 2.0.*
57 **  - Property database handling (repository-independent)
58 **
59 ** NOTES:
60 **
61 **   PROPERTY DATABASE
62 **
63 **   This version assumes that there is a per-resource database provider
64 **   to record properties. The database provider decides how and where to
65 **   store these databases.
66 **
67 **   The DBM keys for the properties have the following form:
68 **
69 **     namespace ":" propname
70 **
71 **   For example: 5:author
72 **
73 **   The namespace provides an integer index into the namespace table
74 **   (see below). propname is simply the property name, without a namespace
75 **   prefix.
76 **
77 **   A special case exists for properties that had a prefix starting with
78 **   "xml". The XML Specification reserves these for future use. mod_dav
79 **   stores and retrieves them unchanged. The keys for these properties
80 **   have the form:
81 **
82 **     ":" propname
83 **
84 **   The propname will contain the prefix and the property name. For
85 **   example, a key might be ":xmlfoo:name"
86 **
87 **   The ":name" style will also be used for properties that do not
88 **   exist within a namespace.
89 **
90 **   The DBM values consist of two null-terminated strings, appended
91 **   together (the null-terms are retained and stored in the database).
92 **   The first string is the xml:lang value for the property. An empty
93 **   string signifies that a lang value was not in context for the value.
94 **   The second string is the property value itself.
95 **
96 **
97 **   NAMESPACE TABLE
98 **
99 **   The namespace table is an array that lists each of the namespaces
100 **   that are in use by the properties in the given propdb. Each entry
101 **   in the array is a simple URI.
102 **
103 **   For example: http://www.foo.bar/standards/props/
104 **
105 **   The prefix used for the property is stripped and the URI for it
106 **   is entered into the namespace table. Also, any namespaces used
107 **   within the property value will be entered into the table (and
108 **   stripped from the child elements).
109 **
110 **   The namespaces are stored in the DBM database under the "METADATA" key.
111 **
112 **
113 **   STRIPPING NAMESPACES
114 **
115 **   Within the property values, the namespace declarations (xmlns...)
116 **   are stripped. Each element and attribute will have its prefix removed
117 **   and a new prefix inserted.
118 **
119 **   This must be done so that we can return multiple properties in a
120 **   PROPFIND which may have (originally) used conflicting prefixes. For
121 **   that case, we must bind all property value elements to new namespace
122 **   values.
123 **
124 **   This implies that clients must NOT be sensitive to the namespace
125 **   prefix used for their properties. It WILL change when the properties
126 **   are returned (we return them as "ns<index>", e.g. "ns5"). Also, the
127 **   property value can contain ONLY XML elements and CDATA. PI and comment
128 **   elements will be stripped. CDATA whitespace will be preserved, but
129 **   whitespace within element tags will be altered. Attribute ordering
130 **   may be altered. Element and CDATA ordering will be preserved.
131 **
132 **
133 **   ATTRIBUTES ON PROPERTY NAME ELEMENTS
134 **
135 **   When getting/setting properties, the XML used looks like:
136 **
137 **     <prop>
138 **       <propname1>value</propname1>
139 **       <propname2>value</propname1>
140 **     </prop>
141 **
142 **   This implementation (mod_dav) DOES NOT save any attributes that are
143 **   associated with the <propname1> element. The property value is deemed
144 **   to be only the contents ("value" in the above example).
145 **
146 **   We do store the xml:lang value (if any) that applies to the context
147 **   of the <propname1> element. Whether the xml:lang attribute is on
148 **   <propname1> itself, or from a higher level element, we will store it
149 **   with the property value.
150 **
151 **
152 **   VERSIONING
153 **
154 **   The DBM db contains a key named "METADATA" that holds database-level
155 **   information, such as the namespace table. The record also contains the
156 **   db's version number as the very first 16-bit value. This first number
157 **   is actually stored as two single bytes: the first byte is a "major"
158 **   version number. The second byte is a "minor" number.
159 **
160 **   If the major number is not what mod_dav expects, then the db is closed
161 **   immediately and an error is returned. A minor number change is
162 **   acceptable -- it is presumed that old/new dav_props.c can deal with
163 **   the database format. For example, a newer dav_props might update the
164 **   minor value and append information to the end of the metadata record
165 **   (which would be ignored by previous versions).
166 **
167 **
168 ** ISSUES:
169 **
170 **   At the moment, for the dav_get_allprops() and dav_get_props() functions,
171 **   we must return a set of xmlns: declarations for ALL known namespaces
172 **   in the file. There isn't a way to filter this because we don't know
173 **   which are going to be used or not. Examining property names is not
174 **   sufficient because the property values could use entirely different
175 **   namespaces.
176 **
177 **   ==> we must devise a scheme where we can "garbage collect" the namespace
178 **       entries from the property database.
179 */
180
181 #include "apr.h"
182 #include "apr_strings.h"
183
184 #define APR_WANT_STDIO
185 #define APR_WANT_BYTEFUNC
186 #include "apr_want.h"
187
188 #include "mod_dav.h"
189
190 #include "http_log.h"
191 #include "http_request.h"
192
193 /*
194 ** There is some rough support for writable DAV:getcontenttype and
195 ** DAV:getcontentlanguage properties. If this #define is (1), then
196 ** this support is disabled.
197 **
198 ** We are disabling it because of a lack of support in GET and PUT
199 ** operations. For GET, it would be "expensive" to look for a propdb,
200 ** open it, and attempt to extract the Content-Type and Content-Language
201 ** values for the response.
202 ** (Handling the PUT would not be difficult, though)
203 */
204 #define DAV_DISABLE_WRITABLE_PROPS      1
205
206 #define DAV_GDBM_NS_KEY         "METADATA"
207 #define DAV_GDBM_NS_KEY_LEN     8
208
209 #define DAV_EMPTY_VALUE         "\0"    /* TWO null terms */
210
211 /* the namespace URI was not found; no ID is available */
212 #define AP_XML_NS_ERROR_NOT_FOUND       (AP_XML_NS_ERROR_BASE)
213
214 typedef struct {
215     unsigned char major;
216 #define DAV_DBVSN_MAJOR         4
217     /*
218     ** V4 -- 0.9.9 ..
219     **       Prior versions could have keys or values with invalid
220     **       namespace prefixes as a result of the xmlns="" form not
221     **       resetting the default namespace to be "no namespace". The
222     **       namespace would be set to "" which is invalid; it should
223     **       be set to "no namespace".
224     **
225     ** V3 -- 0.9.8
226     **       Prior versions could have values with invalid namespace
227     **       prefixes due to an incorrect mapping of input to propdb
228     **       namespace indices. Version bumped to obsolete the old
229     **       values.
230     **
231     ** V2 -- 0.9.7
232     **       This introduced the xml:lang value into the property value's
233     **       record in the propdb.
234     **
235     ** V1 -- .. 0.9.6
236     **       Initial version.
237     */
238
239
240     unsigned char minor;
241 #define DAV_DBVSN_MINOR         0
242
243     short ns_count;
244
245 } dav_propdb_metadata;
246
247 struct dav_propdb {
248     int version;                /* *minor* version of this db */
249
250     apr_pool_t *p;              /* the pool we should use */
251     request_rec *r;             /* the request record */
252
253     const dav_resource *resource;       /* the target resource */
254
255     int deferred;               /* open of db has been deferred */
256     dav_db *db;                 /* underlying database containing props */
257
258     dav_buffer ns_table;        /* table of namespace URIs */
259     short ns_count;             /* number of entries in table */
260     int ns_table_dirty;         /* ns_table was modified */
261
262     apr_array_header_t *ns_xlate;       /* translation of an elem->ns to URI */
263     int *ns_map;                /* map elem->ns to propdb ns values */
264     int incomplete_map;         /* some mappings do not exist */
265
266     dav_lockdb *lockdb;         /* the lock database */
267
268     dav_buffer wb_key;          /* work buffer for dav_gdbm_key */
269     dav_buffer wb_lock;         /* work buffer for lockdiscovery property */
270
271     /* if we ever run a GET subreq, it will be stored here */
272     request_rec *subreq;
273
274     /* hooks we should use for processing (based on the target resource) */
275     const dav_hooks_db *db_hooks;
276
277 };
278
279 /* NOTE: dav_core_props[] and the following enum must stay in sync. */
280 /* ### move these into a "core" liveprop provider? */
281 static const char * const dav_core_props[] =
282 {
283     "getcontenttype",
284     "getcontentlanguage",
285     "lockdiscovery",
286     "supportedlock",
287
288     NULL        /* sentinel */
289 };
290 enum {
291     DAV_PROPID_CORE_getcontenttype = DAV_PROPID_CORE,
292     DAV_PROPID_CORE_getcontentlanguage,
293     DAV_PROPID_CORE_lockdiscovery,
294     DAV_PROPID_CORE_supportedlock,
295
296     DAV_PROPID_CORE_UNKNOWN
297 };
298
299 /*
300 ** This structure is used to track information needed for a rollback.
301 ** If a SET was performed and no prior value existed, then value.dptr
302 ** will be NULL.
303 */
304 typedef struct dav_rollback_item {
305     dav_datum key;              /* key for the item being saved */
306     dav_datum value;            /* value before set/replace/delete */
307
308     /* or use the following (choice selected by dav_prop_ctx.is_liveprop) */
309     struct dav_liveprop_rollback *liveprop;     /* liveprop rollback ctx */
310
311 } dav_rollback_item;
312
313
314 #if 0
315 /* ### unused */
316 static const char *dav_get_ns_table_uri(dav_propdb *propdb, int ns)
317 {
318     const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
319
320     while (ns--)
321         p += strlen(p) + 1;
322
323     return p;
324 }
325 #endif
326
327 static int dav_find_liveprop_provider(dav_propdb *propdb,
328                                       const char *ns_uri,
329                                       const char *propname,
330                                       const dav_hooks_liveprop **provider)
331 {
332     int propid;
333
334     *provider = NULL;
335
336     if (ns_uri == NULL) {
337         /* policy: liveprop providers cannot define no-namespace properties */
338         return DAV_PROPID_CORE_UNKNOWN;
339     }
340     else if (strcmp(ns_uri, "DAV:") == 0) {
341         const char * const *p = dav_core_props;
342
343         for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
344             if (strcmp(propname, *p) == 0) {
345                 return propid;
346             }
347
348         /* didn't find it. fall thru. a provider can define DAV: props */
349     }
350
351     /* is there a liveprop provider for this property? */
352     propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
353                                    provider);
354     if (propid != 0) {
355         return propid;
356     }
357
358     /* no provider for this property */
359     return DAV_PROPID_CORE_UNKNOWN;
360 }
361
362 static void dav_find_liveprop(dav_propdb *propdb, ap_xml_elem *elem)
363 {
364     const char *ns_uri;
365     dav_elem_private *priv = elem->private;
366     const dav_hooks_liveprop *hooks;
367
368
369     if (elem->ns == AP_XML_NS_NONE)
370         ns_uri = NULL;
371     else if (elem->ns == AP_XML_NS_DAV_ID)
372         ns_uri = "DAV:";
373     else
374         ns_uri = AP_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
375
376     priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name,
377                                               &hooks);
378
379     /* ### this test seems redundant... */
380     if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
381         priv->provider = hooks;
382     }
383 }
384
385 /* is the live property read/write? */
386 static int dav_rw_liveprop(dav_propdb *propdb, dav_elem_private *priv)
387 {
388     int propid = priv->propid;
389
390     /*
391     ** Check the liveprop provider (if this is a provider-defined prop)
392     */
393     if (priv->provider != NULL) {
394         return (*priv->provider->is_writable)(propdb->resource, propid);
395     }
396
397     /* these are defined as read-only */
398     if (propid == DAV_PROPID_CORE_lockdiscovery
399 #if DAV_DISABLE_WRITABLE_PROPS
400         || propid == DAV_PROPID_CORE_getcontenttype
401         || propid == DAV_PROPID_CORE_getcontentlanguage
402 #endif
403         || propid == DAV_PROPID_CORE_supportedlock
404         ) {
405
406         return 0;
407     }
408
409     /* these are defined as read/write */
410     if (propid == DAV_PROPID_CORE_getcontenttype
411         || propid == DAV_PROPID_CORE_getcontentlanguage
412         || propid == DAV_PROPID_CORE_UNKNOWN) {
413
414         return 1;
415     }
416
417     /*
418     ** We don't recognize the property, so it must be dead (and writable)
419     */
420     return 1;
421 }
422
423 /* do a sub-request to fetch properties for the target resource's URI. */
424 static void dav_do_prop_subreq(dav_propdb *propdb)
425 {
426     /* perform a "GET" on the resource's URI (note that the resource
427        may not correspond to the current request!). */
428     propdb->subreq = ap_sub_req_lookup_uri(propdb->resource->uri, propdb->r,
429                                            NULL);
430 }
431
432 static dav_error * dav_insert_coreprop(dav_propdb *propdb,
433                                        int propid, const char *name,
434                                        dav_prop_insert what,
435                                        ap_text_header *phdr,
436                                        dav_prop_insert *inserted)
437 {
438     const char *value = NULL;
439     dav_error *err;
440
441     *inserted = DAV_PROP_INSERT_NOTDEF;
442
443     /* fast-path the common case */
444     if (propid == DAV_PROPID_CORE_UNKNOWN)
445         return NULL;
446
447     switch (propid) {
448
449     case DAV_PROPID_CORE_lockdiscovery:
450         if (propdb->lockdb != NULL) {
451             dav_lock *locks;
452
453             if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
454                                       &locks)) != NULL) {
455                 return dav_push_error(propdb->p, err->status, 0,
456                                       "DAV:lockdiscovery could not be "
457                                       "determined due to a problem fetching "
458                                       "the locks for this resource.",
459                                       err);
460             }
461
462             /* fast-path the no-locks case */
463             if (locks == NULL) {
464                 value = "";
465             }
466             else {
467                 /*
468                 ** This may modify the buffer. value may point to
469                 ** wb_lock.pbuf or a string constant.
470                 */
471                 value = dav_lock_get_activelock(propdb->r, locks,
472                                                 &propdb->wb_lock);
473
474                 /* make a copy to isolate it from changes to wb_lock */
475                 value = apr_pstrdup(propdb->p, propdb->wb_lock.buf);
476             }
477         }
478         break;
479
480     case DAV_PROPID_CORE_supportedlock:
481         if (propdb->lockdb != NULL) {
482             value = (*propdb->lockdb->hooks->get_supportedlock)(propdb->resource);
483         }
484         break;
485
486     case DAV_PROPID_CORE_getcontenttype:
487         if (propdb->subreq == NULL) {
488             dav_do_prop_subreq(propdb);
489         }
490         if (propdb->subreq->content_type != NULL) {
491             value = propdb->subreq->content_type;
492         }
493         break;
494
495     case DAV_PROPID_CORE_getcontentlanguage:
496     {
497         const char *lang;
498
499         if (propdb->subreq == NULL) {
500             dav_do_prop_subreq(propdb);
501         }
502         if ((lang = apr_table_get(propdb->subreq->headers_out,
503                                  "Content-Language")) != NULL) {
504             value = lang;
505         }
506         break;
507     }
508
509     default:
510         /* fall through to interpret as a dead property */
511         break;
512     }
513
514     /* if something was supplied, then insert it */
515     if (value != NULL) {
516         const char *s;
517
518         if (what == DAV_PROP_INSERT_SUPPORTED) {
519             /* use D: prefix to refer to the DAV: namespace URI,
520              * and let the namespace attribute default to "DAV:"
521              */
522             s = apr_psprintf(propdb->p,
523                             "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
524                             name);
525         }
526         else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
527             /* use D: prefix to refer to the DAV: namespace URI */
528             s = apr_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR,
529                             name, value, name);
530         }
531         else {
532             /* use D: prefix to refer to the DAV: namespace URI */
533             s = apr_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name);
534         }
535         ap_text_append(propdb->p, phdr, s);
536
537         *inserted = what;
538     }
539
540     return NULL;
541 }
542
543 static dav_error * dav_insert_liveprop(dav_propdb *propdb,
544                                        const ap_xml_elem *elem,
545                                        dav_prop_insert what,
546                                        ap_text_header *phdr,
547                                        dav_prop_insert *inserted)
548 {
549     dav_elem_private *priv = elem->private;
550
551     *inserted = DAV_PROP_INSERT_NOTDEF;
552
553     if (priv->provider == NULL) {
554         /* this is a "core" property that we define */
555         return dav_insert_coreprop(propdb, priv->propid, elem->name,
556                                    what, phdr, inserted);
557     }
558
559     /* ask the provider (that defined this prop) to insert the prop */
560     *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
561                                                what, phdr);
562
563     return NULL;
564 }
565
566 static void dav_append_prop(dav_propdb *propdb,
567                             const char *name, const char *value,
568                             ap_text_header *phdr)
569 {
570     const char *s;
571     const char *lang = value;
572
573     /* skip past the xml:lang value */
574     value += strlen(lang) + 1;
575
576     if (*value == '\0') {
577         /* the property is an empty value */
578         if (*name == ':') {
579             /* "no namespace" case */
580             s = apr_psprintf(propdb->p, "<%s/>" DEBUG_CR, name+1);
581         }
582         else {
583             s = apr_psprintf(propdb->p, "<ns%s/>" DEBUG_CR, name);
584         }
585     }
586     else if (*lang != '\0') {
587         if (*name == ':') {
588             /* "no namespace" case */
589             s = apr_psprintf(propdb->p, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
590                             name+1, lang, value, name+1);
591         }
592         else {
593             s = apr_psprintf(propdb->p, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
594                             name, lang, value, name);
595         }
596     }
597     else if (*name == ':') {
598         /* "no namespace" case */
599         s = apr_psprintf(propdb->p, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
600     }
601     else {
602         s = apr_psprintf(propdb->p, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
603     }
604     ap_text_append(propdb->p, phdr, s);
605 }
606
607 /*
608 ** Prepare the ns_map variable in the propdb structure. This entails copying
609 ** all URIs from the "input" namespace list (in propdb->ns_xlate) into the
610 ** propdb's list of namespaces. As each URI is copied (or pre-existing
611 ** URI looked up), the index mapping is stored into the ns_map variable.
612 **
613 ** Note: we must copy all declared namespaces because we cannot easily
614 **   determine which input namespaces were actually used within the property
615 **   values that are being stored within the propdb. Theoretically, we can
616 **   determine this at the point where we serialize the property values
617 **   back into strings. This would require a bit more work, and will be
618 **   left to future optimizations.
619 **
620 ** ### we should always initialize the propdb namespace array with "DAV:"
621 ** ### since we know it will be entered anyhow (by virtue of it always
622 ** ### occurring in the ns_xlate array). That will allow us to use
623 ** ### AP_XML_NS_DAV_ID for propdb ns values, too.
624 */
625 static void dav_prep_ns_map(dav_propdb *propdb, int add_ns)
626 {
627     int i;
628     const char **puri;
629     const int orig_count = propdb->ns_count;
630     int *pmap;
631     int updating = 0;   /* are we updating an existing ns_map? */
632
633     if (propdb->ns_map) {
634         if (add_ns && propdb->incomplete_map) {
635             /* we must revisit the map and insert new entries */
636             updating = 1;
637             propdb->incomplete_map = 0;
638         }
639         else {
640             /* nothing to do: we have a proper ns_map */
641             return;
642         }
643     }
644     else {
645         propdb->ns_map = apr_palloc(propdb->p, propdb->ns_xlate->nelts * sizeof(*propdb->ns_map));
646     }
647
648     pmap = propdb->ns_map;
649
650     /* ### stupid O(n * orig_count) algorithm */
651     for (i = propdb->ns_xlate->nelts, puri = (const char **)propdb->ns_xlate->elts;
652          i-- > 0;
653          ++puri, ++pmap) {
654
655         const char *uri = *puri;
656         const size_t uri_len = strlen(uri);
657
658         if (updating) {
659             /* updating an existing mapping... we can skip a lot of stuff */
660
661             if (*pmap != AP_XML_NS_ERROR_NOT_FOUND) {
662                 /* This entry has been filled in, so we can skip it */
663                 continue;
664             }
665         }
666         else {
667             int j;
668             size_t len;
669             const char *p;
670
671             /*
672             ** GIVEN: uri (a namespace URI from the request input)
673             **
674             ** FIND: an equivalent URI in the propdb namespace table
675             */
676
677             /* only scan original entries (we may have added some in here) */
678             for (p = propdb->ns_table.buf + sizeof(dav_propdb_metadata),
679                      j = 0;
680                  j < orig_count;
681                  ++j, p += len + 1) {
682
683                 len = strlen(p);
684
685                 if (uri_len != len)
686                     continue;
687                 if (memcmp(uri, p, len) == 0) {
688                     *pmap = j;
689                     goto next_input_uri;
690                 }
691             }
692
693             if (!add_ns) {
694                 *pmap = AP_XML_NS_ERROR_NOT_FOUND;
695
696                 /*
697                 ** This flag indicates that we have an ns_map with missing
698                 ** entries. If dav_prep_ns_map() is called with add_ns==1 AND
699                 ** this flag is set, then we zip thru the array and add those
700                 ** URIs (effectively updating the ns_map as if add_ns=1 was
701                 ** passed when the initial prep was called).
702                 */
703                 propdb->incomplete_map = 1;
704
705                 continue;
706             }
707         }
708
709         /*
710         ** The input URI was not found in the propdb namespace table, and
711         ** we are supposed to add it. Append it to the table and store
712         ** the index into the ns_map.
713         */
714         dav_check_bufsize(propdb->p, &propdb->ns_table, uri_len + 1);
715         memcpy(propdb->ns_table.buf + propdb->ns_table.cur_len, uri, uri_len + 1);
716         propdb->ns_table.cur_len += uri_len + 1;
717
718         propdb->ns_table_dirty = 1;
719
720         *pmap = propdb->ns_count++;
721
722    next_input_uri:
723         ;
724     }
725 }
726
727 /* find the "DAV:" namespace in our table and return its ID. */
728 static int dav_find_dav_id(dav_propdb *propdb)
729 {
730     const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
731     int ns;
732
733     for (ns = 0; ns < propdb->ns_count; ++ns) {
734         size_t len = strlen(p);
735
736         if (len == 4 && memcmp(p, "DAV:", 5) == 0)
737             return ns;
738         p += len + 1;
739     }
740
741     /* the "DAV:" namespace is not present */
742     return -1;
743 }
744
745 static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, int ns,
746                              const char *ns_uri, ap_text_header *phdr)
747 {
748     const char *s;
749
750     s = apr_psprintf(p, " xmlns:%s%d=\"%s\"", pre_prefix, ns, ns_uri);
751     ap_text_append(p, phdr, s);
752 }
753
754 /* return all known namespaces (in this propdb) */
755 static void dav_get_propdb_xmlns(dav_propdb *propdb, ap_text_header *phdr)
756 {
757     int i;
758     const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
759     size_t len;
760
761     /* note: ns_count == 0 when we have no propdb file */
762     for (i = 0; i < propdb->ns_count; ++i, p += len + 1) {
763
764         len = strlen(p);
765
766         dav_insert_xmlns(propdb->p, "ns", i, p, phdr);
767     }
768 }
769
770 /* add a namespace decl from one of the namespace tables */
771 static void dav_add_marked_xmlns(dav_propdb *propdb, char *marks, int ns,
772                                  apr_array_header_t *ns_table,
773                                  const char *pre_prefix,
774                                  ap_text_header *phdr)
775 {
776     if (marks[ns])
777         return;
778     marks[ns] = 1;
779
780     dav_insert_xmlns(propdb->p,
781                      pre_prefix, ns, AP_XML_GET_URI_ITEM(ns_table, ns),
782                      phdr);
783 }
784
785 /*
786 ** Internal function to build a key
787 **
788 ** WARNING: returns a pointer to a "static" buffer holding the key. The
789 **          value must be copied or no longer used if this function is
790 **          called again.
791 */
792 static dav_datum dav_gdbm_key(dav_propdb *propdb, const ap_xml_elem *elem)
793 {
794     int ns;
795     char nsbuf[20];
796     size_t l_ns;
797     size_t l_name = strlen(elem->name);
798     dav_datum key = { 0 };
799
800     /*
801      * Convert namespace ID to a string. "no namespace" is an empty string,
802      * so the keys will have the form ":name". Otherwise, the keys will
803      * have the form "#:name".
804      */
805     if (elem->ns == AP_XML_NS_NONE) {
806         nsbuf[0] = '\0';
807         l_ns = 0;
808     }
809     else {
810         if (propdb->ns_map == NULL) {
811             /*
812              * Note that we prep the map and do NOT add namespaces. If that
813              * is required, then the caller should have called prep
814              * beforehand, passing the correct values.
815              */
816             dav_prep_ns_map(propdb, 0);
817         }
818
819         ns = propdb->ns_map[elem->ns];
820         if (AP_XML_NS_IS_ERROR(ns))
821             return key;         /* zeroed */
822
823         l_ns = sprintf(nsbuf, "%d", ns);
824     }
825
826     /* assemble: #:name */
827     dav_set_bufsize(propdb->p, &propdb->wb_key, l_ns + 1 + l_name + 1);
828     memcpy(propdb->wb_key.buf, nsbuf, l_ns);
829     propdb->wb_key.buf[l_ns] = ':';
830     memcpy(&propdb->wb_key.buf[l_ns + 1], elem->name, l_name + 1);
831
832     /* build the database key */
833     key.dsize = l_ns + 1 + l_name + 1;
834     key.dptr = propdb->wb_key.buf;
835
836     return key;
837 }
838
839 static dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
840 {
841     dav_error *err;
842     dav_datum key;
843     dav_datum value = { 0 };
844
845     /* we're trying to open the db; turn off the 'deferred' flag */
846     propdb->deferred = 0;
847
848     /* ask the DB provider to open the thing */
849     err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro,
850                                     &propdb->db);
851     if (err != NULL) {
852         return dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
853                               DAV_ERR_PROP_OPENING,
854                               "Could not open the property database.",
855                               err);
856     }
857
858     /*
859     ** NOTE: propdb->db could be NULL if we attempted to open a readonly
860     **       database that doesn't exist. If we require read/write
861     **       access, then a database was created and opened.
862     */
863
864     if (propdb->db != NULL) {
865         key.dptr = DAV_GDBM_NS_KEY;
866         key.dsize = DAV_GDBM_NS_KEY_LEN;
867         if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
868                                               &value)) != NULL) {
869             /* ### push a higher-level description? */
870             return err;
871         }
872     }
873     if (value.dptr == NULL) {
874         dav_propdb_metadata m = {
875             DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
876         };
877
878         if (propdb->db != NULL) {
879             /*
880              * If there is no METADATA key, then the database may be
881              * from versions 0.9.0 .. 0.9.4 (which would be incompatible).
882              * These can be identified by the presence of an NS_TABLE entry.
883              */
884             key.dptr = "NS_TABLE";
885             key.dsize = 8;
886             if ((*propdb->db_hooks->exists)(propdb->db, key)) {
887                 (*propdb->db_hooks->close)(propdb->db);
888
889                 /* call it a major version error */
890                 return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
891                                      DAV_ERR_PROP_BAD_MAJOR,
892                                      "Prop database has the wrong major "
893                                      "version number and cannot be used.");
894             }
895         }
896
897         /* initialize a new metadata structure */
898         dav_set_bufsize(propdb->p, &propdb->ns_table, sizeof(m));
899         memcpy(propdb->ns_table.buf, &m, sizeof(m));
900     }
901     else {
902         dav_propdb_metadata m;
903
904         dav_set_bufsize(propdb->p, &propdb->ns_table, value.dsize);
905         memcpy(propdb->ns_table.buf, value.dptr, value.dsize);
906
907         memcpy(&m, value.dptr, sizeof(m));
908         if (m.major != DAV_DBVSN_MAJOR) {
909             (*propdb->db_hooks->close)(propdb->db);
910
911             return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
912                                  DAV_ERR_PROP_BAD_MAJOR,
913                                  "Prop database has the wrong major "
914                                  "version number and cannot be used.");
915         }
916         propdb->version = m.minor;
917         propdb->ns_count = ntohs(m.ns_count);
918
919         (*propdb->db_hooks->freedatum)(propdb->db, value);
920     }
921
922     return NULL;
923 }
924
925 dav_error *dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
926                            const dav_resource *resource,
927                            int ro,
928                            apr_array_header_t * ns_xlate,
929                            dav_propdb **p_propdb)
930 {
931     dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
932
933     *p_propdb = NULL;
934
935 #if DAV_DEBUG
936     if (resource->uri == NULL) {
937         return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
938                              "INTERNAL DESIGN ERROR: resource must define "
939                              "its URI.");
940     }
941 #endif
942
943     propdb->version = DAV_DBVSN_MINOR;
944     propdb->r = r;
945     propdb->p = r->pool; /* ### get rid of this */
946     propdb->resource = resource;
947     propdb->ns_xlate = ns_xlate;
948
949     propdb->db_hooks = DAV_GET_HOOKS_PROPDB(r);
950
951     propdb->lockdb = lockdb;
952
953     /* always defer actual open, to avoid expense of accessing db
954      * when only live properties are involved
955      */
956     propdb->deferred = 1;
957
958     /* ### what to do about closing the propdb on server failure? */
959
960     *p_propdb = propdb;
961     return NULL;
962 }
963
964 void dav_close_propdb(dav_propdb *propdb)
965 {
966     if (propdb->db == NULL)
967         return;
968
969     if (propdb->ns_table_dirty) {
970         dav_propdb_metadata m;
971         dav_datum key;
972         dav_datum value;
973         dav_error *err;
974
975         key.dptr = DAV_GDBM_NS_KEY;
976         key.dsize = DAV_GDBM_NS_KEY_LEN;
977
978         value.dptr = propdb->ns_table.buf;
979         value.dsize = propdb->ns_table.cur_len;
980
981         /* fill in the metadata that we store into the prop db. */
982         m.major = DAV_DBVSN_MAJOR;
983         m.minor = propdb->version;      /* ### keep current minor version? */
984         m.ns_count = htons(propdb->ns_count);
985
986         memcpy(propdb->ns_table.buf, &m, sizeof(m));
987
988         err = (*propdb->db_hooks->store)(propdb->db, key, value);
989         /* ### what to do with the error? */
990     }
991
992     (*propdb->db_hooks->close)(propdb->db);
993 }
994
995 dav_get_props_result dav_get_allprops(dav_propdb *propdb, dav_prop_insert what)
996 {
997     const dav_hooks_db *db_hooks = propdb->db_hooks;
998     ap_text_header hdr = { 0 };
999     ap_text_header hdr_ns = { 0 };
1000     dav_get_props_result result = { 0 };
1001     int found_contenttype = 0;
1002     int found_contentlang = 0;
1003     dav_prop_insert unused_inserted;
1004
1005     /* if not just getting supported live properties,
1006      * scan all properties in the dead prop database
1007      */
1008     if (what != DAV_PROP_INSERT_SUPPORTED) {
1009         if (propdb->deferred) {
1010             /* ### what to do with db open error? */
1011             (void) dav_really_open_db(propdb, 1 /*ro*/);
1012         }
1013
1014         /* generate all the namespaces that are in the propdb */
1015         dav_get_propdb_xmlns(propdb, &hdr_ns);
1016
1017         /* initialize the result with some start tags... */
1018         ap_text_append(propdb->p, &hdr,
1019                        "<D:propstat>" DEBUG_CR
1020                        "<D:prop>" DEBUG_CR);
1021
1022         /* if there ARE properties, then scan them */
1023         if (propdb->db != NULL) {
1024             dav_datum key;
1025             int dav_id = dav_find_dav_id(propdb);
1026
1027             (void) (*db_hooks->firstkey)(propdb->db, &key);
1028             while (key.dptr) {
1029                 dav_datum prevkey;
1030
1031                 /* any keys with leading capital letters should be skipped
1032                    (real keys start with a number or a colon) */
1033                 if (*key.dptr >= 'A' && *key.dptr <= 'Z')
1034                     goto next_key;
1035
1036                 /*
1037                 ** We also look for <DAV:getcontenttype> and
1038                 ** <DAV:getcontentlanguage>. If they are not stored as dead
1039                 ** properties, then we need to perform a subrequest to get
1040                 ** their values (if any).
1041                 */
1042                 if (dav_id != -1
1043                     && *key.dptr != ':'
1044                     && dav_id == atoi(key.dptr)) {
1045
1046                     const char *colon;
1047
1048                     /* find the colon */
1049                     if ( key.dptr[1] == ':' ) {
1050                         colon = key.dptr + 1;
1051                     }
1052                     else {
1053                         colon = strchr(key.dptr + 2, ':');
1054                     }
1055
1056                     if (colon[1] == 'g') {
1057                         if (strcmp(colon + 1, "getcontenttype") == 0) {
1058                             found_contenttype = 1;
1059                         }
1060                         else if (strcmp(colon + 1, "getcontentlanguage") == 0) {
1061                             found_contentlang = 1;
1062                         }
1063                     }
1064                 }
1065
1066                 if (what == DAV_PROP_INSERT_VALUE) {
1067                     dav_datum value;
1068
1069                     (void) (*db_hooks->fetch)(propdb->db, key, &value);
1070                     if (value.dptr == NULL) {
1071                         /* ### anything better to do? */
1072                         /* ### probably should enter a 500 error */
1073                         goto next_key;
1074                     }
1075
1076                     /* put the prop name and value into the result */
1077                     dav_append_prop(propdb, key.dptr, value.dptr, &hdr);
1078
1079                     (*db_hooks->freedatum)(propdb->db, value);
1080                 }
1081                 else {
1082                     /* simple, empty element if a value isn't needed */
1083                     dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr);
1084                 }
1085
1086               next_key:
1087                 prevkey = key;
1088                 (void) (*db_hooks->nextkey)(propdb->db, &key);
1089                 (*db_hooks->freedatum)(propdb->db, prevkey);
1090             }
1091         }
1092
1093         /* add namespaces for all the liveprop providers */
1094         dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
1095     }
1096
1097     /* ask the liveprop providers to insert their properties */
1098     dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
1099
1100     /* insert the standard properties */
1101     /* ### should be handling the return errors here */
1102     (void)dav_insert_coreprop(propdb,
1103                               DAV_PROPID_CORE_supportedlock, "supportedlock",
1104                               what, &hdr, &unused_inserted);
1105     (void)dav_insert_coreprop(propdb,
1106                               DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
1107                               what, &hdr, &unused_inserted);
1108
1109     /* if we didn't find these, then do the whole subreq thing. */
1110     if (!found_contenttype) {
1111         /* ### should be handling the return error here */
1112         (void)dav_insert_coreprop(propdb,
1113                                   DAV_PROPID_CORE_getcontenttype,
1114                                   "getcontenttype",
1115                                   what, &hdr, &unused_inserted);
1116     }
1117     if (!found_contentlang) {
1118         /* ### should be handling the return error here */
1119         (void)dav_insert_coreprop(propdb,
1120                                   DAV_PROPID_CORE_getcontentlanguage,
1121                                   "getcontentlanguage",
1122                                   what, &hdr, &unused_inserted);
1123     }
1124
1125     /* if not just reporting on supported live props,
1126      * terminate the result */
1127     if (what != DAV_PROP_INSERT_SUPPORTED) {
1128         ap_text_append(propdb->p, &hdr,
1129                        "</D:prop>" DEBUG_CR
1130                        "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
1131                        "</D:propstat>" DEBUG_CR);
1132     }
1133
1134     result.propstats = hdr.first;
1135     result.xmlns = hdr_ns.first;
1136     return result;
1137 }
1138
1139 dav_get_props_result dav_get_props(dav_propdb *propdb, ap_xml_doc *doc)
1140 {
1141     const dav_hooks_db *db_hooks = propdb->db_hooks;
1142     ap_xml_elem *elem = dav_find_child(doc->root, "prop");
1143     ap_text_header hdr_good = { 0 };
1144     ap_text_header hdr_bad = { 0 };
1145     ap_text_header hdr_ns = { 0 };
1146     int have_good = 0;
1147     int propdb_xmlns_done = 0;
1148     dav_get_props_result result = { 0 };
1149     char *marks_input;
1150     char *marks_liveprop;
1151
1152     /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
1153        the marks */
1154
1155     /* we will ALWAYS provide a "good" result, even if it is EMPTY */
1156     ap_text_append(propdb->p, &hdr_good,
1157                    "<D:propstat>" DEBUG_CR
1158                    "<D:prop>" DEBUG_CR);
1159
1160     /* ### the marks should be in a buffer! */
1161     /* allocate zeroed-memory for the marks. These marks indicate which
1162        input namespaces we've generated into the output xmlns buffer */
1163     marks_input = apr_pcalloc(propdb->p, propdb->ns_xlate->nelts);
1164
1165     /* same for the liveprops */
1166     marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
1167
1168     for (elem = elem->first_child; elem; elem = elem->next) {
1169         dav_datum key = { 0 };
1170         dav_datum value = { 0 };
1171         dav_elem_private *priv;
1172         dav_error *err;
1173         dav_prop_insert inserted;
1174         int is_liveprop = 0;
1175
1176         /*
1177         ** First try live property providers; if they don't handle
1178         ** the property, then try looking it up in the propdb.
1179         */
1180
1181         if (elem->private == NULL) {
1182             elem->private = apr_pcalloc(propdb->p, sizeof(*priv));
1183         }
1184         priv = elem->private;
1185
1186         /* cache the propid; dav_get_props() could be called many times */
1187         if (priv->propid == 0)
1188             dav_find_liveprop(propdb, elem);
1189
1190         if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
1191             is_liveprop = 1;
1192
1193             /* insert the property. returns 1 if an insertion was done. */
1194             if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
1195                                            &hdr_good, &inserted)) != NULL) {
1196                 /* ### need to propagate the error to the caller... */
1197                 /* ### skip it for now, as if nothing was inserted */
1198             }
1199             if (inserted == DAV_PROP_INSERT_VALUE) {
1200                 have_good = 1;
1201
1202                 /*
1203                 ** Add the liveprop's namespace URIs. Note that provider==NULL
1204                 ** for core properties.
1205                 */
1206                 if (priv->provider != NULL) {
1207                     const char * const * scan_ns_uri;
1208
1209                     for (scan_ns_uri = priv->provider->namespace_uris;
1210                          *scan_ns_uri != NULL;
1211                          ++scan_ns_uri) {
1212                         int ns;
1213
1214                         ns = dav_get_liveprop_ns_index(*scan_ns_uri);
1215                         if (marks_liveprop[ns])
1216                             continue;
1217                         marks_liveprop[ns] = 1;
1218
1219                         dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri,
1220                                          &hdr_ns);
1221                     }
1222                 }
1223
1224                 continue;
1225             }
1226             else if (inserted == DAV_PROP_INSERT_NOTDEF) {
1227                 /* allow property to be handled as a dead property */
1228                 is_liveprop = 0;
1229             }
1230         }
1231
1232         /*
1233         ** If not handled as a live property, look in the dead property
1234         ** database.
1235         */
1236         if (!is_liveprop) {
1237             /* make sure propdb is really open */
1238             if (propdb->deferred) {
1239                 /* ### what to do with db open error? */
1240                 (void) dav_really_open_db(propdb, 1 /*ro*/);
1241             }
1242
1243             /* if not done yet,
1244              * generate all the namespaces that are in the propdb
1245              */
1246             if (!propdb_xmlns_done) {
1247                 dav_get_propdb_xmlns(propdb, &hdr_ns);
1248                 propdb_xmlns_done = 1;
1249             }
1250
1251             /*
1252             ** Note: the key may be NULL if we have no properties that are in
1253             ** a namespace that matches the requested prop's namespace.
1254             */
1255             key = dav_gdbm_key(propdb, elem);
1256
1257             /* fetch IF we have a db and a key. otherwise, value is NULL */
1258             if (propdb->db != NULL && key.dptr != NULL) {
1259                 (void) (*db_hooks->fetch)(propdb->db, key, &value);
1260             }
1261         }
1262
1263         if (value.dptr == NULL) {
1264             /* not found. add a record to the "bad" propstats */
1265
1266             /* make sure we've started our "bad" propstat */
1267             if (hdr_bad.first == NULL) {
1268                 ap_text_append(propdb->p, &hdr_bad,
1269                                "<D:propstat>" DEBUG_CR
1270                                "<D:prop>" DEBUG_CR);
1271             }
1272
1273             /* note: key.dptr may be NULL if the propdb doesn't have an
1274                equivalent namespace stored */
1275             if (key.dptr == NULL) {
1276                 const char *s;
1277
1278                 if (elem->ns == AP_XML_NS_NONE) {
1279                     /*
1280                      * elem has a prefix already (xml...:name) or the elem
1281                      * simply has no namespace.
1282                      */
1283                     s = apr_psprintf(propdb->p, "<%s/>" DEBUG_CR, elem->name);
1284                 }
1285                 else {
1286                     /* ensure that an xmlns is generated for the
1287                        input namespace */
1288                     dav_add_marked_xmlns(propdb, marks_input, elem->ns,
1289                                          propdb->ns_xlate, "i", &hdr_ns);
1290                     s = apr_psprintf(propdb->p, "<i%d:%s/>" DEBUG_CR,
1291                                     elem->ns, elem->name);
1292                 }
1293                 ap_text_append(propdb->p, &hdr_bad, s);
1294             }
1295             else {
1296                 /* add in the bad prop using our namespace decl */
1297                 dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr_bad);
1298             }
1299         }
1300         else {
1301             /* found it. put the value into the "good" propstats */
1302
1303             have_good = 1;
1304
1305             dav_append_prop(propdb, key.dptr, value.dptr, &hdr_good);
1306
1307             (*db_hooks->freedatum)(propdb->db, value);
1308         }
1309     }
1310
1311     ap_text_append(propdb->p, &hdr_good,
1312                     "</D:prop>" DEBUG_CR
1313                     "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
1314                     "</D:propstat>" DEBUG_CR);
1315
1316     /* default to start with the good */
1317     result.propstats = hdr_good.first;
1318
1319     /* we may not have any "bad" results */
1320     if (hdr_bad.first != NULL) {
1321         ap_text_append(propdb->p, &hdr_bad,
1322                        "</D:prop>" DEBUG_CR
1323                        "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
1324                        "</D:propstat>" DEBUG_CR);
1325
1326         /* if there are no good props, then just return the bad */
1327         if (!have_good) {
1328             result.propstats = hdr_bad.first;
1329         }
1330         else {
1331             /* hook the bad propstat to the end of the good one */
1332             hdr_good.last->next = hdr_bad.first;
1333         }
1334     }
1335
1336     result.xmlns = hdr_ns.first;
1337     return result;
1338 }
1339
1340 void dav_get_liveprop_supported(dav_propdb *propdb,
1341                                 const char *ns_uri,
1342                                 const char *propname,
1343                                 ap_text_header *body)
1344 {
1345     int propid;
1346     const dav_hooks_liveprop *hooks;
1347
1348     propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
1349
1350     if (propid != DAV_PROPID_CORE_UNKNOWN) {
1351         if (hooks == NULL) {
1352             /* this is a "core" property that we define */
1353             dav_prop_insert unused_inserted;
1354             dav_insert_coreprop(propdb, propid, propname,
1355                                 DAV_PROP_INSERT_SUPPORTED, body, &unused_inserted);
1356         }
1357         else {
1358             (*hooks->insert_prop)(propdb->resource, propid,
1359                                   DAV_PROP_INSERT_SUPPORTED, body);
1360         }
1361     }
1362 }
1363
1364 void dav_prop_validate(dav_prop_ctx *ctx)
1365 {
1366     dav_propdb *propdb = ctx->propdb;
1367     ap_xml_elem *prop = ctx->prop;
1368     dav_elem_private *priv;
1369
1370     priv = ctx->prop->private = apr_pcalloc(propdb->p, sizeof(*priv));
1371
1372     /*
1373     ** Check to see if this is a live property, and fill the fields
1374     ** in the XML elem, as appropriate.
1375     **
1376     ** Verify that the property is read/write. If not, then it cannot
1377     ** be SET or DELETEd.
1378     */
1379     if (priv->propid == 0) {
1380         dav_find_liveprop(propdb, prop);
1381
1382         /* it's a liveprop if a provider was found */
1383         /* ### actually the "core" props should really be liveprops, but
1384            ### there is no "provider" for those and the r/w props are
1385            ### treated as dead props anyhow */
1386         ctx->is_liveprop = priv->provider != NULL;
1387     }
1388
1389     if (!dav_rw_liveprop(propdb, priv)) {
1390         ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT,
1391                                  DAV_ERR_PROP_READONLY,
1392                                  "Property is read-only.");
1393         return;
1394     }
1395
1396     if (ctx->is_liveprop) {
1397         int defer_to_dead = 0;
1398
1399         ctx->err = (*priv->provider->patch_validate)(propdb->resource,
1400                                                      prop, ctx->operation,
1401                                                      &ctx->liveprop_ctx,
1402                                                      &defer_to_dead);
1403         if (ctx->err != NULL || !defer_to_dead)
1404             return;
1405
1406         /* clear is_liveprop -- act as a dead prop now */
1407         ctx->is_liveprop = 0;
1408     }
1409
1410     /*
1411     ** The property is supposed to be stored into the dead-property
1412     ** database. Make sure the thing is truly open (and writable).
1413     */
1414     if (propdb->deferred
1415         && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
1416         return;
1417     }
1418
1419     /*
1420     ** There should be an open, writable database in here!
1421     **
1422     ** Note: the database would be NULL if it was opened readonly and it
1423     **       did not exist.
1424     */
1425     if (propdb->db == NULL) {
1426         ctx->err = dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
1427                                  DAV_ERR_PROP_NO_DATABASE,
1428                                  "Attempted to set/remove a property "
1429                                  "without a valid, open, read/write "
1430                                  "property database.");
1431         return;
1432     }
1433
1434     if (ctx->operation == DAV_PROP_OP_SET) {
1435         /*
1436         ** Prep the element => propdb namespace index mapping, inserting
1437         ** namespace URIs into the propdb that don't exist.
1438         */
1439         dav_prep_ns_map(propdb, 1);
1440     }
1441     else if (ctx->operation == DAV_PROP_OP_DELETE) {
1442         /*
1443         ** There are no checks to perform here. If a property exists, then
1444         ** we will delete it. If it does not exist, then it does not matter
1445         ** (see S12.13.1).
1446         **
1447         ** Note that if a property does not exist, that does not rule out
1448         ** that a SET will occur during this PROPPATCH (thusly creating it).
1449         */
1450     }
1451 }
1452
1453 void dav_prop_exec(dav_prop_ctx *ctx)
1454 {
1455     dav_propdb *propdb = ctx->propdb;
1456     dav_error *err = NULL;
1457     dav_rollback_item *rollback;
1458     dav_elem_private *priv = ctx->prop->private;
1459
1460     rollback = apr_pcalloc(propdb->p, sizeof(*rollback));
1461     ctx->rollback = rollback;
1462
1463     if (ctx->is_liveprop) {
1464         err = (*priv->provider->patch_exec)(propdb->resource,
1465                                             ctx->prop, ctx->operation,
1466                                             ctx->liveprop_ctx,
1467                                             &ctx->rollback->liveprop);
1468     }
1469     else {
1470         dav_datum key;
1471
1472         /* we're going to need the key for all operations */
1473         key = dav_gdbm_key(propdb, ctx->prop);
1474
1475         /* save the old value so that we can do a rollback. */
1476         rollback->key = key;
1477         if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
1478                                               &rollback->value)) != NULL)
1479             goto error;
1480
1481         if (ctx->operation == DAV_PROP_OP_SET) {
1482
1483             dav_datum value;
1484
1485             /* Note: propdb->ns_map was set in dav_prop_validate() */
1486
1487             /* quote all the values in the element */
1488             ap_xml_quote_elem(propdb->p, ctx->prop);
1489
1490             /* generate a text blob for the xml:lang plus the contents */
1491             ap_xml_to_text(propdb->p, ctx->prop, AP_XML_X2T_LANG_INNER, NULL,
1492                            propdb->ns_map,
1493                            (const char **)&value.dptr, &value.dsize);
1494
1495             err = (*propdb->db_hooks->store)(propdb->db, key, value);
1496
1497             /*
1498             ** If an error occurred, then assume that we didn't change the
1499             ** value. Remove the rollback item so that we don't try to set
1500             ** its value during the rollback.
1501             */
1502         }
1503         else if (ctx->operation == DAV_PROP_OP_DELETE) {
1504
1505             /*
1506             ** Delete the property. Ignore errors -- the property is there, or
1507             ** we are deleting it for a second time.
1508             */
1509             /* ### but what about other errors? */
1510             (void) (*propdb->db_hooks->remove)(propdb->db, key);
1511         }
1512     }
1513
1514   error:
1515     /* push a more specific error here */
1516     if (err != NULL) {
1517         /*
1518         ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
1519         ** any errors at this point.
1520         */
1521         ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
1522                                   DAV_ERR_PROP_EXEC,
1523                                   "Could not execute PROPPATCH.", err);
1524     }
1525 }
1526
1527 void dav_prop_commit(dav_prop_ctx *ctx)
1528 {
1529     dav_elem_private *priv = ctx->prop->private;
1530
1531     /*
1532     ** Note that a commit implies ctx->err is NULL. The caller should assume
1533     ** a status of HTTP_OK for this case.
1534     */
1535
1536     if (ctx->is_liveprop) {
1537         (*priv->provider->patch_commit)(ctx->propdb->resource,
1538                                         ctx->operation,
1539                                         ctx->liveprop_ctx,
1540                                         ctx->rollback->liveprop);
1541     }
1542 }
1543
1544 void dav_prop_rollback(dav_prop_ctx *ctx)
1545 {
1546     dav_error *err = NULL;
1547     dav_elem_private *priv = ctx->prop->private;
1548
1549     /* do nothing if there is no rollback information. */
1550     if (ctx->rollback == NULL)
1551         return;
1552
1553     /*
1554     ** ### if we have an error, and a rollback occurs, then the namespace
1555     ** ### mods should not happen at all. Basically, the namespace management
1556     ** ### is simply a bitch.
1557     */
1558
1559     if (ctx->is_liveprop) {
1560         err = (*priv->provider->patch_rollback)(ctx->propdb->resource,
1561                                                 ctx->operation,
1562                                                 ctx->liveprop_ctx,
1563                                                 ctx->rollback->liveprop);
1564     }
1565     else if (ctx->rollback->value.dptr == NULL) {
1566         /* don't fail if the thing isn't really there */
1567         /* ### but what about other errors? */
1568         (void) (*ctx->propdb->db_hooks->remove)(ctx->propdb->db,
1569                                                 ctx->rollback->key);
1570     }
1571     else {
1572         err = (*ctx->propdb->db_hooks->store)(ctx->propdb->db,
1573                                               ctx->rollback->key,
1574                                               ctx->rollback->value);
1575     }
1576
1577     if (err != NULL) {
1578         if (ctx->err == NULL)
1579             ctx->err = err;
1580         else {
1581             dav_error *scan = err;
1582
1583             /* hook previous errors at the end of the rollback error */
1584             while (scan->prev != NULL)
1585                 scan = scan->prev;
1586             scan->prev = ctx->err;
1587             ctx->err = err;
1588         }
1589     }
1590 }