diff options
Diffstat (limited to 'subversion/libsvn_ra_serf/stat.c')
-rw-r--r-- | subversion/libsvn_ra_serf/stat.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/subversion/libsvn_ra_serf/stat.c b/subversion/libsvn_ra_serf/stat.c new file mode 100644 index 0000000..b6d10c5 --- /dev/null +++ b/subversion/libsvn_ra_serf/stat.c @@ -0,0 +1,615 @@ +/* + * stat.c : file and directory stat and read functions + * + * ==================================================================== + * 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. + * ==================================================================== + */ + + + +#define APR_WANT_STRFUNC +#include <apr_want.h> + +#include <serf.h> + +#include "svn_private_config.h" +#include "svn_pools.h" +#include "svn_xml.h" +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_dirent_uri.h" +#include "svn_hash.h" +#include "svn_path.h" +#include "svn_props.h" +#include "svn_time.h" +#include "svn_version.h" + +#include "private/svn_dav_protocol.h" +#include "private/svn_dep_compat.h" +#include "private/svn_fspath.h" + +#include "ra_serf.h" + + + +/* Implements svn_ra__vtable_t.check_path(). */ +svn_error_t * +svn_ra_serf__check_path(svn_ra_session_t *ra_session, + const char *relpath, + svn_revnum_t revision, + svn_node_kind_t *kind, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_hash_t *props; + svn_error_t *err; + const char *url; + + url = session->session_url.path; + + /* If we have a relative path, append it. */ + if (relpath) + url = svn_path_url_add_component2(url, relpath, scratch_pool); + + /* If we were given a specific revision, get a URL that refers to that + specific revision (rather than floating with HEAD). */ + if (SVN_IS_VALID_REVNUM(revision)) + { + SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, + session, + url, revision, + scratch_pool, scratch_pool)); + } + + /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant. + Or we started with SVN_INVALID_REVNUM and URL may be floating. */ + err = svn_ra_serf__fetch_node_props(&props, session, + url, SVN_INVALID_REVNUM, + check_path_props, + scratch_pool, scratch_pool); + + if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *kind = svn_node_none; + } + else + { + apr_hash_t *dav_props; + const char *res_type; + + /* Any other error, raise to caller. */ + SVN_ERR(err); + + dav_props = apr_hash_get(props, "DAV:", 4); + res_type = svn_prop_get_value(dav_props, "resourcetype"); + if (!res_type) + { + /* How did this happen? */ + return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, + _("The PROPFIND response did not include the " + "requested resourcetype value")); + } + + if (strcmp(res_type, "collection") == 0) + *kind = svn_node_dir; + else + *kind = svn_node_file; + } + + return SVN_NO_ERROR; +} + + +/* Baton for fill_dirent_propfunc() */ +struct fill_dirent_baton_t +{ + /* Update the fields in this entry. */ + svn_dirent_t *entry; + + svn_tristate_t *supports_deadprop_count; + + /* If allocations are necessary, then use this pool. */ + apr_pool_t *result_pool; +}; + +/* Implements svn_ra_serf__prop_func_t */ +static svn_error_t * +fill_dirent_propfunc(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *val, + apr_pool_t *scratch_pool) +{ + struct fill_dirent_baton_t *fdb = baton; + + if (strcmp(ns, "DAV:") == 0) + { + if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) + { + apr_int64_t rev; + SVN_ERR(svn_cstring_atoi64(&rev, val->data)); + + fdb->entry->created_rev = (svn_revnum_t)rev; + } + else if (strcmp(name, "creator-displayname") == 0) + { + fdb->entry->last_author = apr_pstrdup(fdb->result_pool, val->data); + } + else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) + { + SVN_ERR(svn_time_from_cstring(&fdb->entry->time, + val->data, + fdb->result_pool)); + } + else if (strcmp(name, "getcontentlength") == 0) + { + /* 'getcontentlength' property is empty for directories. */ + if (val->len) + { + SVN_ERR(svn_cstring_atoi64(&fdb->entry->size, val->data)); + } + } + else if (strcmp(name, "resourcetype") == 0) + { + if (strcmp(val->data, "collection") == 0) + { + fdb->entry->kind = svn_node_dir; + } + else + { + fdb->entry->kind = svn_node_file; + } + } + } + else if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) + { + fdb->entry->has_props = TRUE; + } + else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) + { + fdb->entry->has_props = TRUE; + } + else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) + { + if(strcmp(name, "deadprop-count") == 0) + { + if (*val->data) + { + apr_int64_t deadprop_count; + SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data)); + fdb->entry->has_props = deadprop_count > 0; + if (fdb->supports_deadprop_count) + *fdb->supports_deadprop_count = svn_tristate_true; + } + else if (fdb->supports_deadprop_count) + *fdb->supports_deadprop_count = svn_tristate_false; + } + } + + return SVN_NO_ERROR; +} + +static const svn_ra_serf__dav_props_t * +get_dirent_props(apr_uint32_t dirent_fields, + svn_ra_serf__session_t *session, + apr_pool_t *pool) +{ + svn_ra_serf__dav_props_t *prop; + apr_array_header_t *props = apr_array_make + (pool, 7, sizeof(svn_ra_serf__dav_props_t)); + + if (session->supports_deadprop_count != svn_tristate_false + || ! (dirent_fields & SVN_DIRENT_HAS_PROPS)) + { + if (dirent_fields & SVN_DIRENT_KIND) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "resourcetype"; + } + + if (dirent_fields & SVN_DIRENT_SIZE) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "getcontentlength"; + } + + if (dirent_fields & SVN_DIRENT_HAS_PROPS) + { + prop = apr_array_push(props); + prop->xmlns = SVN_DAV_PROP_NS_DAV; + prop->name = "deadprop-count"; + } + + if (dirent_fields & SVN_DIRENT_CREATED_REV) + { + svn_ra_serf__dav_props_t *p = apr_array_push(props); + p->xmlns = "DAV:"; + p->name = SVN_DAV__VERSION_NAME; + } + + if (dirent_fields & SVN_DIRENT_TIME) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = SVN_DAV__CREATIONDATE; + } + + if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "creator-displayname"; + } + } + else + { + /* We found an old subversion server that can't handle + the deadprop-count property in the way we expect. + + The neon behavior is to retrieve all properties in this case */ + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "allprop"; + } + + prop = apr_array_push(props); + prop->xmlns = NULL; + prop->name = NULL; + + return (svn_ra_serf__dav_props_t *) props->elts; +} + +/* Implements svn_ra__vtable_t.stat(). */ +svn_error_t * +svn_ra_serf__stat(svn_ra_session_t *ra_session, + const char *relpath, + svn_revnum_t revision, + svn_dirent_t **dirent, + apr_pool_t *pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + svn_error_t *err; + struct fill_dirent_baton_t fdb; + svn_tristate_t deadprop_count = svn_tristate_unknown; + svn_ra_serf__handler_t *handler; + const char *url; + + url = session->session_url.path; + + /* If we have a relative path, append it. */ + if (relpath) + url = svn_path_url_add_component2(url, relpath, pool); + + /* If we were given a specific revision, get a URL that refers to that + specific revision (rather than floating with HEAD). */ + if (SVN_IS_VALID_REVNUM(revision)) + { + SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, + session, + url, revision, + pool, pool)); + } + + fdb.entry = svn_dirent_create(pool); + fdb.supports_deadprop_count = &deadprop_count; + fdb.result_pool = pool; + + SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, url, + SVN_INVALID_REVNUM, "0", + get_dirent_props(SVN_DIRENT_ALL, + session, + pool), + fill_dirent_propfunc, &fdb, pool)); + + err = svn_ra_serf__context_run_one(handler, pool); + + if (err) + { + if (err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *dirent = NULL; + return SVN_NO_ERROR; + } + else + return svn_error_trace(err); + } + + if (deadprop_count == svn_tristate_false + && session->supports_deadprop_count == svn_tristate_unknown + && !fdb.entry->has_props) + { + /* We have to requery as the server didn't give us the right + information */ + session->supports_deadprop_count = svn_tristate_false; + + /* Run the same handler again */ + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); + } + + if (deadprop_count != svn_tristate_unknown) + session->supports_deadprop_count = deadprop_count; + + *dirent = fdb.entry; + + return SVN_NO_ERROR; +} + +/* Baton for get_dir_dirents_cb and get_dir_props_cb */ +struct get_dir_baton_t +{ + apr_pool_t *result_pool; + apr_hash_t *dirents; + apr_hash_t *ret_props; + svn_boolean_t is_directory; + svn_tristate_t supports_deadprop_count; + const char *path; +}; + +/* Implements svn_ra_serf__prop_func_t */ +static svn_error_t * +get_dir_dirents_cb(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + struct get_dir_baton_t *db = baton; + const char *relpath; + + relpath = svn_fspath__skip_ancestor(db->path, path); + + if (relpath && relpath[0] != '\0') + { + struct fill_dirent_baton_t fdb; + + relpath = svn_path_uri_decode(relpath, scratch_pool); + fdb.entry = svn_hash_gets(db->dirents, relpath); + + if (!fdb.entry) + { + fdb.entry = svn_dirent_create(db->result_pool); + svn_hash_sets(db->dirents, + apr_pstrdup(db->result_pool, relpath), + fdb.entry); + } + + fdb.result_pool = db->result_pool; + fdb.supports_deadprop_count = &db->supports_deadprop_count; + SVN_ERR(fill_dirent_propfunc(&fdb, path, ns, name, value, scratch_pool)); + } + else if (relpath && !db->is_directory) + { + if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0) + { + if (strcmp(value->data, "collection") != 0) + { + /* Tell a lie to exit early */ + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get properties of non-directory")); + } + else + db->is_directory = TRUE; + } + } + + return SVN_NO_ERROR; +} + +/* Implements svn_ra_serf__prop_func */ +static svn_error_t * +get_dir_props_cb(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + struct get_dir_baton_t *db = baton; + const char *propname; + + propname = svn_ra_serf__svnname_from_wirename(ns, name, db->result_pool); + if (propname) + { + svn_hash_sets(db->ret_props, propname, + svn_string_dup(value, db->result_pool)); + return SVN_NO_ERROR; + } + + if (!db->is_directory) + { + if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0) + { + if (strcmp(value->data, "collection") != 0) + { + /* Tell a lie to exit early */ + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get properties of non-directory")); + } + else + db->is_directory = TRUE; + } + } + + return SVN_NO_ERROR; +} + +/* Implements svn_ra__vtable_t.get_dir(). */ +svn_error_t * +svn_ra_serf__get_dir(svn_ra_session_t *ra_session, + apr_hash_t **dirents, + svn_revnum_t *fetched_rev, + apr_hash_t **ret_props, + const char *rel_path, + svn_revnum_t revision, + apr_uint32_t dirent_fields, + apr_pool_t *result_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + svn_ra_serf__handler_t *dirent_handler = NULL; + svn_ra_serf__handler_t *props_handler = NULL; + const char *path; + struct get_dir_baton_t gdb; + svn_error_t *err = SVN_NO_ERROR; + + gdb.result_pool = result_pool; + gdb.is_directory = FALSE; + gdb.supports_deadprop_count = svn_tristate_unknown; + + path = session->session_url.path; + + /* If we have a relative path, URI encode and append it. */ + if (rel_path) + { + path = svn_path_url_add_component2(path, rel_path, scratch_pool); + } + + /* If the user specified a peg revision other than HEAD, we have to fetch + the baseline collection url for that revision. If not, we can use the + public url. */ + if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) + { + SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev, + session, + path, revision, + scratch_pool, scratch_pool)); + revision = SVN_INVALID_REVNUM; + } + /* REVISION is always SVN_INVALID_REVNUM */ + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); + + gdb.path = path; + + /* If we're asked for children, fetch them now. */ + if (dirents) + { + /* Always request node kind to check that path is really a + * directory. */ + if (!ret_props) + dirent_fields |= SVN_DIRENT_KIND; + + gdb.dirents = apr_hash_make(result_pool); + + SVN_ERR(svn_ra_serf__create_propfind_handler( + &dirent_handler, session, + path, SVN_INVALID_REVNUM, "1", + get_dirent_props(dirent_fields, + session, + scratch_pool), + get_dir_dirents_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(dirent_handler); + } + else + gdb.dirents = NULL; + + if (ret_props) + { + gdb.ret_props = apr_hash_make(result_pool); + SVN_ERR(svn_ra_serf__create_propfind_handler( + &props_handler, session, + path, SVN_INVALID_REVNUM, "0", + all_props, + get_dir_props_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(props_handler); + } + else + gdb.ret_props = NULL; + + if (dirent_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&dirent_handler->done, + session, + scratch_pool)); + + if (err) + { + svn_pool_clear(scratch_pool); /* Unregisters outstanding requests */ + return err; + } + + if (gdb.supports_deadprop_count == svn_tristate_false + && session->supports_deadprop_count == svn_tristate_unknown + && dirent_fields & SVN_DIRENT_HAS_PROPS) + { + /* We have to requery as the server didn't give us the right + information */ + session->supports_deadprop_count = svn_tristate_false; + + apr_hash_clear(gdb.dirents); + + SVN_ERR(svn_ra_serf__create_propfind_handler( + &dirent_handler, session, + path, SVN_INVALID_REVNUM, "1", + get_dirent_props(dirent_fields, + session, + scratch_pool), + get_dir_dirents_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(dirent_handler); + } + } + + if (props_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&props_handler->done, + session, + scratch_pool)); + } + + /* And dirent again for the case when we had to send the request again */ + if (! err && dirent_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&dirent_handler->done, + session, + scratch_pool)); + } + + if (!err && gdb.supports_deadprop_count != svn_tristate_unknown) + session->supports_deadprop_count = gdb.supports_deadprop_count; + + svn_pool_destroy(scratch_pool); /* Unregisters outstanding requests */ + + SVN_ERR(err); + + if (!gdb.is_directory) + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get entries of non-directory")); + + if (ret_props) + *ret_props = gdb.ret_props; + + if (dirents) + *dirents = gdb.dirents; + + return SVN_NO_ERROR; +} |