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