Apache HTTPD
props.c
Go to the documentation of this file.
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#define DAV_PROP_ELEMENT "mod_dav-element"
171
173 apr_pool_t *p; /* the pool we should use */
174 request_rec *r; /* the request record */
175
176 const dav_resource *resource; /* the target resource */
177
178 int deferred; /* open of db has been deferred */
179 dav_db *db; /* underlying database containing props */
180
181 apr_array_header_t *ns_xlate; /* translation of an elem->ns to URI */
182 dav_namespace_map *mapping; /* namespace mapping */
183
184 dav_lockdb *lockdb; /* the lock database */
185
186 dav_buffer wb_lock; /* work buffer for lockdiscovery property */
187
188 int flags; /* ro, disable lock discovery */
189
190 /* if we ever run a GET subreq, it will be stored here */
192
193 /* hooks we should use for processing (based on the target resource) */
195};
196
197/* NOTE: dav_core_props[] and the following enum must stay in sync. */
198/* ### move these into a "core" liveprop provider? */
199static const char * const dav_core_props[] =
200{
201 "getcontenttype",
202 "getcontentlanguage",
203 "lockdiscovery",
204 "supportedlock",
205
206 NULL /* sentinel */
207};
208enum {
213
216
217/*
218** This structure is used to track information needed for a rollback.
219*/
220typedef struct dav_rollback_item {
221 /* select one of the two rollback context structures based on the
222 value of dav_prop_ctx.is_liveprop */
225
227
228
230 const char *ns_uri,
231 const char *propname,
232 const dav_hooks_liveprop **provider)
233{
234 int propid;
235
236 *provider = NULL;
237
238 if (ns_uri == NULL) {
239 /* policy: liveprop providers cannot define no-namespace properties */
241 }
242
243 /* check liveprop providers first, so they can define core properties */
245 provider);
246 if (propid != 0) {
247 return propid;
248 }
249
250 /* check for core property */
251 if (strcmp(ns_uri, "DAV:") == 0) {
252 const char * const *p = dav_core_props;
253
254 for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
255 if (strcmp(propname, *p) == 0) {
256 return propid;
257 }
258 }
259
260 /* no provider for this property */
262}
263
265{
266 const char *ns_uri;
267 dav_elem_private *priv = elem->priv;
269
270
271 if (elem->ns == APR_XML_NS_NONE)
272 ns_uri = NULL;
273 else if (elem->ns == APR_XML_NS_DAV_ID)
274 ns_uri = "DAV:";
275 else
277
278 priv->propid = dav_find_liveprop_provider(propdb, ns_uri, elem->name,
279 &hooks);
280
281 /* ### this test seems redundant... */
282 if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
283 priv->provider = hooks;
284 }
285}
286
287/* is the live property read/write? */
289{
290 int propid = priv->propid;
291
292 /*
293 ** Check the liveprop provider (if this is a provider-defined prop)
294 */
295 if (priv->provider != NULL) {
296 return (*priv->provider->is_writable)(propdb->resource, propid);
297 }
298
299 /* these are defined as read-only */
304#endif
306 ) {
307
308 return 0;
309 }
310
311 /* these are defined as read/write */
314 || propid == DAV_PROPID_CORE_UNKNOWN) {
315
316 return 1;
317 }
318
319 /*
320 ** We don't recognize the property, so it must be dead (and writable)
321 */
322 return 1;
323}
324
325/* do a sub-request to fetch properties for the target resource's URI. */
326static void dav_do_prop_subreq(dav_propdb *propdb)
327{
328 /* need to escape the uri that's in the resource struct because during
329 * the property walker it's not encoded. */
330 const char *e_uri = ap_escape_uri(propdb->p,
331 propdb->resource->uri);
332
333 /* perform a "GET" on the resource's URI (note that the resource
334 may not correspond to the current request!). */
335 propdb->subreq = ap_sub_req_lookup_uri(e_uri, propdb->r, NULL);
336}
337
339 int propid, const char *name,
343{
344 const char *value = NULL;
345 dav_error *err;
346
348
349 /* fast-path the common case */
350 if (propid == DAV_PROPID_CORE_UNKNOWN)
351 return NULL;
352
353 switch (propid) {
354
357 value = "";
358 break;
359 }
360
361 if (propdb->lockdb != NULL) {
362 dav_lock *locks;
363
364 if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
365 &locks)) != NULL) {
366 return dav_push_error(propdb->p, err->status, 0,
367 "DAV:lockdiscovery could not be "
368 "determined due to a problem fetching "
369 "the locks for this resource.",
370 err);
371 }
372
373 /* fast-path the no-locks case */
374 if (locks == NULL) {
375 value = "";
376 }
377 else {
378 /*
379 ** This may modify the buffer. value may point to
380 ** wb_lock.pbuf or a string constant.
381 */
382 value = dav_lock_get_activelock(propdb->r, locks,
383 &propdb->wb_lock);
384
385 /* make a copy to isolate it from changes to wb_lock */
386 value = apr_pstrdup(propdb->p, propdb->wb_lock.buf);
387 }
388 }
389 break;
390
392 if (propdb->lockdb != NULL) {
393 value = (*propdb->lockdb->hooks->get_supportedlock)(propdb->resource);
394 }
395 break;
396
398 if (propdb->subreq == NULL) {
399 dav_do_prop_subreq(propdb);
400 }
401 if (propdb->subreq->content_type != NULL) {
402 value = propdb->subreq->content_type;
403 }
404 break;
405
407 {
408 const char *lang;
409
410 if (propdb->subreq == NULL) {
411 dav_do_prop_subreq(propdb);
412 }
413 if ((lang = apr_table_get(propdb->subreq->headers_out,
414 "Content-Language")) != NULL) {
415 value = lang;
416 }
417 break;
418 }
419
420 default:
421 /* fall through to interpret as a dead property */
422 break;
423 }
424
425 /* if something was supplied, then insert it */
426 if (value != NULL) {
427 const char *s;
428
430 /* use D: prefix to refer to the DAV: namespace URI,
431 * and let the namespace attribute default to "DAV:"
432 */
433 s = apr_pstrcat(propdb->p,
434 "<D:supported-live-property D:name=\"",
435 name, "\"/>" DEBUG_CR, NULL);
436 }
437 else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
438 /* use D: prefix to refer to the DAV: namespace URI */
439 s = apr_pstrcat(propdb->p, "<D:", name, ">", value, "</D:", name,
440 ">" DEBUG_CR, NULL);
441 }
442 else {
443 /* use D: prefix to refer to the DAV: namespace URI */
444 s = apr_pstrcat(propdb->p, "<D:", name, "/>" DEBUG_CR, NULL);
445 }
446 apr_text_append(propdb->p, phdr, s);
447
448 *inserted = what;
449 }
450
451 return NULL;
452}
453
455 const apr_xml_elem *elem,
459{
460 dav_elem_private *priv = elem->priv;
461
463
464 if (priv->provider == NULL) {
465 /* this is a "core" property that we define */
466 return dav_insert_coreprop(propdb, priv->propid, elem->name,
467 what, phdr, inserted);
468 }
469
470 /* ask the provider (that defined this prop) to insert the prop */
471 *inserted = (*priv->provider->insert_prop)(propdb->resource, priv->propid,
472 what, phdr);
473
474 return NULL;
475}
476
478 const dav_prop_name *name,
481{
482 const char *s;
483
484 if (*name->ns == '\0')
485 s = apr_pstrcat(pool, "<", name->name, "/>" DEBUG_CR, NULL);
486 else {
487 const char *prefix = dav_xmlns_add_uri(xi, name->ns);
488
489 s = apr_pstrcat(pool, "<", prefix, ":", name->name, "/>" DEBUG_CR, NULL);
490 }
491
493}
494
495static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, long ns,
496 const char *ns_uri, apr_text_header *phdr)
497{
498 const char *s;
499
500 s = apr_psprintf(p, " xmlns:%s%ld=\"%s\"", pre_prefix, ns, ns_uri);
502}
503
505{
506 dav_error *err;
507
508 /* we're trying to open the db; turn off the 'deferred' flag */
509 propdb->deferred = 0;
510
511 /* ask the DB provider to open the thing */
512 err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro,
513 &propdb->db);
514 if (err != NULL) {
517 "Could not open the property database.",
518 err);
519 }
520
521 /*
522 ** NOTE: propdb->db could be NULL if we attempted to open a readonly
523 ** database that doesn't exist. If we require read/write
524 ** access, then a database was created and opened.
525 */
526
527 return NULL;
528}
529
531 const dav_resource *resource,
532 int flags,
533 apr_array_header_t * ns_xlate,
535{
536 return dav_popen_propdb(r->pool, r, lockdb, resource,
537 flags, ns_xlate, p_propdb);
538}
539
541 request_rec *r, dav_lockdb *lockdb,
542 const dav_resource *resource,
543 int flags,
544 apr_array_header_t * ns_xlate,
546{
547 dav_propdb *propdb = NULL;
548
549 propdb = apr_pcalloc(p, sizeof(*propdb));
550 propdb->p = p;
551
552 *p_propdb = NULL;
553
554#if DAV_DEBUG
555 if (resource->uri == NULL) {
557 "INTERNAL DESIGN ERROR: resource must define "
558 "its URI.");
559 }
560#endif
561
562 propdb->r = r;
563 propdb->resource = resource;
564 propdb->ns_xlate = ns_xlate;
565
567
568 propdb->lockdb = lockdb;
569
570 propdb->flags = flags;
571
572 /* always defer actual open, to avoid expense of accessing db
573 * when only live properties are involved
574 */
575 propdb->deferred = 1;
576
577 /* ### what to do about closing the propdb on server failure? */
578
579 *p_propdb = propdb;
580 return NULL;
581}
582
584{
585 if (propdb->db != NULL) {
586 (*propdb->db_hooks->close)(propdb->db);
587 }
588
589 if (propdb->subreq) {
590 ap_destroy_sub_req(propdb->subreq);
591 propdb->subreq = NULL;
592 }
593}
594
597{
598 const dav_hooks_db *db_hooks = propdb->db_hooks;
599 apr_text_header hdr = { 0 };
600 apr_text_header hdr_ns = { 0 };
602 int found_contenttype = 0;
603 int found_contentlang = 0;
605
606 /* if not just getting supported live properties,
607 * scan all properties in the dead prop database
608 */
610 if (propdb->deferred) {
611 /* ### what to do with db open error? */
612 (void) dav_really_open_db(propdb, 1 /*ro*/);
613 }
614
615 /* initialize the result with some start tags... */
616 apr_text_append(propdb->p, &hdr,
617 "<D:propstat>" DEBUG_CR
618 "<D:prop>" DEBUG_CR);
619
620 /* if there ARE properties, then scan them */
621 if (propdb->db != NULL) {
624 dav_error *err;
625
626 /* define (up front) any namespaces the db might need */
627 (void) (*db_hooks->define_namespaces)(propdb->db, xi);
628
629 /* get the first property name, beginning the scan */
630 err = (*db_hooks->first_name)(propdb->db, &name);
631 while (!err && name.ns) {
632
633 /*
634 ** We also look for <DAV:getcontenttype> and
635 ** <DAV:getcontentlanguage>. If they are not stored as dead
636 ** properties, then we need to perform a subrequest to get
637 ** their values (if any).
638 */
639 if (*name.ns == 'D' && strcmp(name.ns, "DAV:") == 0
640 && *name.name == 'g') {
641 if (strcmp(name.name, "getcontenttype") == 0) {
643 }
644 else if (strcmp(name.name, "getcontentlanguage") == 0) {
646 }
647 }
648
650 int found;
651
652 if ((err = (*db_hooks->output_value)(propdb->db, &name,
653 xi, &hdr,
654 &found)) != NULL) {
655 /* ### anything better to do? */
656 /* ### probably should enter a 500 error */
657 goto next_key;
658 }
659 /* assert: found == 1 */
660 }
661 else {
662 /* the value was not requested, so just add an empty
663 tag specifying the property name. */
664 dav_output_prop_name(propdb->p, &name, xi, &hdr);
665 }
666
667 next_key:
668 err = (*db_hooks->next_name)(propdb->db, &name);
669 }
670
671 /* all namespaces have been entered into xi. generate them into
672 the output now. */
674
675 } /* propdb->db != NULL */
676
677 /* add namespaces for all the liveprop providers */
679 }
680
681 /* ask the liveprop providers to insert their properties */
682 dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
683
684 /* insert the standard properties */
685 /* ### should be handling the return errors here */
686 (void)dav_insert_coreprop(propdb,
687 DAV_PROPID_CORE_supportedlock, "supportedlock",
689 (void)dav_insert_coreprop(propdb,
690 DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
692
693 /* if we didn't find these, then do the whole subreq thing. */
694 if (!found_contenttype) {
695 /* ### should be handling the return error here */
696 (void)dav_insert_coreprop(propdb,
698 "getcontenttype",
700 }
701 if (!found_contentlang) {
702 /* ### should be handling the return error here */
703 (void)dav_insert_coreprop(propdb,
705 "getcontentlanguage",
707 }
708
709 /* if not just reporting on supported live props,
710 * terminate the result */
712 apr_text_append(propdb->p, &hdr,
713 "</D:prop>" DEBUG_CR
714 "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
715 "</D:propstat>" DEBUG_CR);
716 }
717
718 result.propstats = hdr.first;
719 result.xmlns = hdr_ns.first;
720 return result;
721}
722
724 apr_xml_doc *doc)
725{
726 const dav_hooks_db *db_hooks = propdb->db_hooks;
727 apr_xml_elem *elem = dav_find_child(doc->root, "prop");
729 apr_text_header hdr_bad = { 0 };
730 apr_text_header hdr_ns = { 0 };
731 int have_good = 0;
734 char *marks_liveprop;
736 int xi_filled = 0;
737
738 /* we lose both the document and the element when calling (insert_prop),
739 * make these available in the pool.
740 */
742 if (!element) {
745 }
746 else {
747 memset(element, 0, sizeof(dav_liveprop_elem));
748 }
749 element->doc = doc;
750
751 /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
752 the marks */
753
754 /* we will ALWAYS provide a "good" result, even if it is EMPTY */
755 apr_text_append(propdb->p, &hdr_good,
756 "<D:propstat>" DEBUG_CR
757 "<D:prop>" DEBUG_CR);
758
759 /* ### the marks should be in a buffer! */
760 /* allocate zeroed-memory for the marks. These marks indicate which
761 liveprop namespaces we've generated into the output xmlns buffer */
762
763 /* same for the liveprops */
765
766 xi = dav_xmlns_create(propdb->p);
767
768 for (elem = elem->first_child; elem; elem = elem->next) {
769 dav_elem_private *priv;
770 dav_error *err;
773
774 element->elem = elem;
775
776 /*
777 ** First try live property providers; if they don't handle
778 ** the property, then try looking it up in the propdb.
779 */
780
781 if (elem->priv == NULL) {
782 /* elem->priv outlives propdb->p. Hence use the request pool */
783 elem->priv = apr_pcalloc(propdb->r->pool, sizeof(*priv));
784 }
785 priv = elem->priv;
786
787 /* cache the propid; dav_get_props() could be called many times */
788 if (priv->propid == 0)
789 dav_find_liveprop(propdb, elem);
790
791 if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
792
793 /* insert the property. returns 1 if an insertion was done. */
795 &hdr_good, &inserted)) != NULL) {
796 /* ### need to propagate the error to the caller... */
797 /* ### skip it for now, as if nothing was inserted */
798 }
800 have_good = 1;
801
802 /*
803 ** Add the liveprop's namespace URIs. Note that provider==NULL
804 ** for core properties.
805 */
806 if (priv->provider != NULL) {
807 const char * const * scan_ns_uri;
808
809 for (scan_ns_uri = priv->provider->namespace_uris;
810 *scan_ns_uri != NULL;
811 ++scan_ns_uri) {
812 long ns;
813
815 if (marks_liveprop[ns])
816 continue;
817 marks_liveprop[ns] = 1;
818
819 dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri,
820 &hdr_ns);
821 }
822 }
823
824 /* property added. move on to the next property. */
825 continue;
826 }
827 else if (inserted == DAV_PROP_INSERT_NOTDEF) {
828 /* nothing to do. fall thru to allow property to be handled
829 as a dead property */
830 }
831#if DAV_DEBUG
832 else {
833#if 0
834 /* ### need to change signature to return an error */
835 return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0,
836 0,
837 "INTERNAL DESIGN ERROR: insert_liveprop "
838 "did not insert what was asked for.");
839#endif
840 }
841#endif
842 }
843
844 /* The property wasn't a live property, so look in the dead property
845 database. */
846
847 /* make sure propdb is really open */
848 if (propdb->deferred) {
849 /* ### what to do with db open error? */
850 (void) dav_really_open_db(propdb, 1 /*ro*/);
851 }
852
853 if (elem->ns == APR_XML_NS_NONE)
854 name.ns = "";
855 else
856 name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
857 name.name = elem->name;
858
859 /* only bother to look if a database exists */
860 if (propdb->db != NULL) {
861 int found;
862
863 if ((err = (*db_hooks->output_value)(propdb->db, &name,
864 xi, &hdr_good,
865 &found)) != NULL) {
866 /* ### what to do? continue doesn't seem right... */
867 continue;
868 }
869
870 if (found) {
871 have_good = 1;
872
873 /* if we haven't added the db's namespaces, then do so... */
874 if (!xi_filled) {
875 (void) (*db_hooks->define_namespaces)(propdb->db, xi);
876 xi_filled = 1;
877 }
878 continue;
879 }
880 }
881
882 /* not found as a live OR dead property. add a record to the "bad"
883 propstats */
884
885 /* make sure we've started our "bad" propstat */
886 if (hdr_bad.first == NULL) {
887 apr_text_append(propdb->p, &hdr_bad,
888 "<D:propstat>" DEBUG_CR
889 "<D:prop>" DEBUG_CR);
890 }
891
892 /* output this property's name (into the bad propstats) */
893 dav_output_prop_name(propdb->p, &name, xi, &hdr_bad);
894 }
895
896 apr_text_append(propdb->p, &hdr_good,
897 "</D:prop>" DEBUG_CR
898 "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
899 "</D:propstat>" DEBUG_CR);
900
901 /* default to start with the good */
902 result.propstats = hdr_good.first;
903
904 /* we may not have any "bad" results */
905 if (hdr_bad.first != NULL) {
906 /* "close" the bad propstat */
907 apr_text_append(propdb->p, &hdr_bad,
908 "</D:prop>" DEBUG_CR
909 "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
910 "</D:propstat>" DEBUG_CR);
911
912 /* if there are no good props, then just return the bad */
913 if (!have_good) {
914 result.propstats = hdr_bad.first;
915 }
916 else {
917 /* hook the bad propstat to the end of the good one */
918 hdr_good.last->next = hdr_bad.first;
919 }
920 }
921
922 /* add in all the various namespaces, and return them */
924 result.xmlns = hdr_ns.first;
925
926 return result;
927}
928
930 const char *ns_uri,
931 const char *propname,
932 apr_text_header *body)
933{
934 int propid;
936
937 propid = dav_find_liveprop_provider(propdb, ns_uri, propname, &hooks);
938
939 if (propid != DAV_PROPID_CORE_UNKNOWN) {
940 if (hooks == NULL) {
941 /* this is a "core" property that we define */
943 dav_insert_coreprop(propdb, propid, propname,
945 }
946 else {
947 (*hooks->insert_prop)(propdb->resource, propid,
949 }
950 }
951}
952
961
963{
964 dav_propdb *propdb = ctx->propdb;
965 apr_xml_elem *prop = ctx->prop;
966 dav_elem_private *priv;
967
968 priv = ctx->prop->priv = apr_pcalloc(propdb->p, sizeof(*priv));
969
970 /*
971 ** Check to see if this is a live property, and fill the fields
972 ** in the XML elem, as appropriate.
973 **
974 ** Verify that the property is read/write. If not, then it cannot
975 ** be SET or DELETEd.
976 */
977 if (priv->propid == 0) {
978 dav_find_liveprop(propdb, prop);
979
980 /* it's a liveprop if a provider was found */
981 /* ### actually the "core" props should really be liveprops, but
982 ### there is no "provider" for those and the r/w props are
983 ### treated as dead props anyhow */
984 ctx->is_liveprop = priv->provider != NULL;
985 }
986
987 if (!dav_rw_liveprop(propdb, priv)) {
988 ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT,
990 "Property is read-only.");
991 return;
992 }
993
994 if (ctx->is_liveprop) {
995 int defer_to_dead = 0;
996
997 ctx->err = (*priv->provider->patch_validate)(propdb->resource,
998 prop, ctx->operation,
999 &ctx->liveprop_ctx,
1000 &defer_to_dead);
1001 if (ctx->err != NULL || !defer_to_dead)
1002 return;
1003
1004 /* clear is_liveprop -- act as a dead prop now */
1005 ctx->is_liveprop = 0;
1006 }
1007
1008 /*
1009 ** The property is supposed to be stored into the dead-property
1010 ** database. Make sure the thing is truly open (and writable).
1011 */
1012 if (propdb->deferred
1013 && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
1014 return;
1015 }
1016
1017 /*
1018 ** There should be an open, writable database in here!
1019 **
1020 ** Note: the database would be NULL if it was opened readonly and it
1021 ** did not exist.
1022 */
1023 if (propdb->db == NULL) {
1026 "Attempted to set/remove a property "
1027 "without a valid, open, read/write "
1028 "property database.");
1029 return;
1030 }
1031
1032 if (ctx->operation == DAV_PROP_OP_SET) {
1033 /*
1034 ** Prep the element => propdb namespace index mapping, inserting
1035 ** namespace URIs into the propdb that don't exist.
1036 */
1037 (void) (*propdb->db_hooks->map_namespaces)(propdb->db,
1038 propdb->ns_xlate,
1039 &propdb->mapping);
1040 }
1041 else if (ctx->operation == DAV_PROP_OP_DELETE) {
1042 /*
1043 ** There are no checks to perform here. If a property exists, then
1044 ** we will delete it. If it does not exist, then it does not matter
1045 ** (see S12.13.1).
1046 **
1047 ** Note that if a property does not exist, that does not rule out
1048 ** that a SET will occur during this PROPPATCH (thusly creating it).
1049 */
1050 }
1051}
1052
1054{
1055 dav_propdb *propdb = ctx->propdb;
1056 dav_error *err = NULL;
1057 dav_elem_private *priv = ctx->prop->priv;
1058
1059 ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback));
1060
1061 if (ctx->is_liveprop) {
1062 err = (*priv->provider->patch_exec)(propdb->resource,
1063 ctx->prop, ctx->operation,
1064 ctx->liveprop_ctx,
1065 &ctx->rollback->liveprop);
1066 }
1067 else {
1069
1070 if (ctx->prop->ns == APR_XML_NS_NONE)
1071 name.ns = "";
1072 else
1073 name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns);
1074 name.name = ctx->prop->name;
1075
1076 /* save the old value so that we can do a rollback. */
1077 if ((err = (*propdb->db_hooks
1078 ->get_rollback)(propdb->db, &name,
1079 &ctx->rollback->deadprop)) != NULL)
1080 goto error;
1081
1082 if (ctx->operation == DAV_PROP_OP_SET) {
1083
1084 /* Note: propdb->mapping was set in dav_prop_validate() */
1085 err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
1086 propdb->mapping);
1087
1088 /*
1089 ** If an error occurred, then assume that we didn't change the
1090 ** value. Remove the rollback item so that we don't try to set
1091 ** its value during the rollback.
1092 */
1093 /* ### euh... where is the removal? */
1094 }
1095 else if (ctx->operation == DAV_PROP_OP_DELETE) {
1096
1097 /*
1098 ** Delete the property. Ignore errors -- the property is there, or
1099 ** we are deleting it for a second time.
1100 **
1101 ** http://tools.ietf.org/html/rfc4918#section-14.23 says
1102 ** "Specifying the removal of a property that does not exist is
1103 ** not an error"
1104 */
1105 /* ### but what about other errors? */
1106 (void) (*propdb->db_hooks->remove)(propdb->db, &name);
1107 }
1108 }
1109
1110 error:
1111 /* push a more specific error here */
1112 if (err != NULL) {
1113 /*
1114 ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
1115 ** any errors at this point.
1116 */
1119 "Could not execute PROPPATCH.", err);
1120 }
1121}
1122
1124{
1125 dav_elem_private *priv = ctx->prop->priv;
1126
1127 /*
1128 ** Note that a commit implies ctx->err is NULL. The caller should assume
1129 ** a status of HTTP_OK for this case.
1130 */
1131
1132 if (ctx->is_liveprop) {
1133 (*priv->provider->patch_commit)(ctx->propdb->resource,
1134 ctx->operation,
1135 ctx->liveprop_ctx,
1136 ctx->rollback->liveprop);
1137 }
1138}
1139
1141{
1142 dav_error *err = NULL;
1143 dav_elem_private *priv = ctx->prop->priv;
1144
1145 /* do nothing if there is no rollback information. */
1146 if (ctx->rollback == NULL)
1147 return;
1148
1149 /*
1150 ** ### if we have an error, and a rollback occurs, then the namespace
1151 ** ### mods should not happen at all. Basically, the namespace management
1152 ** ### is simply a bitch.
1153 */
1154
1155 if (ctx->is_liveprop) {
1156 err = (*priv->provider->patch_rollback)(ctx->propdb->resource,
1157 ctx->operation,
1158 ctx->liveprop_ctx,
1159 ctx->rollback->liveprop);
1160 }
1161 else {
1162 err = (*ctx->propdb->db_hooks
1163 ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop);
1164 }
1165
1166 if (err != NULL) {
1167 if (ctx->err == NULL)
1168 ctx->err = err;
1169 else {
1170 dav_error *scan = err;
1171
1172 /* hook previous errors at the end of the rollback error */
1173 while (scan->prev != NULL)
1174 scan = scan->prev;
1175 scan->prev = ctx->err;
1176 ctx->err = err;
1177 }
1178 }
1179}
APR Strings library.
APR Standard Headers Support.
return found
Definition core.c:2840
request_rec * r
request_rec * ap_sub_req_lookup_uri(const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2297
void ap_destroy_sub_req(request_rec *r)
Definition request.c:2547
apr_brigade_flush void * ctx
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
const char apr_ssize_t int flags
Definition apr_encode.h:168
void ** resource
#define APR_XML_NS_NONE
Definition apr_xml.h:134
apr_text_header * hdr
Definition apr_xml.h:77
#define APR_XML_NS_DAV_ID
Definition apr_xml.h:133
#define APR_XML_GET_URI_ITEM(ary, i)
Definition apr_xml.h:339
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_CONFLICT
Definition httpd.h:517
#define DAV_GET_HOOKS_PROPDB(r)
Definition mod_dav.h:807
dav_get_props_result dav_get_allprops(dav_propdb *propdb, dav_prop_insert what)
Definition props.c:595
const char * dav_lock_get_activelock(request_rec *r, dav_lock *locks, dav_buffer *pbuf)
Definition util_lock.c:42
#define DAV_ERR_PROP_READONLY
Definition mod_dav.h:218
void dav_prop_validate(dav_prop_ctx *ctx)
Definition props.c:962
#define DAV_ERR_PROP_EXEC
Definition mod_dav.h:223
dav_prop_insert
Definition mod_dav.h:516
#define DEBUG_CR
Definition mod_dav.h:65
void dav_close_propdb(dav_propdb *propdb)
Definition props.c:583
dav_error * dav_lock_query(dav_lockdb *lockdb, const dav_resource *resource, dav_lock **locks)
Definition util_lock.c:369
void dav_get_liveprop_supported(dav_propdb *propdb, const char *ns_uri, const char *propname, apr_text_header *body)
Definition props.c:929
dav_xmlns_info * dav_xmlns_create(apr_pool_t *pool)
Definition util.c:462
void dav_xmlns_generate(dav_xmlns_info *xi, apr_text_header *phdr)
Definition util.c:510
#define DAV_DECLARE_NONSTD(type)
Definition mod_dav.h:86
long dav_get_liveprop_ns_index(const char *uri)
Definition liveprop.c:56
dav_get_props_result dav_get_props(dav_propdb *propdb, apr_xml_doc *doc)
Definition props.c:723
void dav_prop_exec(dav_prop_ctx *ctx)
Definition props.c:1053
void dav_add_all_liveprop_xmlns(apr_pool_t *p, apr_text_header *phdr)
Definition liveprop.c:66
dav_error * dav_popen_propdb(apr_pool_t *p, request_rec *r, dav_lockdb *lockdb, const dav_resource *resource, int flags, apr_array_header_t *ns_xlate, dav_propdb **p_propdb)
Definition props.c:540
void dav_prop_commit(dav_prop_ctx *ctx)
Definition props.c:1123
apr_xml_elem * dav_find_child(const apr_xml_elem *elem, const char *tagname)
Definition util.c:359
dav_error * dav_push_error(apr_pool_t *p, int status, int error_id, const char *desc, dav_error *prev)
Definition util.c:66
#define DAV_ERR_PROP_NO_DATABASE
Definition mod_dav.h:219
int dav_run_find_liveprop(const dav_resource *resource, const char *ns_uri, const char *name, const dav_hooks_liveprop **hooks)
Definition mod_dav.c:5247
#define DAV_PROPDB_DISABLE_LOCKDISCOVERY
Definition mod_dav.h:1686
#define DAV_DECLARE(type)
Definition mod_dav.h:85
void dav_run_insert_all_liveprops(request_rec *r, const dav_resource *resource, dav_prop_insert what, apr_text_header *phdr)
Definition mod_dav.c:5252
#define DAV_PROPID_CORE
Definition mod_dav.h:1182
dav_liveprop_elem * dav_get_liveprop_element(const dav_resource *resource)
Definition props.c:953
long dav_get_liveprop_ns_count(void)
Definition liveprop.c:61
const char * dav_xmlns_add_uri(dav_xmlns_info *xi, const char *uri)
Definition util.c:484
void dav_prop_rollback(dav_prop_ctx *ctx)
Definition props.c:1140
dav_error * dav_open_propdb(request_rec *r, dav_lockdb *lockdb, const dav_resource *resource, int flags, apr_array_header_t *ns_xlate, dav_propdb **p_propdb)
Definition props.c:530
dav_error * dav_new_error(apr_pool_t *p, int status, int error_id, apr_status_t aprerr, const char *desc)
Definition util.c:36
#define DAV_ERR_PROP_OPENING
Definition mod_dav.h:222
struct dav_liveprop_rollback dav_liveprop_rollback
Definition mod_dav.h:887
@ DAV_PROP_INSERT_VALUE
Definition mod_dav.h:527
@ DAV_PROP_INSERT_NOTDEF
Definition mod_dav.h:517
@ DAV_PROP_INSERT_SUPPORTED
Definition mod_dav.h:529
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
const char * value
Definition apr_env.h:51
apr_array_header_t ** result
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
apr_int32_t apr_int32_t apr_int32_t err
Apache Logging library.
Apache Request library.
apr_pool_t * p
Definition md_event.c:32
DAV extension module for Apache 2.0.*.
#define DAV_PROP_OP_DELETE
Definition mod_dav.h:1766
#define DAV_PROP_OP_SET
Definition mod_dav.h:1765
static const char *const hooks[]
return NULL
Definition mod_so.c:359
static void dav_find_liveprop(dav_propdb *propdb, apr_xml_elem *elem)
Definition props.c:264
#define DAV_PROP_ELEMENT
Definition props.c:170
static void dav_do_prop_subreq(dav_propdb *propdb)
Definition props.c:326
static void dav_output_prop_name(apr_pool_t *pool, const dav_prop_name *name, dav_xmlns_info *xi, apr_text_header *phdr)
Definition props.c:477
static int dav_find_liveprop_provider(dav_propdb *propdb, const char *ns_uri, const char *propname, const dav_hooks_liveprop **provider)
Definition props.c:229
static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, long ns, const char *ns_uri, apr_text_header *phdr)
Definition props.c:495
#define DAV_DISABLE_WRITABLE_PROPS
Definition props.c:166
static dav_error * dav_insert_liveprop(dav_propdb *propdb, const apr_xml_elem *elem, dav_prop_insert what, apr_text_header *phdr, dav_prop_insert *inserted)
Definition props.c:454
static dav_error * dav_insert_coreprop(dav_propdb *propdb, int propid, const char *name, dav_prop_insert what, apr_text_header *phdr, dav_prop_insert *inserted)
Definition props.c:338
static int dav_rw_liveprop(dav_propdb *propdb, dav_elem_private *priv)
Definition props.c:288
@ DAV_PROPID_CORE_lockdiscovery
Definition props.c:211
@ DAV_PROPID_CORE_getcontenttype
Definition props.c:209
@ DAV_PROPID_CORE_getcontentlanguage
Definition props.c:210
@ DAV_PROPID_CORE_UNKNOWN
Definition props.c:214
@ DAV_PROPID_CORE_supportedlock
Definition props.c:212
static dav_error * dav_really_open_db(dav_propdb *propdb, int ro)
Definition props.c:504
static const char *const dav_core_props[]
Definition props.c:199
char * name
apr_text * first
Definition apr_xml.h:66
apr_xml_elem * root
Definition apr_xml.h:202
char * buf
Definition mod_dav.h:448
Definition dbm.c:52
const dav_hooks_liveprop * provider
Definition mod_dav.h:2613
struct dav_error * prev
Definition mod_dav.h:131
int(* is_writable)(const dav_resource *resource, int propid)
Definition mod_dav.h:934
dav_error *(* patch_validate)(const dav_resource *resource, const apr_xml_elem *elem, int operation, void **context, int *defer_to_dead)
Definition mod_dav.h:976
const char *const * namespace_uris
Definition mod_dav.h:944
dav_error *(* patch_exec)(const dav_resource *resource, const apr_xml_elem *elem, int operation, void *context, dav_liveprop_rollback **rollback_ctx)
Definition mod_dav.h:983
void(* patch_commit)(const dav_resource *resource, int operation, void *context, dav_liveprop_rollback *rollback_ctx)
Definition mod_dav.h:990
dav_error *(* patch_rollback)(const dav_resource *resource, int operation, void *context, dav_liveprop_rollback *rollback_ctx)
Definition mod_dav.h:996
dav_prop_insert(* insert_prop)(const dav_resource *resource, int propid, dav_prop_insert what, apr_text_header *phdr)
Definition mod_dav.h:922
const char *(* get_supportedlock)(const dav_resource *resource)
Definition mod_dav.h:1472
dav_error *(* store)(dav_db *db, const dav_prop_name *name, const apr_xml_elem *elem, dav_namespace_map *mapping)
Definition mod_dav.h:1268
dav_error *(* output_value)(dav_db *db, const dav_prop_name *name, dav_xmlns_info *xi, apr_text_header *phdr, int *found)
Definition mod_dav.h:1237
dav_error *(* get_rollback)(dav_db *db, const dav_prop_name *name, dav_deadprop_rollback **prollback)
Definition mod_dav.h:1295
void(* close)(dav_db *db)
Definition mod_dav.h:1209
dav_error *(* first_name)(dav_db *db, dav_prop_name *pname)
Definition mod_dav.h:1285
dav_error *(* next_name)(dav_db *db, dav_prop_name *pname)
Definition mod_dav.h:1286
dav_error *(* map_namespaces)(dav_db *db, const apr_array_header_t *namespaces, dav_namespace_map **mapping)
Definition mod_dav.h:1256
dav_error *(* define_namespaces)(dav_db *db, dav_xmlns_info *xi)
Definition mod_dav.h:1223
dav_error *(* open)(apr_pool_t *p, const dav_resource *resource, int ro, dav_db **pdb)
Definition mod_dav.h:1207
dav_error *(* remove)(dav_db *db, const dav_prop_name *name)
Definition mod_dav.h:1273
const dav_hooks_locks * hooks
Definition mod_dav.h:1334
const char * ns
Definition mod_dav.h:1200
request_rec * subreq
Definition props.c:191
const dav_hooks_db * db_hooks
Definition props.c:194
dav_namespace_map * mapping
Definition props.c:182
apr_array_header_t * ns_xlate
Definition props.c:181
dav_db * db
Definition props.c:179
int deferred
Definition props.c:178
apr_pool_t * p
Definition props.c:173
request_rec * r
Definition props.c:174
const dav_resource * resource
Definition props.c:176
dav_buffer wb_lock
Definition props.c:186
int flags
Definition props.c:188
dav_lockdb * lockdb
Definition props.c:184
const char * uri
Definition mod_dav.h:408
apr_pool_t * pool
Definition mod_dav.h:419
dav_liveprop_rollback * liveprop
Definition props.c:224
dav_deadprop_rollback * deadprop
Definition props.c:223
const XML_Char * name
Definition xmlparse.c:282
A structure that represents the current request.
Definition httpd.h:845
const char * content_type
Definition httpd.h:992
apr_pool_t * pool
Definition httpd.h:847
apr_table_t * headers_out
Definition httpd.h:978
#define ns(x)
Definition xmltok.c:1644