| /* |
| * deadprops.c: mod_dav_svn provider functions for "dead properties" |
| * (properties implemented by Subversion or its users, |
| * not as part of the WebDAV specification). |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| #include <apr_hash.h> |
| |
| #include <httpd.h> |
| #include <mod_dav.h> |
| |
| #include "svn_hash.h" |
| #include "svn_xml.h" |
| #include "svn_pools.h" |
| #include "svn_dav.h" |
| #include "svn_base64.h" |
| #include "svn_props.h" |
| #include "private/svn_log.h" |
| |
| #include "dav_svn.h" |
| |
| |
| struct dav_db { |
| const dav_resource *resource; |
| apr_pool_t *p; |
| |
| /* the resource's properties that we are sequencing over */ |
| apr_hash_t *props; |
| apr_hash_index_t *hi; |
| |
| /* used for constructing repos-local names for properties */ |
| svn_stringbuf_t *work; |
| |
| /* passed to svn_repos_ funcs that fetch revprops. */ |
| svn_repos_authz_func_t authz_read_func; |
| void *authz_read_baton; |
| }; |
| |
| |
| struct dav_deadprop_rollback { |
| int dummy; |
| }; |
| |
| |
| /* retrieve the "right" string to use as a repos path */ |
| static const char * |
| get_repos_path(struct dav_resource_private *info) |
| { |
| return info->repos_path; |
| } |
| |
| |
| /* construct the repos-local name for the given DAV property name */ |
| static void |
| get_repos_propname(dav_db *db, |
| const dav_prop_name *name, |
| const char **repos_propname) |
| { |
| if (strcmp(name->ns, SVN_DAV_PROP_NS_SVN) == 0) |
| { |
| /* recombine the namespace ("svn:") and the name. */ |
| svn_stringbuf_set(db->work, SVN_PROP_PREFIX); |
| svn_stringbuf_appendcstr(db->work, name->name); |
| *repos_propname = db->work->data; |
| } |
| else if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0) |
| { |
| /* the name of a custom prop is just the name -- no ns URI */ |
| *repos_propname = name->name; |
| } |
| else |
| { |
| *repos_propname = NULL; |
| } |
| } |
| |
| |
| static dav_error * |
| get_value(dav_db *db, const dav_prop_name *name, svn_string_t **pvalue) |
| { |
| const char *propname; |
| svn_error_t *serr; |
| |
| /* get the repos-local name */ |
| get_repos_propname(db, name, &propname); |
| |
| if (propname == NULL) |
| { |
| /* we know these are not present. */ |
| *pvalue = NULL; |
| return NULL; |
| } |
| |
| /* If db->props exists, then use it to obtain property value. */ |
| if (db->props) |
| { |
| *pvalue = svn_hash_gets(db->props, propname); |
| return NULL; |
| } |
| |
| /* We've got three different types of properties (node, txn, and |
| revision), and we've got two different protocol versions to deal |
| with. Let's try to make some sense of this, shall we? |
| |
| HTTP v1: |
| working baseline ('wbl') resource -> txn prop change |
| non-working, baselined resource ('bln') -> rev prop change [*] |
| working, non-baselined resource ('wrk') -> node prop change |
| |
| HTTP v2: |
| transaction resource ('txn') -> txn prop change |
| revision resource ('rev') -> rev prop change |
| transaction root resource ('txr') -> node prop change |
| |
| [*] This is a violation of the DeltaV spec (### see issue #916). |
| |
| */ |
| |
| if (db->resource->baselined) |
| { |
| if (db->resource->type == DAV_RESOURCE_TYPE_WORKING) |
| serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn, |
| propname, db->p); |
| else |
| serr = svn_repos_fs_revision_prop(pvalue, |
| db->resource->info->repos->repos, |
| db->resource->info->root.rev, |
| propname, db->authz_read_func, |
| db->authz_read_baton, db->p); |
| } |
| else if (db->resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION) |
| { |
| serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn, |
| propname, db->p); |
| } |
| else |
| { |
| serr = svn_fs_node_prop(pvalue, db->resource->info->root.root, |
| get_repos_path(db->resource->info), |
| propname, db->p); |
| } |
| |
| if (serr != NULL) |
| return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, |
| "could not fetch a property", |
| db->resource->pool); |
| |
| return NULL; |
| } |
| |
| |
| 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) |
| { |
| if (strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0) |
| return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, |
| "Attempted to modify 'svn:author' property " |
| "on a transaction"); |
| |
| SVN_ERR(svn_repos_fs_change_txn_prop(txn, propname, value, scratch_pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| 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) |
| { |
| const char *propname; |
| svn_error_t *serr; |
| const dav_resource *resource = db->resource; |
| apr_pool_t *subpool; |
| |
| /* get the repos-local name */ |
| get_repos_propname(db, name, &propname); |
| |
| if (propname == NULL) |
| { |
| if (resource->info->repos->autoversioning) |
| /* ignore the unknown namespace of the incoming prop. */ |
| propname = name->name; |
| else |
| return dav_svn__new_error(db->p, HTTP_CONFLICT, 0, 0, |
| "Properties may only be defined in the " |
| SVN_DAV_PROP_NS_SVN " and " |
| SVN_DAV_PROP_NS_CUSTOM " namespaces."); |
| } |
| |
| /* We've got three different types of properties (node, txn, and |
| revision), and we've got two different protocol versions to deal |
| with. Let's try to make some sense of this, shall we? |
| |
| HTTP v1: |
| working baseline ('wbl') resource -> txn prop change |
| non-working, baselined resource ('bln') -> rev prop change [*] |
| working, non-baselined resource ('wrk') -> node prop change |
| |
| HTTP v2: |
| transaction resource ('txn') -> txn prop change |
| revision resource ('rev') -> rev prop change |
| transaction root resource ('txr') -> node prop change |
| |
| [*] This is a violation of the DeltaV spec (### see issue #916). |
| |
| */ |
| |
| /* A subpool to cope with mod_dav making multiple calls, e.g. during |
| PROPPATCH with multiple values. */ |
| subpool = svn_pool_create(resource->pool); |
| if (resource->baselined) |
| { |
| if (resource->working) |
| { |
| serr = change_txn_prop(resource->info->root.txn, propname, |
| value, subpool); |
| } |
| else |
| { |
| serr = svn_repos_fs_change_rev_prop4(resource->info->repos->repos, |
| resource->info->root.rev, |
| resource->info->repos->username, |
| propname, old_value_p, value, |
| TRUE, TRUE, |
| db->authz_read_func, |
| db->authz_read_baton, |
| subpool); |
| |
| /* Prepare any hook failure message to get sent over the wire */ |
| if (serr) |
| { |
| svn_error_t *purged_serr = svn_error_purge_tracing(serr); |
| if (purged_serr->apr_err == SVN_ERR_REPOS_HOOK_FAILURE) |
| purged_serr->message = apr_xml_quote_string |
| (purged_serr->pool, |
| purged_serr->message, 1); |
| |
| /* mod_dav doesn't handle the returned error very well, it |
| generates its own generic error that will be returned to |
| the client. Cache the detailed error here so that it can |
| be returned a second time when the rollback mechanism |
| triggers. */ |
| resource->info->revprop_error = svn_error_dup(purged_serr); |
| } |
| |
| /* Tell the logging subsystem about the revprop change. */ |
| dav_svn__operational_log(resource->info, |
| svn_log__change_rev_prop( |
| resource->info->root.rev, |
| propname, subpool)); |
| } |
| } |
| else if (resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION) |
| { |
| serr = change_txn_prop(resource->info->root.txn, propname, |
| value, subpool); |
| } |
| else |
| { |
| serr = svn_repos_fs_change_node_prop(resource->info->root.root, |
| get_repos_path(resource->info), |
| propname, value, subpool); |
| } |
| svn_pool_destroy(subpool); |
| |
| if (serr != NULL) |
| return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, |
| NULL, resource->pool); |
| |
| /* a change to the props was made; make sure our cached copy is gone */ |
| db->props = NULL; |
| |
| return NULL; |
| } |
| |
| |
| static dav_error * |
| db_open(apr_pool_t *p, |
| const dav_resource *resource, |
| int ro, |
| dav_db **pdb) |
| { |
| dav_db *db; |
| dav_svn__authz_read_baton *arb; |
| |
| /* Some resource types do not have deadprop databases. |
| Specifically: REGULAR, VERSION, WORKING, and our custom |
| transaction and transaction root resources have them. (SVN does |
| not have WORKSPACE resources, and isn't covered here.) */ |
| |
| if (resource->type == DAV_RESOURCE_TYPE_HISTORY |
| || resource->type == DAV_RESOURCE_TYPE_ACTIVITY |
| || (resource->type == DAV_RESOURCE_TYPE_PRIVATE |
| && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION |
| && resource->info->restype != DAV_SVN_RESTYPE_TXNROOT_COLLECTION)) |
| { |
| *pdb = NULL; |
| return NULL; |
| } |
| |
| /* If the DB is being opened R/W, and this isn't a working resource, then |
| we have a problem! */ |
| if ((! ro) |
| && resource->type != DAV_RESOURCE_TYPE_WORKING |
| && resource->type != DAV_RESOURCE_TYPE_PRIVATE |
| && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION) |
| { |
| /* ### Exception: in violation of deltaV, we *are* allowing a |
| baseline resource to receive a proppatch, as a way of |
| changing unversioned rev props. Remove this someday: see IZ #916. */ |
| if (! (resource->baselined |
| && resource->type == DAV_RESOURCE_TYPE_VERSION)) |
| return dav_svn__new_error(p, HTTP_CONFLICT, 0, 0, |
| "Properties may only be changed on working " |
| "resources."); |
| } |
| |
| db = apr_pcalloc(p, sizeof(*db)); |
| |
| db->resource = resource; |
| db->p = svn_pool_create(p); |
| |
| /* ### temp hack */ |
| db->work = svn_stringbuf_create_empty(db->p); |
| |
| /* make our path-based authz callback available to svn_repos_* funcs. */ |
| arb = apr_pcalloc(p, sizeof(*arb)); |
| arb->r = resource->info->r; |
| arb->repos = resource->info->repos; |
| db->authz_read_baton = arb; |
| db->authz_read_func = dav_svn__authz_read_func(arb); |
| |
| /* ### use RO and node's mutable status to look for an error? */ |
| |
| *pdb = db; |
| |
| return NULL; |
| } |
| |
| |
| static void |
| db_close(dav_db *db) |
| { |
| svn_pool_destroy(db->p); |
| } |
| |
| |
| static dav_error * |
| db_define_namespaces(dav_db *db, dav_xmlns_info *xi) |
| { |
| dav_xmlns_add(xi, "S", SVN_DAV_PROP_NS_SVN); |
| dav_xmlns_add(xi, "C", SVN_DAV_PROP_NS_CUSTOM); |
| dav_xmlns_add(xi, "V", SVN_DAV_PROP_NS_DAV); |
| |
| /* ### we don't have any other possible namespaces right now. */ |
| |
| return NULL; |
| } |
| |
| static dav_error * |
| db_output_value(dav_db *db, |
| const dav_prop_name *name, |
| dav_xmlns_info *xi, |
| apr_text_header *phdr, |
| int *found) |
| { |
| const char *prefix; |
| const char *s; |
| svn_string_t *propval; |
| dav_error *err; |
| apr_pool_t *pool = db->resource->pool; |
| |
| if ((err = get_value(db, name, &propval)) != NULL) |
| return err; |
| |
| /* return whether the prop was found, then punt or handle it. */ |
| *found = (propval != NULL); |
| if (propval == NULL) |
| return NULL; |
| |
| if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0) |
| prefix = "C:"; |
| else |
| prefix = "S:"; |
| |
| if (propval->len == 0) |
| { |
| /* empty value. add an empty elem. */ |
| s = apr_psprintf(pool, "<%s%s/>" DEBUG_CR, prefix, name->name); |
| apr_text_append(pool, phdr, s); |
| } |
| else |
| { |
| /* add <prefix:name [V:encoding="base64"]>value</prefix:name> */ |
| const char *xml_safe; |
| const char *encoding = ""; |
| |
| /* Ensure XML-safety of our property values before sending them |
| across the wire. */ |
| if (! svn_xml_is_xml_safe(propval->data, propval->len)) |
| { |
| const svn_string_t *enc_propval |
| = svn_base64_encode_string2(propval, TRUE, pool); |
| xml_safe = enc_propval->data; |
| encoding = " V:encoding=\"base64\""; |
| } |
| else |
| { |
| svn_stringbuf_t *xmlval = NULL; |
| svn_xml_escape_cdata_string(&xmlval, propval, pool); |
| xml_safe = xmlval->data; |
| } |
| |
| s = apr_psprintf(pool, "<%s%s%s>", prefix, name->name, encoding); |
| apr_text_append(pool, phdr, s); |
| |
| /* the value is in our pool which means it has the right lifetime. */ |
| /* ### at least, per the current mod_dav architecture/API */ |
| apr_text_append(pool, phdr, xml_safe); |
| |
| s = apr_psprintf(pool, "</%s%s>" DEBUG_CR, prefix, name->name); |
| apr_text_append(pool, phdr, s); |
| } |
| |
| return NULL; |
| } |
| |
| |
| static dav_error * |
| db_map_namespaces(dav_db *db, |
| const apr_array_header_t *namespaces, |
| dav_namespace_map **mapping) |
| { |
| /* we don't need a namespace mapping right now. nothing to do */ |
| return NULL; |
| } |
| |
| |
| 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) |
| { |
| apr_xml_attr *attr = elem->attr; |
| |
| /* Default: no "encoding" attribute. */ |
| *absent = FALSE; |
| *out_propval_p = maybe_encoded_propval; |
| |
| /* Check for special encodings of the property value. */ |
| while (attr) |
| { |
| if (strcmp(attr->name, "encoding") == 0) /* ### namespace check? */ |
| { |
| const char *enc_type = attr->value; |
| |
| /* Handle known encodings here. */ |
| if (enc_type && (strcmp(enc_type, "base64") == 0)) |
| *out_propval_p = svn_base64_decode_string(maybe_encoded_propval, |
| pool); |
| else |
| return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0, |
| "Unknown property encoding"); |
| break; |
| } |
| |
| if (strcmp(attr->name, SVN_DAV__OLD_VALUE__ABSENT) == 0) |
| { |
| /* ### parse attr->value */ |
| *absent = TRUE; |
| *out_propval_p = NULL; |
| } |
| |
| /* Next attribute, please. */ |
| attr = attr->next; |
| } |
| |
| return NULL; |
| } |
| |
| static dav_error * |
| db_store(dav_db *db, |
| const dav_prop_name *name, |
| const apr_xml_elem *elem, |
| dav_namespace_map *mapping) |
| { |
| const svn_string_t *const *old_propval_p; |
| const svn_string_t *old_propval; |
| const svn_string_t *propval; |
| svn_boolean_t absent; |
| apr_pool_t *pool = db->p; |
| dav_error *derr; |
| |
| /* SVN sends property values as a big blob of bytes. Thus, there should be |
| no child elements of the property-name element. That also means that |
| the entire contents of the blob is located in elem->first_cdata. The |
| dav_xml_get_cdata() will figure it all out for us, but (normally) it |
| should be awfully fast and not need to copy any data. */ |
| |
| propval = svn_string_create |
| (dav_xml_get_cdata(elem, pool, 0 /* strip_white */), pool); |
| |
| derr = decode_property_value(&propval, &absent, propval, elem, pool); |
| if (derr) |
| return derr; |
| |
| if (absent && ! elem->first_child) |
| /* ### better error check */ |
| return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0, |
| apr_psprintf(pool, |
| "'%s' cannot be specified on the " |
| "value without specifying an " |
| "expectation", |
| SVN_DAV__OLD_VALUE__ABSENT)); |
| |
| /* ### namespace check? */ |
| if (elem->first_child && !strcmp(elem->first_child->name, SVN_DAV__OLD_VALUE)) |
| { |
| /* Parse OLD_PROPVAL. */ |
| old_propval = svn_string_create(dav_xml_get_cdata(elem->first_child, pool, |
| 0 /* strip_white */), |
| pool); |
| derr = decode_property_value(&old_propval, &absent, |
| old_propval, elem->first_child, pool); |
| if (derr) |
| return derr; |
| |
| old_propval_p = (const svn_string_t *const *) &old_propval; |
| } |
| else |
| old_propval_p = NULL; |
| |
| |
| return save_value(db, name, old_propval_p, propval); |
| } |
| |
| |
| static dav_error * |
| db_remove(dav_db *db, const dav_prop_name *name) |
| { |
| svn_error_t *serr; |
| const char *propname; |
| apr_pool_t *subpool; |
| |
| /* get the repos-local name */ |
| get_repos_propname(db, name, &propname); |
| |
| /* ### non-svn props aren't in our repos, so punt for now */ |
| if (propname == NULL) |
| return NULL; |
| |
| /* A subpool to cope with mod_dav making multiple calls, e.g. during |
| PROPPATCH with multiple values. */ |
| subpool = svn_pool_create(db->resource->pool); |
| |
| /* Working Baseline or Working (Version) Resource */ |
| if (db->resource->baselined) |
| if (db->resource->working) |
| serr = change_txn_prop(db->resource->info->root.txn, propname, |
| NULL, subpool); |
| else |
| /* ### VIOLATING deltaV: you can't proppatch a baseline, it's |
| not a working resource! But this is how we currently |
| (hackily) allow the svn client to change unversioned rev |
| props. See issue #916. */ |
| serr = svn_repos_fs_change_rev_prop4(db->resource->info->repos->repos, |
| db->resource->info->root.rev, |
| db->resource->info->repos->username, |
| propname, NULL, NULL, TRUE, TRUE, |
| db->authz_read_func, |
| db->authz_read_baton, |
| subpool); |
| else |
| serr = svn_repos_fs_change_node_prop(db->resource->info->root.root, |
| get_repos_path(db->resource->info), |
| propname, NULL, subpool); |
| svn_pool_destroy(subpool); |
| if (serr != NULL) |
| return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, |
| "could not remove a property", |
| db->resource->pool); |
| |
| /* a change to the props was made; make sure our cached copy is gone */ |
| db->props = NULL; |
| |
| return NULL; |
| } |
| |
| |
| static int |
| db_exists(dav_db *db, const dav_prop_name *name) |
| { |
| const char *propname; |
| svn_string_t *propval; |
| svn_error_t *serr; |
| int retval; |
| |
| /* get the repos-local name */ |
| get_repos_propname(db, name, &propname); |
| |
| /* ### non-svn props aren't in our repos */ |
| if (propname == NULL) |
| return 0; |
| |
| /* Working Baseline, Baseline, or (Working) Version resource */ |
| if (db->resource->baselined) |
| if (db->resource->type == DAV_RESOURCE_TYPE_WORKING) |
| serr = svn_fs_txn_prop(&propval, db->resource->info->root.txn, |
| propname, db->p); |
| else |
| serr = svn_repos_fs_revision_prop(&propval, |
| db->resource->info->repos->repos, |
| db->resource->info->root.rev, |
| propname, |
| db->authz_read_func, |
| db->authz_read_baton, db->p); |
| else |
| serr = svn_fs_node_prop(&propval, db->resource->info->root.root, |
| get_repos_path(db->resource->info), |
| propname, db->p); |
| |
| /* ### try and dispose of the value? */ |
| |
| retval = (serr == NULL && propval != NULL); |
| svn_error_clear(serr); |
| return retval; |
| } |
| |
| static void get_name(dav_db *db, dav_prop_name *pname) |
| { |
| if (db->hi == NULL) |
| { |
| pname->ns = pname->name = NULL; |
| } |
| else |
| { |
| const char *name = apr_hash_this_key(db->hi); |
| |
| #define PREFIX_LEN (sizeof(SVN_PROP_PREFIX) - 1) |
| if (strncmp(name, SVN_PROP_PREFIX, PREFIX_LEN) == 0) |
| #undef PREFIX_LEN |
| { |
| pname->ns = SVN_DAV_PROP_NS_SVN; |
| pname->name = name + 4; |
| } |
| else |
| { |
| pname->ns = SVN_DAV_PROP_NS_CUSTOM; |
| pname->name = name; |
| } |
| } |
| } |
| |
| |
| static dav_error * |
| db_first_name(dav_db *db, dav_prop_name *pname) |
| { |
| /* for operational logging */ |
| const char *action = NULL; |
| |
| /* if we don't have a copy of the properties, then get one */ |
| if (db->props == NULL) |
| { |
| svn_error_t *serr; |
| |
| /* Working Baseline, Baseline, or (Working) Version resource */ |
| if (db->resource->baselined) |
| { |
| if (db->resource->type == DAV_RESOURCE_TYPE_WORKING) |
| serr = svn_fs_txn_proplist(&db->props, |
| db->resource->info->root.txn, |
| db->p); |
| else |
| { |
| action = svn_log__rev_proplist(db->resource->info->root.rev, |
| db->resource->pool); |
| serr = svn_repos_fs_revision_proplist |
| (&db->props, |
| db->resource->info->repos->repos, |
| db->resource->info->root.rev, |
| db->authz_read_func, |
| db->authz_read_baton, |
| db->p); |
| } |
| } |
| else |
| { |
| serr = svn_fs_node_proplist(&db->props, |
| db->resource->info->root.root, |
| get_repos_path(db->resource->info), |
| db->p); |
| |
| if (! serr) |
| { |
| if (db->resource->collection) |
| action = svn_log__get_dir(db->resource->info->repos_path, |
| db->resource->info->root.rev, |
| FALSE, TRUE, 0, db->resource->pool); |
| else |
| action = svn_log__get_file(db->resource->info->repos_path, |
| db->resource->info->root.rev, |
| FALSE, TRUE, db->resource->pool); |
| } |
| } |
| if (serr != NULL) |
| return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, |
| "could not begin sequencing through " |
| "properties", |
| db->resource->pool); |
| } |
| |
| /* begin the iteration over the hash */ |
| db->hi = apr_hash_first(db->p, db->props); |
| |
| /* fetch the first key */ |
| get_name(db, pname); |
| |
| /* If we have a high-level action to log, do so. */ |
| if (action != NULL) |
| dav_svn__operational_log(db->resource->info, action); |
| |
| return NULL; |
| } |
| |
| |
| static dav_error * |
| db_next_name(dav_db *db, dav_prop_name *pname) |
| { |
| /* skip to the next hash entry */ |
| if (db->hi != NULL) |
| db->hi = apr_hash_next(db->hi); |
| |
| /* fetch the key */ |
| get_name(db, pname); |
| |
| return NULL; |
| } |
| |
| |
| static dav_error * |
| db_get_rollback(dav_db *db, |
| const dav_prop_name *name, |
| dav_deadprop_rollback **prollback) |
| { |
| /* This gets called by mod_dav in preparation for a revprop change. |
| mod_dav_svn doesn't need to make any changes during rollback, but |
| we want the rollback mechanism to trigger. Making changes in |
| response to post-revprop-change hook errors would be positively |
| wrong. */ |
| |
| *prollback = apr_palloc(db->p, sizeof(dav_deadprop_rollback)); |
| |
| return NULL; |
| } |
| |
| |
| static dav_error * |
| db_apply_rollback(dav_db *db, dav_deadprop_rollback *rollback) |
| { |
| dav_error *derr; |
| |
| if (! db->resource->info->revprop_error) |
| return NULL; |
| |
| /* Returning the original revprop change error here will cause this |
| detailed error to get returned to the client in preference to the |
| more generic error created by mod_dav. */ |
| derr = dav_svn__convert_err(db->resource->info->revprop_error, |
| HTTP_INTERNAL_SERVER_ERROR, NULL, |
| db->resource->pool); |
| db->resource->info->revprop_error = NULL; |
| |
| return derr; |
| } |
| |
| |
| const dav_hooks_propdb dav_svn__hooks_propdb = { |
| db_open, |
| db_close, |
| db_define_namespaces, |
| db_output_value, |
| db_map_namespaces, |
| db_store, |
| db_remove, |
| db_exists, |
| db_first_name, |
| db_next_name, |
| db_get_rollback, |
| db_apply_rollback, |
| }; |