2 * deadprops.c: mod_dav_svn dead property provider functions for Subversion
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
25 #include "svn_pools.h"
27 #include "svn_base64.h"
28 #include "svn_props.h"
34 const dav_resource
*resource
;
37 /* the resource's properties that we are sequencing over */
41 /* used for constructing repos-local names for properties */
42 svn_stringbuf_t
*work
;
44 /* passed to svn_repos_ funcs that fetch revprops. */
45 svn_repos_authz_func_t authz_read_func
;
46 void *authz_read_baton
;
50 struct dav_deadprop_rollback
{
56 /* retrieve the "right" string to use as a repos path */
58 get_repos_path(struct dav_resource_private
*info
)
60 return info
->repos_path
;
64 /* construct the repos-local name for the given DAV property name */
66 get_repos_propname(dav_db
*db
,
67 const dav_prop_name
*name
,
68 const char **repos_propname
)
70 if (strcmp(name
->ns
, SVN_DAV_PROP_NS_SVN
) == 0)
72 /* recombine the namespace ("svn:") and the name. */
73 svn_stringbuf_set(db
->work
, SVN_PROP_PREFIX
);
74 svn_stringbuf_appendcstr(db
->work
, name
->name
);
75 *repos_propname
= db
->work
->data
;
77 else if (strcmp(name
->ns
, SVN_DAV_PROP_NS_CUSTOM
) == 0)
79 /* the name of a custom prop is just the name -- no ns URI */
80 *repos_propname
= name
->name
;
84 *repos_propname
= NULL
;
90 get_value(dav_db
*db
, const dav_prop_name
*name
, svn_string_t
**pvalue
)
95 /* get the repos-local name */
96 get_repos_propname(db
, name
, &propname
);
100 /* we know these are not present. */
105 /* ### if db->props exists, then try in there first */
107 /* Working Baseline, Baseline, or (Working) Version resource */
108 if (db
->resource
->baselined
)
109 if (db
->resource
->type
== DAV_RESOURCE_TYPE_WORKING
)
110 serr
= svn_fs_txn_prop(pvalue
, db
->resource
->info
->root
.txn
,
113 serr
= svn_repos_fs_revision_prop(pvalue
,
114 db
->resource
->info
-> repos
->repos
,
115 db
->resource
->info
->root
.rev
, propname
,
117 db
->authz_read_baton
, db
->p
);
119 serr
= svn_fs_node_prop(pvalue
, db
->resource
->info
->root
.root
,
120 get_repos_path(db
->resource
->info
),
123 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
124 "could not fetch a property",
132 save_value(dav_db
*db
, const dav_prop_name
*name
, const svn_string_t
*value
)
134 const char *propname
;
137 /* get the repos-local name */
138 get_repos_propname(db
, name
, &propname
);
140 if (propname
== NULL
)
142 if (db
->resource
->info
->repos
->autoversioning
)
143 /* ignore the unknown namespace of the incoming prop. */
144 propname
= name
->name
;
146 return dav_new_error(db
->p
, HTTP_CONFLICT
, 0,
147 "Properties may only be defined in the "
148 SVN_DAV_PROP_NS_SVN
" and " SVN_DAV_PROP_NS_CUSTOM
152 /* Working Baseline or Working (Version) Resource */
153 if (db
->resource
->baselined
)
154 if (db
->resource
->working
)
155 serr
= svn_repos_fs_change_txn_prop(db
->resource
->info
->root
.txn
,
156 propname
, value
, db
->resource
->pool
);
159 /* ### VIOLATING deltaV: you can't proppatch a baseline, it's
160 not a working resource! But this is how we currently
161 (hackily) allow the svn client to change unversioned rev
162 props. See issue #916. */
163 serr
= svn_repos_fs_change_rev_prop3
164 (db
->resource
->info
->repos
->repos
,
165 db
->resource
->info
->root
.rev
,
166 db
->resource
->info
->repos
->username
,
167 propname
, value
, TRUE
, TRUE
,
169 db
->authz_read_baton
,
172 /* Tell the logging subsystem about the revprop change. */
173 dav_svn__operational_log(db
->resource
->info
,
174 apr_psprintf(db
->resource
->pool
,
175 "change-rev-prop r%ld %s",
176 db
->resource
->info
->root
.rev
,
177 svn_path_uri_encode(propname
,
178 db
->resource
->pool
)));
181 serr
= svn_repos_fs_change_node_prop(db
->resource
->info
->root
.root
,
182 get_repos_path(db
->resource
->info
),
183 propname
, value
, db
->resource
->pool
);
185 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
189 /* a change to the props was made; make sure our cached copy is gone */
197 db_open(apr_pool_t
*p
,
198 const dav_resource
*resource
,
203 dav_svn__authz_read_baton
*arb
;
205 /* Some resource types do not have deadprop databases. Specifically:
206 REGULAR, VERSION, and WORKING resources have them. (SVN does not
207 have WORKSPACE resources, and isn't covered here) */
208 if (resource
->type
== DAV_RESOURCE_TYPE_HISTORY
209 || resource
->type
== DAV_RESOURCE_TYPE_ACTIVITY
210 || resource
->type
== DAV_RESOURCE_TYPE_PRIVATE
)
216 /* If the DB is being opened R/W, and this isn't a working resource, then
217 we have a problem! */
218 if (!ro
&& resource
->type
!= DAV_RESOURCE_TYPE_WORKING
)
220 /* ### Exception: in violation of deltaV, we *are* allowing a
221 baseline resource to receive a proppatch, as a way of
222 changing unversioned rev props. Remove this someday: see IZ #916. */
223 if (! (resource
->baselined
224 && resource
->type
== DAV_RESOURCE_TYPE_VERSION
))
225 return dav_new_error(p
, HTTP_CONFLICT
, 0,
226 "Properties may only be changed on working "
230 db
= apr_pcalloc(p
, sizeof(*db
));
232 db
->resource
= resource
;
233 db
->p
= svn_pool_create(p
);
236 db
->work
= svn_stringbuf_ncreate("", 0, db
->p
);
238 /* make our path-based authz callback available to svn_repos_* funcs. */
239 arb
= apr_pcalloc(p
, sizeof(*arb
));
240 arb
->r
= resource
->info
->r
;
241 arb
->repos
= resource
->info
->repos
;
242 db
->authz_read_baton
= arb
;
243 db
->authz_read_func
= dav_svn__authz_read_func(arb
);
245 /* ### use RO and node's mutable status to look for an error? */
256 svn_pool_destroy(db
->p
);
261 db_define_namespaces(dav_db
*db
, dav_xmlns_info
*xi
)
263 dav_xmlns_add(xi
, "S", SVN_DAV_PROP_NS_SVN
);
264 dav_xmlns_add(xi
, "C", SVN_DAV_PROP_NS_CUSTOM
);
265 dav_xmlns_add(xi
, "V", SVN_DAV_PROP_NS_DAV
);
267 /* ### we don't have any other possible namespaces right now. */
273 db_output_value(dav_db
*db
,
274 const dav_prop_name
*name
,
276 apr_text_header
*phdr
,
281 svn_string_t
*propval
;
283 apr_pool_t
*pool
= db
->resource
->pool
;
285 if ((err
= get_value(db
, name
, &propval
)) != NULL
)
288 /* return whether the prop was found, then punt or handle it. */
289 *found
= (propval
!= NULL
);
293 if (strcmp(name
->ns
, SVN_DAV_PROP_NS_CUSTOM
) == 0)
298 if (propval
->len
== 0)
300 /* empty value. add an empty elem. */
301 s
= apr_psprintf(pool
, "<%s%s/>" DEBUG_CR
, prefix
, name
->name
);
302 apr_text_append(pool
, phdr
, s
);
306 /* add <prefix:name [V:encoding="base64"]>value</prefix:name> */
307 const char *xml_safe
;
308 const char *encoding
= "";
310 /* Ensure XML-safety of our property values before sending them
312 if (! svn_xml_is_xml_safe(propval
->data
, propval
->len
))
314 const svn_string_t
*enc_propval
315 = svn_base64_encode_string(propval
, pool
);
316 xml_safe
= enc_propval
->data
;
317 encoding
= apr_pstrcat(pool
, " V:encoding=\"base64\"", NULL
);
321 svn_stringbuf_t
*xmlval
= NULL
;
322 svn_xml_escape_cdata_string(&xmlval
, propval
, pool
);
323 xml_safe
= xmlval
->data
;
326 s
= apr_psprintf(pool
, "<%s%s%s>", prefix
, name
->name
, encoding
);
327 apr_text_append(pool
, phdr
, s
);
329 /* the value is in our pool which means it has the right lifetime. */
330 /* ### at least, per the current mod_dav architecture/API */
331 apr_text_append(pool
, phdr
, xml_safe
);
333 s
= apr_psprintf(pool
, "</%s%s>" DEBUG_CR
, prefix
, name
->name
);
334 apr_text_append(pool
, phdr
, s
);
342 db_map_namespaces(dav_db
*db
,
343 const apr_array_header_t
*namespaces
,
344 dav_namespace_map
**mapping
)
346 /* we don't need a namespace mapping right now. nothing to do */
353 const dav_prop_name
*name
,
354 const apr_xml_elem
*elem
,
355 dav_namespace_map
*mapping
)
357 const svn_string_t
*propval
;
358 apr_pool_t
*pool
= db
->p
;
359 apr_xml_attr
*attr
= elem
->attr
;
361 /* SVN sends property values as a big blob of bytes. Thus, there should be
362 no child elements of the property-name element. That also means that
363 the entire contents of the blob is located in elem->first_cdata. The
364 dav_xml_get_cdata() will figure it all out for us, but (normally) it
365 should be awfully fast and not need to copy any data. */
367 propval
= svn_string_create
368 (dav_xml_get_cdata(elem
, pool
, 0 /* strip_white */), pool
);
370 /* Check for special encodings of the property value. */
373 if (strcmp(attr
->name
, "encoding") == 0) /* ### namespace check? */
375 const char *enc_type
= attr
->value
;
377 /* Handle known encodings here. */
378 if (enc_type
&& (strcmp(enc_type
, "base64") == 0))
379 propval
= svn_base64_decode_string(propval
, pool
);
381 return dav_new_error(pool
, HTTP_INTERNAL_SERVER_ERROR
, 0,
382 "Unknown property encoding");
385 /* Next attribute, please. */
389 return save_value(db
, name
, propval
);
394 db_remove(dav_db
*db
, const dav_prop_name
*name
)
397 const char *propname
;
399 /* get the repos-local name */
400 get_repos_propname(db
, name
, &propname
);
402 /* ### non-svn props aren't in our repos, so punt for now */
403 if (propname
== NULL
)
406 /* Working Baseline or Working (Version) Resource */
407 if (db
->resource
->baselined
)
408 if (db
->resource
->working
)
409 serr
= svn_repos_fs_change_txn_prop(db
->resource
->info
->root
.txn
,
410 propname
, NULL
, db
->resource
->pool
);
412 /* ### VIOLATING deltaV: you can't proppatch a baseline, it's
413 not a working resource! But this is how we currently
414 (hackily) allow the svn client to change unversioned rev
415 props. See issue #916. */
416 serr
= svn_repos_fs_change_rev_prop3(db
->resource
->info
->repos
->repos
,
417 db
->resource
->info
->root
.rev
,
418 db
->resource
->info
->repos
->username
,
419 propname
, NULL
, TRUE
, TRUE
,
421 db
->authz_read_baton
,
424 serr
= svn_repos_fs_change_node_prop(db
->resource
->info
->root
.root
,
425 get_repos_path(db
->resource
->info
),
426 propname
, NULL
, db
->resource
->pool
);
428 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
429 "could not remove a property",
432 /* a change to the props was made; make sure our cached copy is gone */
440 db_exists(dav_db
*db
, const dav_prop_name
*name
)
442 const char *propname
;
443 svn_string_t
*propval
;
447 /* get the repos-local name */
448 get_repos_propname(db
, name
, &propname
);
450 /* ### non-svn props aren't in our repos */
451 if (propname
== NULL
)
454 /* Working Baseline, Baseline, or (Working) Version resource */
455 if (db
->resource
->baselined
)
456 if (db
->resource
->type
== DAV_RESOURCE_TYPE_WORKING
)
457 serr
= svn_fs_txn_prop(&propval
, db
->resource
->info
->root
.txn
,
460 serr
= svn_repos_fs_revision_prop(&propval
,
461 db
->resource
->info
->repos
->repos
,
462 db
->resource
->info
->root
.rev
,
465 db
->authz_read_baton
, db
->p
);
467 serr
= svn_fs_node_prop(&propval
, db
->resource
->info
->root
.root
,
468 get_repos_path(db
->resource
->info
),
471 /* ### try and dispose of the value? */
473 retval
= (serr
== NULL
&& propval
!= NULL
);
474 svn_error_clear(serr
);
478 static void get_name(dav_db
*db
, dav_prop_name
*pname
)
482 pname
->ns
= pname
->name
= NULL
;
488 apr_hash_this(db
->hi
, &name
, NULL
, NULL
);
490 #define PREFIX_LEN (sizeof(SVN_PROP_PREFIX) - 1)
491 if (strncmp(name
, SVN_PROP_PREFIX
, PREFIX_LEN
) == 0)
494 pname
->ns
= SVN_DAV_PROP_NS_SVN
;
495 pname
->name
= (const char *)name
+ 4;
499 pname
->ns
= SVN_DAV_PROP_NS_CUSTOM
;
507 db_first_name(dav_db
*db
, dav_prop_name
*pname
)
509 /* for operational logging */
512 /* if we don't have a copy of the properties, then get one */
513 if (db
->props
== NULL
)
517 /* Working Baseline, Baseline, or (Working) Version resource */
518 if (db
->resource
->baselined
)
520 if (db
->resource
->type
== DAV_RESOURCE_TYPE_WORKING
)
521 serr
= svn_fs_txn_proplist(&db
->props
,
522 db
->resource
->info
->root
.txn
,
526 action
= apr_psprintf(db
->resource
->pool
, "rev-proplist r%ld",
527 db
->resource
->info
->root
.rev
);
528 serr
= svn_repos_fs_revision_proplist
530 db
->resource
->info
->repos
->repos
,
531 db
->resource
->info
->root
.rev
,
533 db
->authz_read_baton
,
539 svn_node_kind_t kind
;
540 serr
= svn_fs_node_proplist(&db
->props
,
541 db
->resource
->info
->root
.root
,
542 get_repos_path(db
->resource
->info
),
545 serr
= svn_fs_check_path(&kind
, db
->resource
->info
->root
.root
,
546 get_repos_path(db
->resource
->info
),
550 action
= apr_psprintf(db
->resource
->pool
, "get-%s %s r%ld props",
551 (kind
== svn_node_dir
? "dir" : "file"),
552 svn_path_uri_encode(db
->resource
->info
->repos_path
,
554 db
->resource
->info
->root
.rev
);
557 return dav_svn__convert_err(serr
, HTTP_INTERNAL_SERVER_ERROR
,
558 "could not begin sequencing through "
563 /* begin the iteration over the hash */
564 db
->hi
= apr_hash_first(db
->p
, db
->props
);
566 /* fetch the first key */
569 /* If we have a high-level action to log, do so. */
571 dav_svn__operational_log(db
->resource
->info
, action
);
578 db_next_name(dav_db
*db
, dav_prop_name
*pname
)
580 /* skip to the next hash entry */
582 db
->hi
= apr_hash_next(db
->hi
);
592 db_get_rollback(dav_db
*db
,
593 const dav_prop_name
*name
,
594 dav_deadprop_rollback
**prollback
)
597 dav_deadprop_rollback
*ddp
;
598 svn_string_t
*propval
;
600 if ((err
= get_value(db
, name
, &propval
)) != NULL
)
603 ddp
= apr_palloc(db
->p
, sizeof(*ddp
));
605 ddp
->value
.data
= propval
? propval
->data
: NULL
;
606 ddp
->value
.len
= propval
? propval
->len
: 0;
614 db_apply_rollback(dav_db
*db
, dav_deadprop_rollback
*rollback
)
616 if (rollback
->value
.data
== NULL
)
618 return db_remove(db
, &rollback
->name
);
621 return save_value(db
, &rollback
->name
, &rollback
->value
);
625 const dav_hooks_propdb dav_svn__hooks_propdb
= {
628 db_define_namespaces
,