summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_serf/stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_serf/stat.c')
-rw-r--r--subversion/libsvn_ra_serf/stat.c615
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;
+}