upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / dav / main / props.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18 ** DAV 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, int ns,
483                              const char *ns_uri, apr_text_header *phdr)
484 {
485     const char *s;
486
487     s = apr_psprintf(p, " xmlns:%s%d=\"%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     propdb->p = r->pool; /* ### get rid of this */
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         return;
559
560     (*propdb->db_hooks->close)(propdb->db);
561 }
562
563 DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
564                                                    dav_prop_insert what)
565 {
566     const dav_hooks_db *db_hooks = propdb->db_hooks;
567     apr_text_header hdr = { 0 };
568     apr_text_header hdr_ns = { 0 };
569     dav_get_props_result result = { 0 };
570     int found_contenttype = 0;
571     int found_contentlang = 0;
572     dav_prop_insert unused_inserted;
573
574     /* if not just getting supported live properties,
575      * scan all properties in the dead prop database
576      */
577     if (what != DAV_PROP_INSERT_SUPPORTED) {
578         if (propdb->deferred) {
579             /* ### what to do with db open error? */
580             (void) dav_really_open_db(propdb, 1 /*ro*/);
581         }
582
583         /* initialize the result with some start tags... */
584         apr_text_append(propdb->p, &hdr,
585                         "<D:propstat>" DEBUG_CR
586                         "<D:prop>" DEBUG_CR);
587
588         /* if there ARE properties, then scan them */
589         if (propdb->db != NULL) {
590             dav_xmlns_info *xi = dav_xmlns_create(propdb->p);
591             dav_prop_name name;
592
593             /* define (up front) any namespaces the db might need */
594             (void) (*db_hooks->define_namespaces)(propdb->db, xi);
595
596             /* get the first property name, beginning the scan */
597             (void) (*db_hooks->first_name)(propdb->db, &name);
598             while (name.ns != NULL) {
599
600                 /*
601                 ** We also look for <DAV:getcontenttype> and
602                 ** <DAV:getcontentlanguage>. If they are not stored as dead
603                 ** properties, then we need to perform a subrequest to get
604                 ** their values (if any).
605                 */
606                 if (*name.ns == 'D' && strcmp(name.ns, "DAV:") == 0
607                     && *name.name == 'g') {
608                     if (strcmp(name.name, "getcontenttype") == 0) {
609                         found_contenttype = 1;
610                     }
611                     else if (strcmp(name.name, "getcontentlanguage") == 0) {
612                         found_contentlang = 1;
613                     }
614                 }
615
616                 if (what == DAV_PROP_INSERT_VALUE) {
617                     dav_error *err;
618                     int found;
619
620                     if ((err = (*db_hooks->output_value)(propdb->db, &name,
621                                                          xi, &hdr,
622                                                          &found)) != NULL) {
623                         /* ### anything better to do? */
624                         /* ### probably should enter a 500 error */
625                         goto next_key;
626                     }
627                     /* assert: found == 1 */
628                 }
629                 else {
630                     /* the value was not requested, so just add an empty
631                        tag specifying the property name. */
632                     dav_output_prop_name(propdb->p, &name, xi, &hdr);
633                 }
634
635               next_key:
636                 (void) (*db_hooks->next_name)(propdb->db, &name);
637             }
638
639             /* all namespaces have been entered into xi. generate them into
640                the output now. */
641             dav_xmlns_generate(xi, &hdr_ns);
642
643         } /* propdb->db != NULL */
644
645         /* add namespaces for all the liveprop providers */
646         dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
647     }
648
649     /* ask the liveprop providers to insert their properties */
650     dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
651
652     /* insert the standard properties */
653     /* ### should be handling the return errors here */
654     (void)dav_insert_coreprop(propdb,
655                               DAV_PROPID_CORE_supportedlock, "supportedlock",
656                               what, &hdr, &unused_inserted);
657     (void)dav_insert_coreprop(propdb,
658                               DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
659                               what, &hdr, &unused_inserted);
660
661     /* if we didn't find these, then do the whole subreq thing. */
662     if (!found_contenttype) {
663         /* ### should be handling the return error here */
664         (void)dav_insert_coreprop(propdb,
665                                   DAV_PROPID_CORE_getcontenttype,
666                                   "getcontenttype",
667                                   what, &hdr, &unused_inserted);
668     }
669     if (!found_contentlang) {
670         /* ### should be handling the return error here */
671         (void)dav_insert_coreprop(propdb,
672                                   DAV_PROPID_CORE_getcontentlanguage,
673                                   "getcontentlanguage",
674                                   what, &hdr, &unused_inserted);
675     }
676
677     /* if not just reporting on supported live props,
678      * terminate the result */
679     if (what != DAV_PROP_INSERT_SUPPORTED) {
680         apr_text_append(propdb->p, &hdr,
681                         "</D:prop>" DEBUG_CR
682                         "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
683                         "</D:propstat>" DEBUG_CR);
684     }
685
686     result.propstats = hdr.first;
687     result.xmlns = hdr_ns.first;
688     return result;
689 }
690
691 DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
692                                                 apr_xml_doc *doc)
693 {
694     const dav_hooks_db *db_hooks = propdb->db_hooks;
695     apr_xml_elem *elem = dav_find_child(doc->root, "prop");
696     apr_text_header hdr_good = { 0 };
697     apr_text_header hdr_bad = { 0 };
698     apr_text_header hdr_ns = { 0 };
699     int have_good = 0;
700     dav_get_props_result result = { 0 };
701     char *marks_liveprop;
702     dav_xmlns_info *xi;
703     int xi_filled = 0;
704
705     /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
706        the marks */
707
708     /* we will ALWAYS provide a "good" result, even if it is EMPTY */
709     apr_text_append(propdb->p, &hdr_good,
710                    "<D:propstat>" DEBUG_CR
711                    "<D:prop>" DEBUG_CR);
712
713     /* ### the marks should be in a buffer! */
714     /* allocate zeroed-memory for the marks. These marks indicate which
715        liveprop namespaces we've generated into the output xmlns buffer */
716
717     /* same for the liveprops */
718     marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
719
720     xi = dav_xmlns_create(propdb->p);
721
722     for (elem = elem->first_child; elem; elem = elem->next) {
723         dav_elem_private *priv;
724         dav_error *err;
725         dav_prop_insert inserted;
726         dav_prop_name name;
727
728         /*
729         ** First try live property providers; if they don't handle
730         ** the property, then try looking it up in the propdb.
731         */
732
733         if (elem->priv == NULL) {
734             elem->priv = apr_pcalloc(propdb->p, sizeof(*priv));
735         }
736         priv = elem->priv;
737
738         /* cache the propid; dav_get_props() could be called many times */
739         if (priv->propid == 0)
740             dav_find_liveprop(propdb, elem);
741
742         if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
743
744             /* insert the property. returns 1 if an insertion was done. */
745             if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
746                                            &hdr_good, &inserted)) != NULL) {
747                 /* ### need to propagate the error to the caller... */
748                 /* ### skip it for now, as if nothing was inserted */
749             }
750             if (inserted == DAV_PROP_INSERT_VALUE) {
751                 have_good = 1;
752
753                 /*
754                 ** Add the liveprop's namespace URIs. Note that provider==NULL
755                 ** for core properties.
756                 */
757                 if (priv->provider != NULL) {
758                     const char * const * scan_ns_uri;
759
760                     for (scan_ns_uri = priv->provider->namespace_uris;
761                          *scan_ns_uri != NULL;
762                          ++scan_ns_uri) {
763                         int ns;
764
765                         ns = dav_get_liveprop_ns_index(*scan_ns_uri);
766                         if (marks_liveprop[ns])
767                             continue;
768                         marks_liveprop[ns] = 1;
769
770                         dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri,
771                                          &hdr_ns);
772                     }
773                 }
774
775                 /* property added. move on to the next property. */
776                 continue;
777             }
778             else if (inserted == DAV_PROP_INSERT_NOTDEF) {
779                 /* nothing to do. fall thru to allow property to be handled
780                    as a dead property */
781             }
782 #if DAV_DEBUG
783             else {
784 #if 0
785                 /* ### need to change signature to return an error */
786                 return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0,
787                                      "INTERNAL DESIGN ERROR: insert_liveprop "
788                                      "did not insert what was asked for.");
789 #endif
790             }
791 #endif
792         }
793
794         /* The property wasn't a live property, so look in the dead property
795            database. */
796
797         /* make sure propdb is really open */
798         if (propdb->deferred) {
799             /* ### what to do with db open error? */
800             (void) dav_really_open_db(propdb, 1 /*ro*/);
801         }
802
803         if (elem->ns == APR_XML_NS_NONE)
804             name.ns = "";
805         else
806             name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
807         name.name = elem->name;
808
809         /* only bother to look if a database exists */
810         if (propdb->db != NULL) {
811             int found;
812
813             if ((err = (*db_hooks->output_value)(propdb->db, &name,
814                                                  xi, &hdr_good,
815                                                  &found)) != NULL) {
816                 /* ### what to do? continue doesn't seem right... */
817                 continue;
818             }
819
820             if (found) {
821                 have_good = 1;
822
823                 /* if we haven't added the db's namespaces, then do so... */
824                 if (!xi_filled) {
825                     (void) (*db_hooks->define_namespaces)(propdb->db, xi);
826                     xi_filled = 1;
827                 }
828                 continue;
829             }
830         }
831
832         /* not found as a live OR dead property. add a record to the "bad"
833            propstats */
834
835         /* make sure we've started our "bad" propstat */
836         if (hdr_bad.first == NULL) {
837             apr_text_append(propdb->p, &hdr_bad,
838                             "<D:propstat>" DEBUG_CR
839                             "<D:prop>" DEBUG_CR);
840         }
841
842         /* output this property's name (into the bad propstats) */
843         dav_output_prop_name(propdb->p, &name, xi, &hdr_bad);
844     }
845
846     apr_text_append(propdb->p, &hdr_good,
847                     "</D:prop>" DEBUG_CR
848                     "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
849                     "</D:propstat>" DEBUG_CR);
850
851     /* default to start with the good */
852     result.propstats = hdr_good.first;
853
854     /* we may not have any "bad" results */
855     if (hdr_bad.first != NULL) {
856         /* "close" the bad propstat */
857         apr_text_append(propdb->p, &hdr_bad,
858                         "</D:prop>" DEBUG_CR
859                         "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
860                         "</D:propstat>" DEBUG_CR);
861
862         /* if there are no good props, then just return the bad */
863         if (!have_good) {
864             result.propstats = hdr_bad.first;
865         }
866         else {
867             /* hook the bad propstat to the end of the good one */
868             hdr_good.last->next = hdr_bad.first;
869         }
870     }
871
872     /* add in all the various namespaces, and return them */
873     dav_xmlns_generate(xi, &hdr_ns);
874     result.xmlns = hdr_ns.first;
875
876     return result;
877 }
878
879 DAV_DECLARE(void) dav_get_liveprop_supported(dav_propdb *propdb,
880                                              const char *ns_uri,
881                                              const char *propname,
882                                              apr_text_header *body)
883 {
884     int propid;
885     const dav_hooks_liveprop *hooks;
886
887     propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
888
889     if (propid != DAV_PROPID_CORE_UNKNOWN) {
890         if (hooks == NULL) {
891             /* this is a "core" property that we define */
892             dav_prop_insert unused_inserted;
893             dav_insert_coreprop(propdb, propid, propname,
894                                 DAV_PROP_INSERT_SUPPORTED, body, &unused_inserted);
895         }
896         else {
897             (*hooks->insert_prop)(propdb->resource, propid,
898                                   DAV_PROP_INSERT_SUPPORTED, body);
899         }
900     }
901 }
902
903 DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx)
904 {
905     dav_propdb *propdb = ctx->propdb;
906     apr_xml_elem *prop = ctx->prop;
907     dav_elem_private *priv;
908
909     priv = ctx->prop->priv = apr_pcalloc(propdb->p, sizeof(*priv));
910
911     /*
912     ** Check to see if this is a live property, and fill the fields
913     ** in the XML elem, as appropriate.
914     **
915     ** Verify that the property is read/write. If not, then it cannot
916     ** be SET or DELETEd.
917     */
918     if (priv->propid == 0) {
919         dav_find_liveprop(propdb, prop);
920
921         /* it's a liveprop if a provider was found */
922         /* ### actually the "core" props should really be liveprops, but
923            ### there is no "provider" for those and the r/w props are
924            ### treated as dead props anyhow */
925         ctx->is_liveprop = priv->provider != NULL;
926     }
927
928     if (!dav_rw_liveprop(propdb, priv)) {
929         ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT,
930                                  DAV_ERR_PROP_READONLY,
931                                  "Property is read-only.");
932         return;
933     }
934
935     if (ctx->is_liveprop) {
936         int defer_to_dead = 0;
937
938         ctx->err = (*priv->provider->patch_validate)(propdb->resource,
939                                                      prop, ctx->operation,
940                                                      &ctx->liveprop_ctx,
941                                                      &defer_to_dead);
942         if (ctx->err != NULL || !defer_to_dead)
943             return;
944
945         /* clear is_liveprop -- act as a dead prop now */
946         ctx->is_liveprop = 0;
947     }
948
949     /*
950     ** The property is supposed to be stored into the dead-property
951     ** database. Make sure the thing is truly open (and writable).
952     */
953     if (propdb->deferred
954         && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
955         return;
956     }
957
958     /*
959     ** There should be an open, writable database in here!
960     **
961     ** Note: the database would be NULL if it was opened readonly and it
962     **       did not exist.
963     */
964     if (propdb->db == NULL) {
965         ctx->err = dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
966                                  DAV_ERR_PROP_NO_DATABASE,
967                                  "Attempted to set/remove a property "
968                                  "without a valid, open, read/write "
969                                  "property database.");
970         return;
971     }
972
973     if (ctx->operation == DAV_PROP_OP_SET) {
974         /*
975         ** Prep the element => propdb namespace index mapping, inserting
976         ** namespace URIs into the propdb that don't exist.
977         */
978         (void) (*propdb->db_hooks->map_namespaces)(propdb->db,
979                                                    propdb->ns_xlate,
980                                                    &propdb->mapping);
981     }
982     else if (ctx->operation == DAV_PROP_OP_DELETE) {
983         /*
984         ** There are no checks to perform here. If a property exists, then
985         ** we will delete it. If it does not exist, then it does not matter
986         ** (see S12.13.1).
987         **
988         ** Note that if a property does not exist, that does not rule out
989         ** that a SET will occur during this PROPPATCH (thusly creating it).
990         */
991     }
992 }
993
994 DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx)
995 {
996     dav_propdb *propdb = ctx->propdb;
997     dav_error *err = NULL;
998     dav_elem_private *priv = ctx->prop->priv;
999
1000     ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback));
1001
1002     if (ctx->is_liveprop) {
1003         err = (*priv->provider->patch_exec)(propdb->resource,
1004                                             ctx->prop, ctx->operation,
1005                                             ctx->liveprop_ctx,
1006                                             &ctx->rollback->liveprop);
1007     }
1008     else {
1009         dav_prop_name name;
1010
1011         if (ctx->prop->ns == APR_XML_NS_NONE)
1012             name.ns = "";
1013         else
1014             name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns);
1015         name.name = ctx->prop->name;
1016
1017         /* save the old value so that we can do a rollback. */
1018         if ((err = (*propdb->db_hooks
1019                     ->get_rollback)(propdb->db, &name,
1020                                     &ctx->rollback->deadprop)) != NULL)
1021             goto error;
1022
1023         if (ctx->operation == DAV_PROP_OP_SET) {
1024
1025             /* Note: propdb->mapping was set in dav_prop_validate() */
1026             err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
1027                                              propdb->mapping);
1028
1029             /*
1030             ** If an error occurred, then assume that we didn't change the
1031             ** value. Remove the rollback item so that we don't try to set
1032             ** its value during the rollback.
1033             */
1034             /* ### euh... where is the removal? */
1035         }
1036         else if (ctx->operation == DAV_PROP_OP_DELETE) {
1037
1038             /*
1039             ** Delete the property. Ignore errors -- the property is there, or
1040             ** we are deleting it for a second time.
1041             */
1042             /* ### but what about other errors? */
1043             (void) (*propdb->db_hooks->remove)(propdb->db, &name);
1044         }
1045     }
1046
1047   error:
1048     /* push a more specific error here */
1049     if (err != NULL) {
1050         /*
1051         ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
1052         ** any errors at this point.
1053         */
1054         ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
1055                                   DAV_ERR_PROP_EXEC,
1056                                   "Could not execute PROPPATCH.", err);
1057     }
1058 }
1059
1060 DAV_DECLARE_NONSTD(void) dav_prop_commit(dav_prop_ctx *ctx)
1061 {
1062     dav_elem_private *priv = ctx->prop->priv;
1063
1064     /*
1065     ** Note that a commit implies ctx->err is NULL. The caller should assume
1066     ** a status of HTTP_OK for this case.
1067     */
1068
1069     if (ctx->is_liveprop) {
1070         (*priv->provider->patch_commit)(ctx->propdb->resource,
1071                                         ctx->operation,
1072                                         ctx->liveprop_ctx,
1073                                         ctx->rollback->liveprop);
1074     }
1075 }
1076
1077 DAV_DECLARE_NONSTD(void) dav_prop_rollback(dav_prop_ctx *ctx)
1078 {
1079     dav_error *err = NULL;
1080     dav_elem_private *priv = ctx->prop->priv;
1081
1082     /* do nothing if there is no rollback information. */
1083     if (ctx->rollback == NULL)
1084         return;
1085
1086     /*
1087     ** ### if we have an error, and a rollback occurs, then the namespace
1088     ** ### mods should not happen at all. Basically, the namespace management
1089     ** ### is simply a bitch.
1090     */
1091
1092     if (ctx->is_liveprop) {
1093         err = (*priv->provider->patch_rollback)(ctx->propdb->resource,
1094                                                 ctx->operation,
1095                                                 ctx->liveprop_ctx,
1096                                                 ctx->rollback->liveprop);
1097     }
1098     else {
1099         err = (*ctx->propdb->db_hooks
1100                ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop);
1101     }
1102
1103     if (err != NULL) {
1104         if (ctx->err == NULL)
1105             ctx->err = err;
1106         else {
1107             dav_error *scan = err;
1108
1109             /* hook previous errors at the end of the rollback error */
1110             while (scan->prev != NULL)
1111                 scan = scan->prev;
1112             scan->prev = ctx->err;
1113             ctx->err = err;
1114         }
1115     }
1116 }