1 /*
2 * deadprops.c: mod_dav_svn provider functions for "dead properties"
3 * (properties implemented by Subversion or its users,
4 * not as part of the WebDAV specification).
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 */
25
26 #include <apr_hash.h>
27
28 #include <httpd.h>
29 #include <mod_dav.h>
30
31 #include "svn_hash.h"
32 #include "svn_xml.h"
33 #include "svn_pools.h"
34 #include "svn_dav.h"
35 #include "svn_base64.h"
36 #include "svn_props.h"
37 #include "private/svn_log.h"
38
39 #include "dav_svn.h"
40
41
42 struct dav_db {
43 const dav_resource *resource;
44 apr_pool_t *p;
45
46 /* the resource's properties that we are sequencing over */
47 apr_hash_t *props;
48 apr_hash_index_t *hi;
49
50 /* used for constructing repos-local names for properties */
51 svn_stringbuf_t *work;
52
53 /* passed to svn_repos_ funcs that fetch revprops. */
54 svn_repos_authz_func_t authz_read_func;
55 void *authz_read_baton;
56 };
57
58
59 struct dav_deadprop_rollback {
60 int dummy;
61 };
62
63
64 /* retrieve the "right" string to use as a repos path */
65 static const char *
get_repos_path(struct dav_resource_private * info)66 get_repos_path(struct dav_resource_private *info)
67 {
68 return info->repos_path;
69 }
70
71
72 /* construct the repos-local name for the given DAV property name */
73 static void
get_repos_propname(dav_db * db,const dav_prop_name * name,const char ** repos_propname)74 get_repos_propname(dav_db *db,
75 const dav_prop_name *name,
76 const char **repos_propname)
77 {
78 if (strcmp(name->ns, SVN_DAV_PROP_NS_SVN) == 0)
79 {
80 /* recombine the namespace ("svn:") and the name. */
81 svn_stringbuf_set(db->work, SVN_PROP_PREFIX);
82 svn_stringbuf_appendcstr(db->work, name->name);
83 *repos_propname = db->work->data;
84 }
85 else if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
86 {
87 /* the name of a custom prop is just the name -- no ns URI */
88 *repos_propname = name->name;
89 }
90 else
91 {
92 *repos_propname = NULL;
93 }
94 }
95
96
97 static dav_error *
get_value(dav_db * db,const dav_prop_name * name,svn_string_t ** pvalue)98 get_value(dav_db *db, const dav_prop_name *name, svn_string_t **pvalue)
99 {
100 const char *propname;
101 svn_error_t *serr;
102
103 /* get the repos-local name */
104 get_repos_propname(db, name, &propname);
105
106 if (propname == NULL)
107 {
108 /* we know these are not present. */
109 *pvalue = NULL;
110 return NULL;
111 }
112
113 /* If db->props exists, then use it to obtain property value. */
114 if (db->props)
115 {
116 *pvalue = svn_hash_gets(db->props, propname);
117 return NULL;
118 }
119
120 /* We've got three different types of properties (node, txn, and
121 revision), and we've got two different protocol versions to deal
122 with. Let's try to make some sense of this, shall we?
123
124 HTTP v1:
125 working baseline ('wbl') resource -> txn prop change
126 non-working, baselined resource ('bln') -> rev prop change [*]
127 working, non-baselined resource ('wrk') -> node prop change
128
129 HTTP v2:
130 transaction resource ('txn') -> txn prop change
131 revision resource ('rev') -> rev prop change
132 transaction root resource ('txr') -> node prop change
133
134 [*] This is a violation of the DeltaV spec (### see issue #916).
135
136 */
137
138 if (db->resource->baselined)
139 {
140 if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
141 serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn,
142 propname, db->p);
143 else
144 serr = svn_repos_fs_revision_prop(pvalue,
145 db->resource->info->repos->repos,
146 db->resource->info->root.rev,
147 propname, db->authz_read_func,
148 db->authz_read_baton, db->p);
149 }
150 else if (db->resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION)
151 {
152 serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn,
153 propname, db->p);
154 }
155 else
156 {
157 serr = svn_fs_node_prop(pvalue, db->resource->info->root.root,
158 get_repos_path(db->resource->info),
159 propname, db->p);
160 }
161
162 if (serr != NULL)
163 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
164 "could not fetch a property",
165 db->resource->pool);
166
167 return NULL;
168 }
169
170
171 static svn_error_t *
change_txn_prop(svn_fs_txn_t * txn,const char * propname,const svn_string_t * value,apr_pool_t * scratch_pool)172 change_txn_prop(svn_fs_txn_t *txn,
173 const char *propname,
174 const svn_string_t *value,
175 apr_pool_t *scratch_pool)
176 {
177 if (strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0)
178 return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
179 "Attempted to modify 'svn:author' property "
180 "on a transaction");
181
182 SVN_ERR(svn_repos_fs_change_txn_prop(txn, propname, value, scratch_pool));
183
184 return SVN_NO_ERROR;
185 }
186
187
188 static dav_error *
save_value(dav_db * db,const dav_prop_name * name,const svn_string_t * const * old_value_p,const svn_string_t * value)189 save_value(dav_db *db, const dav_prop_name *name,
190 const svn_string_t *const *old_value_p,
191 const svn_string_t *value)
192 {
193 const char *propname;
194 svn_error_t *serr;
195 const dav_resource *resource = db->resource;
196 apr_pool_t *subpool;
197
198 /* get the repos-local name */
199 get_repos_propname(db, name, &propname);
200
201 if (propname == NULL)
202 {
203 if (resource->info->repos->autoversioning)
204 /* ignore the unknown namespace of the incoming prop. */
205 propname = name->name;
206 else
207 return dav_svn__new_error(db->p, HTTP_CONFLICT, 0, 0,
208 "Properties may only be defined in the "
209 SVN_DAV_PROP_NS_SVN " and "
210 SVN_DAV_PROP_NS_CUSTOM " namespaces.");
211 }
212
213 /* We've got three different types of properties (node, txn, and
214 revision), and we've got two different protocol versions to deal
215 with. Let's try to make some sense of this, shall we?
216
217 HTTP v1:
218 working baseline ('wbl') resource -> txn prop change
219 non-working, baselined resource ('bln') -> rev prop change [*]
220 working, non-baselined resource ('wrk') -> node prop change
221
222 HTTP v2:
223 transaction resource ('txn') -> txn prop change
224 revision resource ('rev') -> rev prop change
225 transaction root resource ('txr') -> node prop change
226
227 [*] This is a violation of the DeltaV spec (### see issue #916).
228
229 */
230
231 /* A subpool to cope with mod_dav making multiple calls, e.g. during
232 PROPPATCH with multiple values. */
233 subpool = svn_pool_create(resource->pool);
234 if (resource->baselined)
235 {
236 if (resource->working)
237 {
238 serr = change_txn_prop(resource->info->root.txn, propname,
239 value, subpool);
240 }
241 else
242 {
243 serr = svn_repos_fs_change_rev_prop4(resource->info->repos->repos,
244 resource->info->root.rev,
245 resource->info->repos->username,
246 propname, old_value_p, value,
247 TRUE, TRUE,
248 db->authz_read_func,
249 db->authz_read_baton,
250 subpool);
251
252 /* Prepare any hook failure message to get sent over the wire */
253 if (serr)
254 {
255 svn_error_t *purged_serr = svn_error_purge_tracing(serr);
256 if (purged_serr->apr_err == SVN_ERR_REPOS_HOOK_FAILURE)
257 purged_serr->message = apr_xml_quote_string
258 (purged_serr->pool,
259 purged_serr->message, 1);
260
261 /* mod_dav doesn't handle the returned error very well, it
262 generates its own generic error that will be returned to
263 the client. Cache the detailed error here so that it can
264 be returned a second time when the rollback mechanism
265 triggers. */
266 resource->info->revprop_error = svn_error_dup(purged_serr);
267 }
268
269 /* Tell the logging subsystem about the revprop change. */
270 dav_svn__operational_log(resource->info,
271 svn_log__change_rev_prop(
272 resource->info->root.rev,
273 propname, subpool));
274 }
275 }
276 else if (resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION)
277 {
278 serr = change_txn_prop(resource->info->root.txn, propname,
279 value, subpool);
280 }
281 else
282 {
283 serr = svn_repos_fs_change_node_prop(resource->info->root.root,
284 get_repos_path(resource->info),
285 propname, value, subpool);
286 }
287 svn_pool_destroy(subpool);
288
289 if (serr != NULL)
290 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
291 NULL, resource->pool);
292
293 /* a change to the props was made; make sure our cached copy is gone */
294 db->props = NULL;
295
296 return NULL;
297 }
298
299
300 static dav_error *
db_open(apr_pool_t * p,const dav_resource * resource,int ro,dav_db ** pdb)301 db_open(apr_pool_t *p,
302 const dav_resource *resource,
303 int ro,
304 dav_db **pdb)
305 {
306 dav_db *db;
307 dav_svn__authz_read_baton *arb;
308
309 /* Some resource types do not have deadprop databases.
310 Specifically: REGULAR, VERSION, WORKING, and our custom
311 transaction and transaction root resources have them. (SVN does
312 not have WORKSPACE resources, and isn't covered here.) */
313
314 if (resource->type == DAV_RESOURCE_TYPE_HISTORY
315 || resource->type == DAV_RESOURCE_TYPE_ACTIVITY
316 || (resource->type == DAV_RESOURCE_TYPE_PRIVATE
317 && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION
318 && resource->info->restype != DAV_SVN_RESTYPE_TXNROOT_COLLECTION))
319 {
320 *pdb = NULL;
321 return NULL;
322 }
323
324 /* If the DB is being opened R/W, and this isn't a working resource, then
325 we have a problem! */
326 if ((! ro)
327 && resource->type != DAV_RESOURCE_TYPE_WORKING
328 && resource->type != DAV_RESOURCE_TYPE_PRIVATE
329 && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION)
330 {
331 /* ### Exception: in violation of deltaV, we *are* allowing a
332 baseline resource to receive a proppatch, as a way of
333 changing unversioned rev props. Remove this someday: see IZ #916. */
334 if (! (resource->baselined
335 && resource->type == DAV_RESOURCE_TYPE_VERSION))
336 return dav_svn__new_error(p, HTTP_CONFLICT, 0, 0,
337 "Properties may only be changed on working "
338 "resources.");
339 }
340
341 db = apr_pcalloc(p, sizeof(*db));
342
343 db->resource = resource;
344 db->p = svn_pool_create(p);
345
346 /* ### temp hack */
347 db->work = svn_stringbuf_create_empty(db->p);
348
349 /* make our path-based authz callback available to svn_repos_* funcs. */
350 arb = apr_pcalloc(p, sizeof(*arb));
351 arb->r = resource->info->r;
352 arb->repos = resource->info->repos;
353 db->authz_read_baton = arb;
354 db->authz_read_func = dav_svn__authz_read_func(arb);
355
356 /* ### use RO and node's mutable status to look for an error? */
357
358 *pdb = db;
359
360 return NULL;
361 }
362
363
364 static void
db_close(dav_db * db)365 db_close(dav_db *db)
366 {
367 svn_pool_destroy(db->p);
368 }
369
370
371 static dav_error *
db_define_namespaces(dav_db * db,dav_xmlns_info * xi)372 db_define_namespaces(dav_db *db, dav_xmlns_info *xi)
373 {
374 dav_xmlns_add(xi, "S", SVN_DAV_PROP_NS_SVN);
375 dav_xmlns_add(xi, "C", SVN_DAV_PROP_NS_CUSTOM);
376 dav_xmlns_add(xi, "V", SVN_DAV_PROP_NS_DAV);
377
378 /* ### we don't have any other possible namespaces right now. */
379
380 return NULL;
381 }
382
383 static dav_error *
db_output_value(dav_db * db,const dav_prop_name * name,dav_xmlns_info * xi,apr_text_header * phdr,int * found)384 db_output_value(dav_db *db,
385 const dav_prop_name *name,
386 dav_xmlns_info *xi,
387 apr_text_header *phdr,
388 int *found)
389 {
390 const char *prefix;
391 const char *s;
392 svn_string_t *propval;
393 dav_error *err;
394 apr_pool_t *pool = db->resource->pool;
395
396 if ((err = get_value(db, name, &propval)) != NULL)
397 return err;
398
399 /* return whether the prop was found, then punt or handle it. */
400 *found = (propval != NULL);
401 if (propval == NULL)
402 return NULL;
403
404 if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
405 prefix = "C:";
406 else
407 prefix = "S:";
408
409 if (propval->len == 0)
410 {
411 /* empty value. add an empty elem. */
412 s = apr_psprintf(pool, "<%s%s/>" DEBUG_CR, prefix, name->name);
413 apr_text_append(pool, phdr, s);
414 }
415 else
416 {
417 /* add <prefix:name [V:encoding="base64"]>value</prefix:name> */
418 const char *xml_safe;
419 const char *encoding = "";
420
421 /* Ensure XML-safety of our property values before sending them
422 across the wire. */
423 if (! svn_xml_is_xml_safe(propval->data, propval->len))
424 {
425 const svn_string_t *enc_propval
426 = svn_base64_encode_string2(propval, TRUE, pool);
427 xml_safe = enc_propval->data;
428 encoding = " V:encoding=\"base64\"";
429 }
430 else
431 {
432 svn_stringbuf_t *xmlval = NULL;
433 svn_xml_escape_cdata_string(&xmlval, propval, pool);
434 xml_safe = xmlval->data;
435 }
436
437 s = apr_psprintf(pool, "<%s%s%s>", prefix, name->name, encoding);
438 apr_text_append(pool, phdr, s);
439
440 /* the value is in our pool which means it has the right lifetime. */
441 /* ### at least, per the current mod_dav architecture/API */
442 apr_text_append(pool, phdr, xml_safe);
443
444 s = apr_psprintf(pool, "</%s%s>" DEBUG_CR, prefix, name->name);
445 apr_text_append(pool, phdr, s);
446 }
447
448 return NULL;
449 }
450
451
452 static dav_error *
db_map_namespaces(dav_db * db,const apr_array_header_t * namespaces,dav_namespace_map ** mapping)453 db_map_namespaces(dav_db *db,
454 const apr_array_header_t *namespaces,
455 dav_namespace_map **mapping)
456 {
457 /* we don't need a namespace mapping right now. nothing to do */
458 return NULL;
459 }
460
461
462 static dav_error *
decode_property_value(const svn_string_t ** out_propval_p,svn_boolean_t * absent,const svn_string_t * maybe_encoded_propval,const apr_xml_elem * elem,apr_pool_t * pool)463 decode_property_value(const svn_string_t **out_propval_p,
464 svn_boolean_t *absent,
465 const svn_string_t *maybe_encoded_propval,
466 const apr_xml_elem *elem,
467 apr_pool_t *pool)
468 {
469 apr_xml_attr *attr = elem->attr;
470
471 /* Default: no "encoding" attribute. */
472 *absent = FALSE;
473 *out_propval_p = maybe_encoded_propval;
474
475 /* Check for special encodings of the property value. */
476 while (attr)
477 {
478 if (strcmp(attr->name, "encoding") == 0) /* ### namespace check? */
479 {
480 const char *enc_type = attr->value;
481
482 /* Handle known encodings here. */
483 if (enc_type && (strcmp(enc_type, "base64") == 0))
484 *out_propval_p = svn_base64_decode_string(maybe_encoded_propval,
485 pool);
486 else
487 return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
488 "Unknown property encoding");
489 break;
490 }
491
492 if (strcmp(attr->name, SVN_DAV__OLD_VALUE__ABSENT) == 0)
493 {
494 /* ### parse attr->value */
495 *absent = TRUE;
496 *out_propval_p = NULL;
497 }
498
499 /* Next attribute, please. */
500 attr = attr->next;
501 }
502
503 return NULL;
504 }
505
506 static dav_error *
db_store(dav_db * db,const dav_prop_name * name,const apr_xml_elem * elem,dav_namespace_map * mapping)507 db_store(dav_db *db,
508 const dav_prop_name *name,
509 const apr_xml_elem *elem,
510 dav_namespace_map *mapping)
511 {
512 const svn_string_t *const *old_propval_p;
513 const svn_string_t *old_propval;
514 const svn_string_t *propval;
515 svn_boolean_t absent;
516 apr_pool_t *pool = db->p;
517 dav_error *derr;
518
519 /* SVN sends property values as a big blob of bytes. Thus, there should be
520 no child elements of the property-name element. That also means that
521 the entire contents of the blob is located in elem->first_cdata. The
522 dav_xml_get_cdata() will figure it all out for us, but (normally) it
523 should be awfully fast and not need to copy any data. */
524
525 propval = svn_string_create
526 (dav_xml_get_cdata(elem, pool, 0 /* strip_white */), pool);
527
528 derr = decode_property_value(&propval, &absent, propval, elem, pool);
529 if (derr)
530 return derr;
531
532 if (absent && ! elem->first_child)
533 /* ### better error check */
534 return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
535 apr_psprintf(pool,
536 "'%s' cannot be specified on the "
537 "value without specifying an "
538 "expectation",
539 SVN_DAV__OLD_VALUE__ABSENT));
540
541 /* ### namespace check? */
542 if (elem->first_child && !strcmp(elem->first_child->name, SVN_DAV__OLD_VALUE))
543 {
544 /* Parse OLD_PROPVAL. */
545 old_propval = svn_string_create(dav_xml_get_cdata(elem->first_child, pool,
546 0 /* strip_white */),
547 pool);
548 derr = decode_property_value(&old_propval, &absent,
549 old_propval, elem->first_child, pool);
550 if (derr)
551 return derr;
552
553 old_propval_p = (const svn_string_t *const *) &old_propval;
554 }
555 else
556 old_propval_p = NULL;
557
558
559 return save_value(db, name, old_propval_p, propval);
560 }
561
562
563 static dav_error *
db_remove(dav_db * db,const dav_prop_name * name)564 db_remove(dav_db *db, const dav_prop_name *name)
565 {
566 svn_error_t *serr;
567 const char *propname;
568 apr_pool_t *subpool;
569
570 /* get the repos-local name */
571 get_repos_propname(db, name, &propname);
572
573 /* ### non-svn props aren't in our repos, so punt for now */
574 if (propname == NULL)
575 return NULL;
576
577 /* A subpool to cope with mod_dav making multiple calls, e.g. during
578 PROPPATCH with multiple values. */
579 subpool = svn_pool_create(db->resource->pool);
580
581 /* Working Baseline or Working (Version) Resource */
582 if (db->resource->baselined)
583 if (db->resource->working)
584 serr = change_txn_prop(db->resource->info->root.txn, propname,
585 NULL, subpool);
586 else
587 /* ### VIOLATING deltaV: you can't proppatch a baseline, it's
588 not a working resource! But this is how we currently
589 (hackily) allow the svn client to change unversioned rev
590 props. See issue #916. */
591 serr = svn_repos_fs_change_rev_prop4(db->resource->info->repos->repos,
592 db->resource->info->root.rev,
593 db->resource->info->repos->username,
594 propname, NULL, NULL, TRUE, TRUE,
595 db->authz_read_func,
596 db->authz_read_baton,
597 subpool);
598 else
599 serr = svn_repos_fs_change_node_prop(db->resource->info->root.root,
600 get_repos_path(db->resource->info),
601 propname, NULL, subpool);
602 svn_pool_destroy(subpool);
603 if (serr != NULL)
604 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
605 "could not remove a property",
606 db->resource->pool);
607
608 /* a change to the props was made; make sure our cached copy is gone */
609 db->props = NULL;
610
611 return NULL;
612 }
613
614
615 static int
db_exists(dav_db * db,const dav_prop_name * name)616 db_exists(dav_db *db, const dav_prop_name *name)
617 {
618 const char *propname;
619 svn_string_t *propval;
620 svn_error_t *serr;
621 int retval;
622
623 /* get the repos-local name */
624 get_repos_propname(db, name, &propname);
625
626 /* ### non-svn props aren't in our repos */
627 if (propname == NULL)
628 return 0;
629
630 /* Working Baseline, Baseline, or (Working) Version resource */
631 if (db->resource->baselined)
632 if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
633 serr = svn_fs_txn_prop(&propval, db->resource->info->root.txn,
634 propname, db->p);
635 else
636 serr = svn_repos_fs_revision_prop(&propval,
637 db->resource->info->repos->repos,
638 db->resource->info->root.rev,
639 propname,
640 db->authz_read_func,
641 db->authz_read_baton, db->p);
642 else
643 serr = svn_fs_node_prop(&propval, db->resource->info->root.root,
644 get_repos_path(db->resource->info),
645 propname, db->p);
646
647 /* ### try and dispose of the value? */
648
649 retval = (serr == NULL && propval != NULL);
650 svn_error_clear(serr);
651 return retval;
652 }
653
get_name(dav_db * db,dav_prop_name * pname)654 static void get_name(dav_db *db, dav_prop_name *pname)
655 {
656 if (db->hi == NULL)
657 {
658 pname->ns = pname->name = NULL;
659 }
660 else
661 {
662 const char *name = apr_hash_this_key(db->hi);
663
664 #define PREFIX_LEN (sizeof(SVN_PROP_PREFIX) - 1)
665 if (strncmp(name, SVN_PROP_PREFIX, PREFIX_LEN) == 0)
666 #undef PREFIX_LEN
667 {
668 pname->ns = SVN_DAV_PROP_NS_SVN;
669 pname->name = name + 4;
670 }
671 else
672 {
673 pname->ns = SVN_DAV_PROP_NS_CUSTOM;
674 pname->name = name;
675 }
676 }
677 }
678
679
680 static dav_error *
db_first_name(dav_db * db,dav_prop_name * pname)681 db_first_name(dav_db *db, dav_prop_name *pname)
682 {
683 /* for operational logging */
684 const char *action = NULL;
685
686 /* if we don't have a copy of the properties, then get one */
687 if (db->props == NULL)
688 {
689 svn_error_t *serr;
690
691 /* Working Baseline, Baseline, or (Working) Version resource */
692 if (db->resource->baselined)
693 {
694 if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
695 serr = svn_fs_txn_proplist(&db->props,
696 db->resource->info->root.txn,
697 db->p);
698 else
699 {
700 action = svn_log__rev_proplist(db->resource->info->root.rev,
701 db->resource->pool);
702 serr = svn_repos_fs_revision_proplist
703 (&db->props,
704 db->resource->info->repos->repos,
705 db->resource->info->root.rev,
706 db->authz_read_func,
707 db->authz_read_baton,
708 db->p);
709 }
710 }
711 else
712 {
713 serr = svn_fs_node_proplist(&db->props,
714 db->resource->info->root.root,
715 get_repos_path(db->resource->info),
716 db->p);
717
718 if (! serr)
719 {
720 if (db->resource->collection)
721 action = svn_log__get_dir(db->resource->info->repos_path,
722 db->resource->info->root.rev,
723 FALSE, TRUE, 0, db->resource->pool);
724 else
725 action = svn_log__get_file(db->resource->info->repos_path,
726 db->resource->info->root.rev,
727 FALSE, TRUE, db->resource->pool);
728 }
729 }
730 if (serr != NULL)
731 return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
732 "could not begin sequencing through "
733 "properties",
734 db->resource->pool);
735 }
736
737 /* begin the iteration over the hash */
738 db->hi = apr_hash_first(db->p, db->props);
739
740 /* fetch the first key */
741 get_name(db, pname);
742
743 /* If we have a high-level action to log, do so. */
744 if (action != NULL)
745 dav_svn__operational_log(db->resource->info, action);
746
747 return NULL;
748 }
749
750
751 static dav_error *
db_next_name(dav_db * db,dav_prop_name * pname)752 db_next_name(dav_db *db, dav_prop_name *pname)
753 {
754 /* skip to the next hash entry */
755 if (db->hi != NULL)
756 db->hi = apr_hash_next(db->hi);
757
758 /* fetch the key */
759 get_name(db, pname);
760
761 return NULL;
762 }
763
764
765 static dav_error *
db_get_rollback(dav_db * db,const dav_prop_name * name,dav_deadprop_rollback ** prollback)766 db_get_rollback(dav_db *db,
767 const dav_prop_name *name,
768 dav_deadprop_rollback **prollback)
769 {
770 /* This gets called by mod_dav in preparation for a revprop change.
771 mod_dav_svn doesn't need to make any changes during rollback, but
772 we want the rollback mechanism to trigger. Making changes in
773 response to post-revprop-change hook errors would be positively
774 wrong. */
775
776 *prollback = apr_palloc(db->p, sizeof(dav_deadprop_rollback));
777
778 return NULL;
779 }
780
781
782 static dav_error *
db_apply_rollback(dav_db * db,dav_deadprop_rollback * rollback)783 db_apply_rollback(dav_db *db, dav_deadprop_rollback *rollback)
784 {
785 dav_error *derr;
786
787 if (! db->resource->info->revprop_error)
788 return NULL;
789
790 /* Returning the original revprop change error here will cause this
791 detailed error to get returned to the client in preference to the
792 more generic error created by mod_dav. */
793 derr = dav_svn__convert_err(db->resource->info->revprop_error,
794 HTTP_INTERNAL_SERVER_ERROR, NULL,
795 db->resource->pool);
796 db->resource->info->revprop_error = NULL;
797
798 return derr;
799 }
800
801
802 const dav_hooks_propdb dav_svn__hooks_propdb = {
803 db_open,
804 db_close,
805 db_define_namespaces,
806 db_output_value,
807 db_map_namespaces,
808 db_store,
809 db_remove,
810 db_exists,
811 db_first_name,
812 db_next_name,
813 db_get_rollback,
814 db_apply_rollback,
815 };
816