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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 ** DAV extension module for Apache 2.0.*
19 ** - Property database handling (repository-independent)
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.
29 ** The DBM keys for the properties have the following form:
31 ** namespace ":" propname
33 ** For example: 5:author
35 ** The namespace provides an integer index into the namespace table
36 ** (see below). propname is simply the property name, without a namespace
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
46 ** The propname will contain the prefix and the property name. For
47 ** example, a key might be ":xmlfoo:name"
49 ** The ":name" style will also be used for properties that do not
50 ** exist within a namespace.
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.
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.
65 ** For example: http://www.foo.bar/standards/props/
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).
72 ** The namespaces are stored in the DBM database under the "METADATA" key.
75 ** STRIPPING NAMESPACES
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.
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
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.
95 ** ATTRIBUTES ON PROPERTY NAME ELEMENTS
97 ** When getting/setting properties, the XML used looks like:
100 ** <propname1>value</propname1>
101 ** <propname2>value</propname1>
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).
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.
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.
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).
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
139 ** ==> we must devise a scheme where we can "garbage collect" the namespace
140 ** entries from the property database.
144 #include "apr_strings.h"
146 #define APR_WANT_STDIO
147 #define APR_WANT_BYTEFUNC
148 #include "apr_want.h"
152 #include "http_log.h"
153 #include "http_request.h"
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.
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)
166 #define DAV_DISABLE_WRITABLE_PROPS 1
168 #define DAV_EMPTY_VALUE "\0" /* TWO null terms */
171 apr_pool_t *p; /* the pool we should use */
172 request_rec *r; /* the request record */
174 const dav_resource *resource; /* the target resource */
176 int deferred; /* open of db has been deferred */
177 dav_db *db; /* underlying database containing props */
179 apr_array_header_t *ns_xlate; /* translation of an elem->ns to URI */
180 dav_namespace_map *mapping; /* namespace mapping */
182 dav_lockdb *lockdb; /* the lock database */
184 dav_buffer wb_lock; /* work buffer for lockdiscovery property */
186 /* if we ever run a GET subreq, it will be stored here */
189 /* hooks we should use for processing (based on the target resource) */
190 const dav_hooks_db *db_hooks;
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[] =
198 "getcontentlanguage",
205 DAV_PROPID_CORE_getcontenttype = DAV_PROPID_CORE,
206 DAV_PROPID_CORE_getcontentlanguage,
207 DAV_PROPID_CORE_lockdiscovery,
208 DAV_PROPID_CORE_supportedlock,
210 DAV_PROPID_CORE_UNKNOWN
214 ** This structure is used to track information needed for a rollback.
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;
225 static int dav_find_liveprop_provider(dav_propdb *propdb,
227 const char *propname,
228 const dav_hooks_liveprop **provider)
234 if (ns_uri == NULL) {
235 /* policy: liveprop providers cannot define no-namespace properties */
236 return DAV_PROPID_CORE_UNKNOWN;
239 /* check liveprop providers first, so they can define core properties */
240 propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
246 /* check for core property */
247 if (strcmp(ns_uri, "DAV:") == 0) {
248 const char * const *p = dav_core_props;
250 for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
251 if (strcmp(propname, *p) == 0) {
256 /* no provider for this property */
257 return DAV_PROPID_CORE_UNKNOWN;
260 static void dav_find_liveprop(dav_propdb *propdb, apr_xml_elem *elem)
263 dav_elem_private *priv = elem->priv;
264 const dav_hooks_liveprop *hooks;
267 if (elem->ns == APR_XML_NS_NONE)
269 else if (elem->ns == APR_XML_NS_DAV_ID)
272 ns_uri = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
274 priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name,
277 /* ### this test seems redundant... */
278 if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
279 priv->provider = hooks;
283 /* is the live property read/write? */
284 static int dav_rw_liveprop(dav_propdb *propdb, dav_elem_private *priv)
286 int propid = priv->propid;
289 ** Check the liveprop provider (if this is a provider-defined prop)
291 if (priv->provider != NULL) {
292 return (*priv->provider->is_writable)(propdb->resource, propid);
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
301 || propid == DAV_PROPID_CORE_supportedlock
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) {
316 ** We don't recognize the property, so it must be dead (and writable)
321 /* do a sub-request to fetch properties for the target resource's URI. */
322 static void dav_do_prop_subreq(dav_propdb *propdb)
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,
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)
336 const char *value = NULL;
339 *inserted = DAV_PROP_INSERT_NOTDEF;
341 /* fast-path the common case */
342 if (propid == DAV_PROPID_CORE_UNKNOWN)
347 case DAV_PROPID_CORE_lockdiscovery:
348 if (propdb->lockdb != NULL) {
351 if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
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.",
360 /* fast-path the no-locks case */
366 ** This may modify the buffer. value may point to
367 ** wb_lock.pbuf or a string constant.
369 value = dav_lock_get_activelock(propdb->r, locks,
372 /* make a copy to isolate it from changes to wb_lock */
373 value = apr_pstrdup(propdb->p, propdb->wb_lock.buf);
378 case DAV_PROPID_CORE_supportedlock:
379 if (propdb->lockdb != NULL) {
380 value = (*propdb->lockdb->hooks->get_supportedlock)(propdb->resource);
384 case DAV_PROPID_CORE_getcontenttype:
385 if (propdb->subreq == NULL) {
386 dav_do_prop_subreq(propdb);
388 if (propdb->subreq->content_type != NULL) {
389 value = propdb->subreq->content_type;
393 case DAV_PROPID_CORE_getcontentlanguage:
397 if (propdb->subreq == NULL) {
398 dav_do_prop_subreq(propdb);
400 if ((lang = apr_table_get(propdb->subreq->headers_out,
401 "Content-Language")) != NULL) {
408 /* fall through to interpret as a dead property */
412 /* if something was supplied, then insert it */
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:"
420 s = apr_psprintf(propdb->p,
421 "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
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,
430 /* use D: prefix to refer to the DAV: namespace URI */
431 s = apr_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name);
433 apr_text_append(propdb->p, phdr, s);
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)
447 dav_elem_private *priv = elem->priv;
449 *inserted = DAV_PROP_INSERT_NOTDEF;
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);
457 /* ask the provider (that defined this prop) to insert the prop */
458 *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
464 static void dav_output_prop_name(apr_pool_t *pool,
465 const dav_prop_name *name,
467 apr_text_header *phdr)
471 if (*name->ns == '\0')
472 s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name->name);
474 const char *prefix = dav_xmlns_add_uri(xi, name->ns);
476 s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name);
479 apr_text_append(pool, phdr, s);
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)
487 s = apr_psprintf(p, " xmlns:%s%d=\"%s\"", pre_prefix, ns, ns_uri);
488 apr_text_append(p, phdr, s);
491 static dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
495 /* we're trying to open the db; turn off the 'deferred' flag */
496 propdb->deferred = 0;
498 /* ask the DB provider to open the thing */
499 err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro,
502 return dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
503 DAV_ERR_PROP_OPENING,
504 "Could not open the property database.",
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.
517 DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
518 const dav_resource *resource,
520 apr_array_header_t * ns_xlate,
521 dav_propdb **p_propdb)
523 dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
528 if (resource->uri == NULL) {
529 return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
530 "INTERNAL DESIGN ERROR: resource must define "
536 propdb->p = r->pool; /* ### get rid of this */
537 propdb->resource = resource;
538 propdb->ns_xlate = ns_xlate;
540 propdb->db_hooks = DAV_GET_HOOKS_PROPDB(r);
542 propdb->lockdb = lockdb;
544 /* always defer actual open, to avoid expense of accessing db
545 * when only live properties are involved
547 propdb->deferred = 1;
549 /* ### what to do about closing the propdb on server failure? */
555 DAV_DECLARE(void) dav_close_propdb(dav_propdb *propdb)
557 if (propdb->db == NULL)
560 (*propdb->db_hooks->close)(propdb->db);
563 DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
564 dav_prop_insert what)
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;
574 /* if not just getting supported live properties,
575 * scan all properties in the dead prop database
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*/);
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);
588 /* if there ARE properties, then scan them */
589 if (propdb->db != NULL) {
590 dav_xmlns_info *xi = dav_xmlns_create(propdb->p);
593 /* define (up front) any namespaces the db might need */
594 (void) (*db_hooks->define_namespaces)(propdb->db, xi);
596 /* get the first property name, beginning the scan */
597 (void) (*db_hooks->first_name)(propdb->db, &name);
598 while (name.ns != NULL) {
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).
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;
611 else if (strcmp(name.name, "getcontentlanguage") == 0) {
612 found_contentlang = 1;
616 if (what == DAV_PROP_INSERT_VALUE) {
620 if ((err = (*db_hooks->output_value)(propdb->db, &name,
623 /* ### anything better to do? */
624 /* ### probably should enter a 500 error */
627 /* assert: found == 1 */
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);
636 (void) (*db_hooks->next_name)(propdb->db, &name);
639 /* all namespaces have been entered into xi. generate them into
641 dav_xmlns_generate(xi, &hdr_ns);
643 } /* propdb->db != NULL */
645 /* add namespaces for all the liveprop providers */
646 dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
649 /* ask the liveprop providers to insert their properties */
650 dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
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);
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,
667 what, &hdr, &unused_inserted);
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);
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,
682 "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
683 "</D:propstat>" DEBUG_CR);
686 result.propstats = hdr.first;
687 result.xmlns = hdr_ns.first;
691 DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
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 };
700 dav_get_props_result result = { 0 };
701 char *marks_liveprop;
705 /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
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);
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 */
717 /* same for the liveprops */
718 marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
720 xi = dav_xmlns_create(propdb->p);
722 for (elem = elem->first_child; elem; elem = elem->next) {
723 dav_elem_private *priv;
725 dav_prop_insert inserted;
729 ** First try live property providers; if they don't handle
730 ** the property, then try looking it up in the propdb.
733 if (elem->priv == NULL) {
734 elem->priv = apr_pcalloc(propdb->p, sizeof(*priv));
738 /* cache the propid; dav_get_props() could be called many times */
739 if (priv->propid == 0)
740 dav_find_liveprop(propdb, elem);
742 if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
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 */
750 if (inserted == DAV_PROP_INSERT_VALUE) {
754 ** Add the liveprop's namespace URIs. Note that provider==NULL
755 ** for core properties.
757 if (priv->provider != NULL) {
758 const char * const * scan_ns_uri;
760 for (scan_ns_uri = priv->provider->namespace_uris;
761 *scan_ns_uri != NULL;
765 ns = dav_get_liveprop_ns_index(*scan_ns_uri);
766 if (marks_liveprop[ns])
768 marks_liveprop[ns] = 1;
770 dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri,
775 /* property added. move on to the next property. */
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 */
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.");
794 /* The property wasn't a live property, so look in the dead property
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*/);
803 if (elem->ns == APR_XML_NS_NONE)
806 name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
807 name.name = elem->name;
809 /* only bother to look if a database exists */
810 if (propdb->db != NULL) {
813 if ((err = (*db_hooks->output_value)(propdb->db, &name,
816 /* ### what to do? continue doesn't seem right... */
823 /* if we haven't added the db's namespaces, then do so... */
825 (void) (*db_hooks->define_namespaces)(propdb->db, xi);
832 /* not found as a live OR dead property. add a record to the "bad"
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);
842 /* output this property's name (into the bad propstats) */
843 dav_output_prop_name(propdb->p, &name, xi, &hdr_bad);
846 apr_text_append(propdb->p, &hdr_good,
848 "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
849 "</D:propstat>" DEBUG_CR);
851 /* default to start with the good */
852 result.propstats = hdr_good.first;
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,
859 "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
860 "</D:propstat>" DEBUG_CR);
862 /* if there are no good props, then just return the bad */
864 result.propstats = hdr_bad.first;
867 /* hook the bad propstat to the end of the good one */
868 hdr_good.last->next = hdr_bad.first;
872 /* add in all the various namespaces, and return them */
873 dav_xmlns_generate(xi, &hdr_ns);
874 result.xmlns = hdr_ns.first;
879 DAV_DECLARE(void) dav_get_liveprop_supported(dav_propdb *propdb,
881 const char *propname,
882 apr_text_header *body)
885 const dav_hooks_liveprop *hooks;
887 propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
889 if (propid != DAV_PROPID_CORE_UNKNOWN) {
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);
897 (*hooks->insert_prop)(propdb->resource, propid,
898 DAV_PROP_INSERT_SUPPORTED, body);
903 DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx)
905 dav_propdb *propdb = ctx->propdb;
906 apr_xml_elem *prop = ctx->prop;
907 dav_elem_private *priv;
909 priv = ctx->prop->priv = apr_pcalloc(propdb->p, sizeof(*priv));
912 ** Check to see if this is a live property, and fill the fields
913 ** in the XML elem, as appropriate.
915 ** Verify that the property is read/write. If not, then it cannot
916 ** be SET or DELETEd.
918 if (priv->propid == 0) {
919 dav_find_liveprop(propdb, prop);
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;
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.");
935 if (ctx->is_liveprop) {
936 int defer_to_dead = 0;
938 ctx->err = (*priv->provider->patch_validate)(propdb->resource,
939 prop, ctx->operation,
942 if (ctx->err != NULL || !defer_to_dead)
945 /* clear is_liveprop -- act as a dead prop now */
946 ctx->is_liveprop = 0;
950 ** The property is supposed to be stored into the dead-property
951 ** database. Make sure the thing is truly open (and writable).
954 && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
959 ** There should be an open, writable database in here!
961 ** Note: the database would be NULL if it was opened readonly and it
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.");
973 if (ctx->operation == DAV_PROP_OP_SET) {
975 ** Prep the element => propdb namespace index mapping, inserting
976 ** namespace URIs into the propdb that don't exist.
978 (void) (*propdb->db_hooks->map_namespaces)(propdb->db,
982 else if (ctx->operation == DAV_PROP_OP_DELETE) {
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
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).
994 DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx)
996 dav_propdb *propdb = ctx->propdb;
997 dav_error *err = NULL;
998 dav_elem_private *priv = ctx->prop->priv;
1000 ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback));
1002 if (ctx->is_liveprop) {
1003 err = (*priv->provider->patch_exec)(propdb->resource,
1004 ctx->prop, ctx->operation,
1006 &ctx->rollback->liveprop);
1011 if (ctx->prop->ns == APR_XML_NS_NONE)
1014 name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns);
1015 name.name = ctx->prop->name;
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)
1023 if (ctx->operation == DAV_PROP_OP_SET) {
1025 /* Note: propdb->mapping was set in dav_prop_validate() */
1026 err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
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.
1034 /* ### euh... where is the removal? */
1036 else if (ctx->operation == DAV_PROP_OP_DELETE) {
1039 ** Delete the property. Ignore errors -- the property is there, or
1040 ** we are deleting it for a second time.
1042 /* ### but what about other errors? */
1043 (void) (*propdb->db_hooks->remove)(propdb->db, &name);
1048 /* push a more specific error here */
1051 ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
1052 ** any errors at this point.
1054 ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
1056 "Could not execute PROPPATCH.", err);
1060 DAV_DECLARE_NONSTD(void) dav_prop_commit(dav_prop_ctx *ctx)
1062 dav_elem_private *priv = ctx->prop->priv;
1065 ** Note that a commit implies ctx->err is NULL. The caller should assume
1066 ** a status of HTTP_OK for this case.
1069 if (ctx->is_liveprop) {
1070 (*priv->provider->patch_commit)(ctx->propdb->resource,
1073 ctx->rollback->liveprop);
1077 DAV_DECLARE_NONSTD(void) dav_prop_rollback(dav_prop_ctx *ctx)
1079 dav_error *err = NULL;
1080 dav_elem_private *priv = ctx->prop->priv;
1082 /* do nothing if there is no rollback information. */
1083 if (ctx->rollback == NULL)
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.
1092 if (ctx->is_liveprop) {
1093 err = (*priv->provider->patch_rollback)(ctx->propdb->resource,
1096 ctx->rollback->liveprop);
1099 err = (*ctx->propdb->db_hooks
1100 ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop);
1104 if (ctx->err == NULL)
1107 dav_error *scan = err;
1109 /* hook previous errors at the end of the rollback error */
1110 while (scan->prev != NULL)
1112 scan->prev = ctx->err;