summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_neon/fetch.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_neon/fetch.c')
-rw-r--r--subversion/libsvn_ra_neon/fetch.c2814
1 files changed, 0 insertions, 2814 deletions
diff --git a/subversion/libsvn_ra_neon/fetch.c b/subversion/libsvn_ra_neon/fetch.c
deleted file mode 100644
index 0b8ced0..0000000
--- a/subversion/libsvn_ra_neon/fetch.c
+++ /dev/null
@@ -1,2814 +0,0 @@
-/*
- * fetch.c : routines for fetching updates and checkouts
- *
- * ====================================================================
- * 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 <stdlib.h> /* for free() */
-
-#define APR_WANT_STRFUNC
-#include <apr_want.h> /* for strcmp() */
-
-#include <apr_pools.h>
-#include <apr_tables.h>
-#include <apr_strings.h>
-#include <apr_xml.h>
-
-#include <ne_basic.h>
-
-#include "svn_error.h"
-#include "svn_pools.h"
-#include "svn_delta.h"
-#include "svn_io.h"
-#include "svn_base64.h"
-#include "svn_ra.h"
-#include "../libsvn_ra/ra_loader.h"
-#include "svn_dirent_uri.h"
-#include "svn_path.h"
-#include "svn_xml.h"
-#include "svn_dav.h"
-#include "svn_time.h"
-#include "svn_props.h"
-
-#include "private/svn_dav_protocol.h"
-#include "svn_private_config.h"
-
-#include "ra_neon.h"
-
-
-typedef struct file_read_ctx_t {
- apr_pool_t *pool;
-
- /* these two are the handler that the editor gave us */
- svn_txdelta_window_handler_t handler;
- void *handler_baton;
-
- /* if we're receiving an svndiff, this is a parser which places the
- resulting windows into the above handler/baton. */
- svn_stream_t *stream;
-
-} file_read_ctx_t;
-
-typedef struct file_write_ctx_t {
- svn_boolean_t do_checksum; /* only accumulate checksum if set */
- svn_checksum_ctx_t *checksum_ctx; /* accumulating checksum of file contents */
- svn_stream_t *stream; /* stream to write file contents to */
-} file_write_ctx_t;
-
-typedef struct custom_get_ctx_t {
- svn_ra_neon__request_t *req; /* Used to propagate errors out of the reader */
- int checked_type; /* have we processed ctype yet? */
-
- void *subctx;
-} custom_get_ctx_t;
-
-typedef svn_error_t * (*prop_setter_t)(void *baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool);
-
-typedef struct dir_item_t {
- /* The baton returned by the editor's open_root/open_dir */
- void *baton;
-
- /* Should we fetch properties for this directory when the close tag
- is found? */
- svn_boolean_t fetch_props;
-
- /* The version resource URL for this directory. */
- const char *vsn_url;
-
- /* A buffer which stores the relative directory name. We also use this
- for temporary construction of relative file names. */
- svn_stringbuf_t *pathbuf;
-
- /* If a directory, this may contain a hash of prophashes returned
- from doing a depth 1 PROPFIND. */
- apr_hash_t *children;
-
- /* A subpool. It's about memory. Ya dig? */
- apr_pool_t *pool;
-
-} dir_item_t;
-
-typedef struct report_baton_t {
- svn_ra_neon__session_t *ras;
-
- apr_file_t *tmpfile;
-
- /* The pool of the report baton; used for things that must live during the
- whole editing operation. */
- apr_pool_t *pool;
- /* Pool initialized when the report_baton is created, and meant for
- quick scratchwork. This is like a loop pool, but since the loop
- that drives ra_neon callbacks is in the wrong scope for us to use
- the normal loop pool idiom, we must resort to this. Always clear
- this pool right after using it; only YOU can prevent forest fires. */
- apr_pool_t *scratch_pool;
-
- svn_boolean_t fetch_content;
- svn_boolean_t fetch_props;
-
- const svn_delta_editor_t *editor;
- void *edit_baton;
-
- /* Stack of directory batons/vsn_urls. */
- apr_array_header_t *dirs;
-
-#define TOP_DIR(rb) (APR_ARRAY_IDX((rb)->dirs, (rb)->dirs->nelts - 1, \
- dir_item_t))
-#define DIR_DEPTH(rb) ((rb)->dirs->nelts)
-#define PUSH_BATON(rb,b) (APR_ARRAY_PUSH((rb)->dirs, void *) = (b))
-
- /* These items are only valid inside add- and open-file tags! */
- void *file_baton;
- apr_pool_t *file_pool;
- const char *result_checksum; /* hex md5 digest of result; may be null */
-
- svn_stringbuf_t *namestr;
- svn_stringbuf_t *cpathstr;
- svn_stringbuf_t *href;
-
- /* Empty string means no encoding, "base64" means base64. */
- svn_stringbuf_t *encoding;
-
- /* These are used when receiving an inline txdelta, and null at all
- other times. */
- svn_txdelta_window_handler_t whandler;
- void *whandler_baton;
- svn_stream_t *svndiff_decoder;
- svn_stream_t *base64_decoder;
-
- /* A generic accumulator for elements that have small bits of cdata,
- like md5_checksum, href, etc. Uh, or where our own API gives us
- no choice about holding them in memory, as with prop values, ahem.
- This is always the empty stringbuf when not in use. */
- svn_stringbuf_t *cdata_accum;
-
- /* Are we inside a resource element? */
- svn_boolean_t in_resource;
- /* Valid if in_resource is true. */
- svn_stringbuf_t *current_wcprop_path;
- svn_boolean_t is_switch;
-
- /* Named target, or NULL if none. For example, in 'svn up wc/foo',
- this is "wc/foo", but in 'svn up' it is "".
-
- The target helps us determine whether a response received from
- the server should be acted on. Take 'svn up wc/foo': the server
- may send back a new vsn-rsrc-url wcprop for 'wc' (because the
- report had to be anchored there just in case the update deletes
- wc/foo). While this is correct behavior for the server, the
- client should ignore the new wcprop, because the client knows
- it's not really updating the top level directory. */
- const char *target;
-
- /* Whether the server should try to send copyfrom arguments. */
- svn_boolean_t send_copyfrom_args;
-
- /* Use an intermediate tmpfile for the REPORT response. */
- svn_boolean_t spool_response;
-
- /* If this report is for a switch, update, or status (but not a
- merge/diff), then we made the update report request with the "send-all"
- attribute. The server reponds to this by putting a "send-all" attribute
- in its response. If we see that attribute, we set this to true,
- otherwise, it stays false. */
- svn_boolean_t receiving_all;
-
- /* Hash mapping 'const char *' paths -> 'const char *' lock tokens. */
- apr_hash_t *lock_tokens;
-
-} report_baton_t;
-
-static const svn_ra_neon__xml_elm_t report_elements[] =
-{
- { SVN_XML_NAMESPACE, "update-report", ELEM_update_report, 0 },
- { SVN_XML_NAMESPACE, "resource-walk", ELEM_resource_walk, 0 },
- { SVN_XML_NAMESPACE, "resource", ELEM_resource, 0 },
- { SVN_XML_NAMESPACE, "target-revision", ELEM_target_revision, 0 },
- { SVN_XML_NAMESPACE, "open-directory", ELEM_open_directory, 0 },
- { SVN_XML_NAMESPACE, "add-directory", ELEM_add_directory, 0 },
- { SVN_XML_NAMESPACE, "absent-directory", ELEM_absent_directory, 0 },
- { SVN_XML_NAMESPACE, "open-file", ELEM_open_file, 0 },
- { SVN_XML_NAMESPACE, "add-file", ELEM_add_file, 0 },
- { SVN_XML_NAMESPACE, "txdelta", ELEM_txdelta, 0 },
- { SVN_XML_NAMESPACE, "absent-file", ELEM_absent_file, 0 },
- { SVN_XML_NAMESPACE, "delete-entry", ELEM_delete_entry, 0 },
- { SVN_XML_NAMESPACE, "fetch-props", ELEM_fetch_props, 0 },
- { SVN_XML_NAMESPACE, "set-prop", ELEM_set_prop, 0 },
- { SVN_XML_NAMESPACE, "remove-prop", ELEM_remove_prop, 0 },
- { SVN_XML_NAMESPACE, "fetch-file", ELEM_fetch_file, 0 },
- { SVN_XML_NAMESPACE, "prop", ELEM_SVN_prop, 0 },
- { SVN_DAV_PROP_NS_DAV, "repository-uuid",
- ELEM_repository_uuid, SVN_RA_NEON__XML_CDATA },
-
- { SVN_DAV_PROP_NS_DAV, "md5-checksum", ELEM_md5_checksum,
- SVN_RA_NEON__XML_CDATA },
-
- { "DAV:", "version-name", ELEM_version_name, SVN_RA_NEON__XML_CDATA },
- { "DAV:", SVN_DAV__CREATIONDATE, ELEM_creationdate, SVN_RA_NEON__XML_CDATA },
- { "DAV:", "creator-displayname", ELEM_creator_displayname,
- SVN_RA_NEON__XML_CDATA },
-
- { "DAV:", "checked-in", ELEM_checked_in, 0 },
- { "DAV:", "href", ELEM_href, SVN_RA_NEON__XML_CDATA },
-
- { NULL }
-};
-
-static svn_error_t *simple_store_vsn_url(const char *vsn_url,
- void *baton,
- prop_setter_t setter,
- apr_pool_t *pool)
-{
- /* store the version URL as a property */
- SVN_ERR_W((*setter)(baton, SVN_RA_NEON__LP_VSN_URL,
- svn_string_create(vsn_url, pool), pool),
- _("Could not save the URL of the version resource"));
-
- return NULL;
-}
-
-/* helper func which maps certain DAV: properties to svn:wc:
- properties. Used during checkouts and updates. */
-static svn_error_t *set_special_wc_prop(const char *key,
- const svn_string_t *val,
- prop_setter_t setter,
- void *baton,
- apr_pool_t *pool)
-{
- const char *name = NULL;
-
- if (strcmp(key, SVN_RA_NEON__PROP_VERSION_NAME) == 0)
- name = SVN_PROP_ENTRY_COMMITTED_REV;
- else if (strcmp(key, SVN_RA_NEON__PROP_CREATIONDATE) == 0)
- name = SVN_PROP_ENTRY_COMMITTED_DATE;
- else if (strcmp(key, SVN_RA_NEON__PROP_CREATOR_DISPLAYNAME) == 0)
- name = SVN_PROP_ENTRY_LAST_AUTHOR;
- else if (strcmp(key, SVN_RA_NEON__PROP_REPOSITORY_UUID) == 0)
- name = SVN_PROP_ENTRY_UUID;
-
- /* If we got a name we care about it, call the setter function. */
- if (name)
- SVN_ERR((*setter)(baton, name, val, pool));
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *add_props(apr_hash_t *props,
- prop_setter_t setter,
- void *baton,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
- {
- const void *vkey;
- void *vval;
- const char *key;
- const svn_string_t *val;
-
- apr_hash_this(hi, &vkey, NULL, &vval);
- key = vkey;
- val = vval;
-
-#define NSLEN (sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1)
- if (strncmp(key, SVN_DAV_PROP_NS_CUSTOM, NSLEN) == 0)
- {
- /* for props in the 'custom' namespace, we strip the
- namespace and just use whatever name the user gave the
- property. */
- SVN_ERR((*setter)(baton, key + NSLEN, val, pool));
- continue;
- }
-#undef NSLEN
-
-#define NSLEN (sizeof(SVN_DAV_PROP_NS_SVN) - 1)
- if (strncmp(key, SVN_DAV_PROP_NS_SVN, NSLEN) == 0)
- {
- /* This property is an 'svn:' prop, recognized by client, or
- server, or both. Convert the URI namespace into normal
- 'svn:' prefix again before pushing it at the wc. */
- SVN_ERR((*setter)(baton, apr_pstrcat(pool, SVN_PROP_PREFIX,
- key + NSLEN, (char *)NULL),
- val, pool));
- }
-#undef NSLEN
-
- else
- {
- /* If we get here, then we have a property that is neither
- in the 'custom' space, nor in the 'svn' space. So it
- must be either in the 'network' space or 'DAV:' space.
- The following routine converts a handful of DAV: props
- into 'svn:wc:' or 'svn:entry:' props that libsvn_wc
- wants. */
- SVN_ERR(set_special_wc_prop(key, val, setter, baton, pool));
- }
- }
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *custom_get_request(svn_ra_neon__session_t *ras,
- const char *url,
- const char *editor_relpath,
- svn_ra_neon__block_reader reader,
- void *subctx,
- svn_ra_get_wc_prop_func_t get_wc_prop,
- void *cb_baton,
- svn_boolean_t use_base,
- apr_pool_t *pool)
-{
- custom_get_ctx_t cgc = { 0 };
- const char *delta_base = NULL;
- svn_ra_neon__request_t *request;
- svn_error_t *err;
-
- if (use_base && editor_relpath != NULL)
- {
- /* See if we can get a version URL for this resource. This will
- refer to what we already have in the working copy, thus we
- can get a diff against this particular resource. */
-
- if (get_wc_prop != NULL)
- {
- const svn_string_t *value;
-
- SVN_ERR(get_wc_prop(cb_baton, editor_relpath,
- SVN_RA_NEON__LP_VSN_URL,
- &value, pool));
-
- delta_base = value ? value->data : NULL;
- }
- }
-
- SVN_ERR(svn_ra_neon__request_create(&request, ras, "GET", url, pool));
-
- if (delta_base)
- {
- /* The HTTP delta draft uses an If-None-Match header holding an
- entity tag corresponding to the copy we have. It is much more
- natural for us to use a version URL to specify what we have.
- Thus, we want to use the If: header to specify the URL. But
- mod_dav sees all "State-token" items as lock tokens. When we
- get mod_dav updated and the backend APIs expanded, then we
- can switch to using the If: header. For now, use a custom
- header to specify the version resource to use as the base. */
- ne_add_request_header(request->ne_req,
- SVN_DAV_DELTA_BASE_HEADER, delta_base);
- }
-
- svn_ra_neon__add_response_body_reader(request, ne_accept_2xx, reader, &cgc);
-
- /* complete initialization of the body reading context */
- cgc.req = request;
- cgc.subctx = subctx;
-
- /* run the request */
- err = svn_ra_neon__request_dispatch(NULL, request, NULL, NULL,
- 200 /* OK */,
- 226 /* IM Used */,
- pool);
- svn_ra_neon__request_destroy(request);
-
- /* The request runner raises internal errors before Neon errors,
- pass a returned error to our callers */
-
- return err;
-}
-
-/* This implements the svn_ra_neon__block_reader() callback interface. */
-static svn_error_t *
-fetch_file_reader(void *userdata, const char *buf, size_t len)
-{
- custom_get_ctx_t *cgc = userdata;
- file_read_ctx_t *frc = cgc->subctx;
-
- if (len == 0)
- {
- /* file is complete. */
- return 0;
- }
-
- if (!cgc->checked_type)
- {
- ne_content_type ctype = { 0 };
- int rv = ne_get_content_type(cgc->req->ne_req, &ctype);
-
- if (rv != 0)
- return
- svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL,
- _("Could not get content-type from response"));
-
- /* Neon guarantees non-NULL values when rv==0 */
- if (!strcmp(ctype.type, "application")
- && !strcmp(ctype.subtype, "vnd.svn-svndiff"))
- {
- /* we are receiving an svndiff. set things up. */
- frc->stream = svn_txdelta_parse_svndiff(frc->handler,
- frc->handler_baton,
- TRUE,
- frc->pool);
- }
-
- if (ctype.value)
- free(ctype.value);
-
- cgc->checked_type = 1;
- }
-
- if (frc->stream == NULL)
- {
- /* receiving plain text. construct a window for it. */
-
- svn_txdelta_window_t window = { 0 };
- svn_txdelta_op_t op;
- svn_string_t data;
-
- data.data = buf;
- data.len = len;
-
- op.action_code = svn_txdelta_new;
- op.offset = 0;
- op.length = len;
-
- window.tview_len = len; /* result will be this long */
- window.num_ops = 1;
- window.ops = &op;
- window.new_data = &data;
-
- /* We can't really do anything useful if we get an error here. Pass
- it off to someone who can. */
- SVN_RA_NEON__REQ_ERR
- (cgc->req,
- (*frc->handler)(&window, frc->handler_baton));
- }
- else
- {
- /* receiving svndiff. feed it to the svndiff parser. */
-
- apr_size_t written = len;
-
- SVN_ERR(svn_stream_write(frc->stream, buf, &written));
-
- /* ### the svndiff stream parser does not obey svn_stream semantics
- ### in its write handler. it does not output the number of bytes
- ### consumed by the handler. specifically, it may decrement the
- ### number by 4 for the header, then never touch it again. that
- ### makes it appear like an incomplete write.
- ### disable this check for now. the svndiff parser actually does
- ### consume all bytes, all the time.
- */
-#if 0
- if (written != len && cgc->err == NULL)
- cgc->err = svn_error_createf(SVN_ERR_INCOMPLETE_DATA, NULL,
- "Unable to completely write the svndiff "
- "data to the parser stream "
- "(wrote " APR_SIZE_T_FMT " "
- "of " APR_SIZE_T_FMT " bytes)",
- written, len);
-#endif
- }
-
- return 0;
-}
-
-static svn_error_t *simple_fetch_file(svn_ra_neon__session_t *ras,
- const char *url,
- const char *relpath,
- svn_boolean_t text_deltas,
- void *file_baton,
- const char *base_checksum,
- const svn_delta_editor_t *editor,
- svn_ra_get_wc_prop_func_t get_wc_prop,
- void *cb_baton,
- apr_pool_t *pool)
-{
- file_read_ctx_t frc = { 0 };
-
- SVN_ERR_W((*editor->apply_textdelta)(file_baton,
- base_checksum,
- pool,
- &frc.handler,
- &frc.handler_baton),
- _("Could not save file"));
-
- /* Only bother with text-deltas if our caller cares. */
- if (! text_deltas)
- return (*frc.handler)(NULL, frc.handler_baton);
-
- frc.pool = pool;
-
- SVN_ERR(custom_get_request(ras, url, relpath,
- fetch_file_reader, &frc,
- get_wc_prop, cb_baton,
- TRUE, pool));
-
- /* close the handler, since the file reading completed successfully. */
- if (frc.stream)
- return svn_stream_close(frc.stream);
- else
- return (*frc.handler)(NULL, frc.handler_baton);
-}
-
-/* Helper for svn_ra_neon__get_file. This implements
- the svn_ra_neon__block_reader() callback interface. */
-static svn_error_t *
-get_file_reader(void *userdata, const char *buf, size_t len)
-{
- custom_get_ctx_t *cgc = userdata;
-
- /* The stream we want to push data at. */
- file_write_ctx_t *fwc = cgc->subctx;
- svn_stream_t *stream = fwc->stream;
-
- if (fwc->do_checksum)
- SVN_ERR(svn_checksum_update(fwc->checksum_ctx, buf, len));
-
- /* Write however many bytes were passed in by neon. */
- return svn_stream_write(stream, buf, &len);
-}
-
-
-/* minor helper for svn_ra_neon__get_file, of type prop_setter_t */
-static svn_error_t *
-add_prop_to_hash(void *baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- apr_hash_t *ht = (apr_hash_t *) baton;
- apr_hash_set(ht, name, APR_HASH_KEY_STRING, value);
- return SVN_NO_ERROR;
-}
-
-
-/* Helper for svn_ra_neon__get_file(), svn_ra_neon__get_dir(), and
- svn_ra_neon__rev_proplist().
-
- Loop over the properties in RSRC->propset, examining namespaces and
- such to filter Subversion, custom, etc. properties.
-
- User-visible props get added to the PROPS hash (alloced in POOL).
-
- If ADD_ENTRY_PROPS is true, then "special" working copy entry-props
- are added to the hash by set_special_wc_prop().
-*/
-static svn_error_t *
-filter_props(apr_hash_t *props,
- svn_ra_neon__resource_t *rsrc,
- svn_boolean_t add_entry_props,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(pool, rsrc->propset); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- const char *name;
- void *val;
- const svn_string_t *value;
-
- apr_hash_this(hi, &key, NULL, &val);
- name = key;
- value = svn_string_dup(val, pool);
-
- /* If the property is in the 'custom' namespace, then it's a
- normal user-controlled property coming from the fs. Just
- strip off this prefix and add to the hash. */
-#define NSLEN (sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1)
- if (strncmp(name, SVN_DAV_PROP_NS_CUSTOM, NSLEN) == 0)
- {
- apr_hash_set(props, name + NSLEN, APR_HASH_KEY_STRING, value);
- continue;
- }
-#undef NSLEN
-
- /* If the property is in the 'svn' namespace, then it's a
- normal user-controlled property coming from the fs. Just
- strip off the URI prefix, add an 'svn:', and add to the hash. */
-#define NSLEN (sizeof(SVN_DAV_PROP_NS_SVN) - 1)
- if (strncmp(name, SVN_DAV_PROP_NS_SVN, NSLEN) == 0)
- {
- apr_hash_set(props,
- apr_pstrcat(pool, SVN_PROP_PREFIX, name + NSLEN,
- (char *)NULL),
- APR_HASH_KEY_STRING,
- value);
- continue;
- }
-#undef NSLEN
- else if (strcmp(name, SVN_RA_NEON__PROP_CHECKED_IN) == 0)
- {
- /* For files, we currently only have one 'wc' prop. */
- apr_hash_set(props, SVN_RA_NEON__LP_VSN_URL,
- APR_HASH_KEY_STRING, value);
- }
- else
- {
- /* If it's one of the 'entry' props, this func will
- recognize the DAV: name & add it to the hash mapped to a
- new name recognized by libsvn_wc. */
- if (add_entry_props)
- SVN_ERR(set_special_wc_prop(name, value, add_prop_to_hash,
- props, pool));
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-static const ne_propname restype_props[] =
-{
- { "DAV:", "resourcetype" },
- { NULL }
-};
-
-static const ne_propname restype_checksum_props[] =
-{
- { "DAV:", "resourcetype" },
- { SVN_DAV_PROP_NS_DAV, "md5-checksum" },
- { NULL }
-};
-
-svn_error_t *svn_ra_neon__get_file(svn_ra_session_t *session,
- const char *path,
- svn_revnum_t revision,
- svn_stream_t *stream,
- svn_revnum_t *fetched_rev,
- apr_hash_t **props,
- apr_pool_t *pool)
-{
- svn_ra_neon__resource_t *rsrc;
- const char *final_url;
- svn_ra_neon__session_t *ras = session->priv;
- const char *url = svn_path_url_add_component2(ras->url->data, path, pool);
- const ne_propname *which_props;
-
- /* If the revision is invalid (head), then we're done. Just fetch
- the public URL, because that will always get HEAD. */
- if ((! SVN_IS_VALID_REVNUM(revision)) && (fetched_rev == NULL))
- final_url = url;
-
- /* If the revision is something specific, we need to create a bc_url. */
- else
- {
- svn_revnum_t got_rev;
- const char *bc_url;
- const char *bc_relative;
-
- SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, &got_rev,
- ras, url, revision, pool));
- final_url = svn_path_url_add_component2(bc_url, bc_relative, pool);
- if (fetched_rev != NULL)
- *fetched_rev = got_rev;
- }
-
- if (props)
- {
- /* Request all properties if caller requested them. */
- which_props = NULL;
- }
- else if (stream)
- {
- /* Request md5 checksum and resource type properties if caller
- requested file contents. */
- which_props = restype_checksum_props;
- }
- else
- {
- /* Request only resource type on other cases. */
- which_props = restype_props;
- }
-
- SVN_ERR(svn_ra_neon__get_props_resource(&rsrc, ras, final_url, NULL,
- which_props, pool));
- if (rsrc->is_collection)
- {
- return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
- _("Can't get text contents of a directory"));
- }
-
- if (props)
- {
- *props = apr_hash_make(pool);
- SVN_ERR(filter_props(*props, rsrc, TRUE, pool));
- }
-
- if (stream)
- {
- const svn_string_t *expected_checksum;
- file_write_ctx_t fwc;
-
- expected_checksum = apr_hash_get(rsrc->propset,
- SVN_RA_NEON__PROP_MD5_CHECKSUM,
- APR_HASH_KEY_STRING);
-
- /* Older servers don't serve checksum prop, but that's okay. */
- /* ### temporary hack for 0.17. if the server doesn't have the prop,
- ### then __get_one_prop returns an empty string. deal with it. */
- if (!expected_checksum
- || (expected_checksum && expected_checksum->data[0] == '\0'))
- {
- fwc.do_checksum = FALSE;
- }
- else
- {
- fwc.do_checksum = TRUE;
- }
-
- fwc.stream = stream;
-
- if (fwc.do_checksum)
- fwc.checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
-
- /* Fetch the file, shoving it at the provided stream. */
- SVN_ERR(custom_get_request(ras, final_url, path,
- get_file_reader, &fwc,
- ras->callbacks->get_wc_prop,
- ras->callback_baton,
- FALSE, pool));
-
- if (fwc.do_checksum)
- {
- const char *hex_digest;
- svn_checksum_t *checksum;
-
- SVN_ERR(svn_checksum_final(&checksum, fwc.checksum_ctx, pool));
- hex_digest = svn_checksum_to_cstring_display(checksum, pool);
-
- if (strcmp(hex_digest, expected_checksum->data) != 0)
- return svn_error_createf
- (SVN_ERR_CHECKSUM_MISMATCH, NULL,
- apr_psprintf(pool, "%s:\n%s\n%s\n",
- _("Checksum mismatch for '%s'"),
- _(" expected: %s"),
- _(" actual: %s")),
- path, expected_checksum->data, hex_digest);
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *svn_ra_neon__get_dir(svn_ra_session_t *session,
- apr_hash_t **dirents,
- svn_revnum_t *fetched_rev,
- apr_hash_t **props,
- const char *path,
- svn_revnum_t revision,
- apr_uint32_t dirent_fields,
- apr_pool_t *pool)
-{
- svn_ra_neon__resource_t *rsrc;
- apr_hash_index_t *hi;
- apr_hash_t *resources;
- const char *final_url;
- apr_size_t final_url_n_components;
- svn_ra_neon__session_t *ras = session->priv;
- const char *url = svn_path_url_add_component2(ras->url->data, path, pool);
-
- /* If the revision is invalid (HEAD), then we're done -- just fetch
- the public URL, because that will always get HEAD. Otherwise, we
- need to create a bc_url. */
- if ((! SVN_IS_VALID_REVNUM(revision)) && (fetched_rev == NULL))
- {
- final_url = url;
- }
- else
- {
- svn_revnum_t got_rev;
- const char *bc_url;
- const char *bc_relative;
-
- SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, &got_rev,
- ras, url, revision, pool));
- final_url = svn_path_url_add_component2(bc_url, bc_relative, pool);
- if (fetched_rev != NULL)
- *fetched_rev = got_rev;
- }
-
- if (dirents)
- {
- ne_propname *which_props;
- svn_boolean_t supports_deadprop_count;
-
- /* For issue 2151: See if we are dealing with a server that
- understands the deadprop-count property. If it doesn't, we'll
- need to do an allprop PROPFIND. If it does, we'll execute a more
- targeted PROPFIND. */
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- SVN_ERR(svn_ra_neon__get_deadprop_count_support(
- &supports_deadprop_count, ras, final_url, pool));
- }
-
- /* if we didn't ask for the has_props field, we can get individual
- properties. */
- if ((SVN_DIRENT_HAS_PROPS & dirent_fields) == 0
- || supports_deadprop_count)
- {
- int num_props = 1; /* start with one for the final NULL */
-
- if (dirent_fields & SVN_DIRENT_KIND)
- ++num_props;
-
- if (dirent_fields & SVN_DIRENT_SIZE)
- ++num_props;
-
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- ++num_props;
-
- if (dirent_fields & SVN_DIRENT_CREATED_REV)
- ++num_props;
-
- if (dirent_fields & SVN_DIRENT_TIME)
- ++num_props;
-
- if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
- ++num_props;
-
- which_props = apr_pcalloc(pool, num_props * sizeof(ne_propname));
-
- --num_props; /* damn zero based arrays... */
-
- /* first, null out the end... */
- which_props[num_props].nspace = NULL;
- which_props[num_props--].name = NULL;
-
- /* Now, go through and fill in the ones we care about, moving along
- the array as we go. */
-
- if (dirent_fields & SVN_DIRENT_KIND)
- {
- which_props[num_props].nspace = "DAV:";
- which_props[num_props--].name = "resourcetype";
- }
-
- if (dirent_fields & SVN_DIRENT_SIZE)
- {
- which_props[num_props].nspace = "DAV:";
- which_props[num_props--].name = "getcontentlength";
- }
-
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- which_props[num_props].nspace = SVN_DAV_PROP_NS_DAV;
- which_props[num_props--].name = "deadprop-count";
- }
-
- if (dirent_fields & SVN_DIRENT_CREATED_REV)
- {
- which_props[num_props].nspace = "DAV:";
- which_props[num_props--].name = "version-name";
- }
-
- if (dirent_fields & SVN_DIRENT_TIME)
- {
- which_props[num_props].nspace = "DAV:";
- which_props[num_props--].name = SVN_DAV__CREATIONDATE;
- }
-
- if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
- {
- which_props[num_props].nspace = "DAV:";
- which_props[num_props--].name = "creator-displayname";
- }
-
- SVN_ERR_ASSERT(num_props == -1);
- }
- else
- {
- /* get all props, since we need them all to do has_props */
- which_props = NULL;
- }
-
- /* Just like Nautilus, Cadaver, or any other browser, we do a
- PROPFIND on the directory of depth 1. */
- SVN_ERR(svn_ra_neon__get_props(&resources, ras,
- final_url, SVN_RA_NEON__DEPTH_ONE,
- NULL, which_props, pool));
-
- /* Count the number of path components in final_url. */
- final_url_n_components = svn_path_component_count(final_url);
-
- /* Now we have a hash that maps a bunch of url children to resource
- objects. Each resource object contains the properties of the
- child. Parse these resources into svn_dirent_t structs. */
- *dirents = apr_hash_make(pool);
- for (hi = apr_hash_first(pool, resources);
- hi;
- hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- const char *childname;
- svn_ra_neon__resource_t *resource;
- const svn_string_t *propval;
- apr_hash_index_t *h;
- svn_dirent_t *entry;
-
- apr_hash_this(hi, &key, NULL, &val);
- childname = svn_relpath_canonicalize(key, pool);
- resource = val;
-
- /* Skip the effective '.' entry that comes back from
- SVN_RA_NEON__DEPTH_ONE. The children must have one more
- component then final_url.
- Note that we can't just strcmp the URLs because of URL encoding
- differences (i.e. %3c vs. %3C etc.) */
- if (svn_path_component_count(childname) == final_url_n_components)
- continue;
-
- entry = apr_pcalloc(pool, sizeof(*entry));
-
- if (dirent_fields & SVN_DIRENT_KIND)
- {
- /* node kind */
- entry->kind = resource->is_collection ? svn_node_dir
- : svn_node_file;
- }
-
- if (dirent_fields & SVN_DIRENT_SIZE)
- {
- /* size */
- propval = apr_hash_get(resource->propset,
- SVN_RA_NEON__PROP_GETCONTENTLENGTH,
- APR_HASH_KEY_STRING);
- if (propval == NULL)
- entry->size = 0;
- else
- entry->size = svn__atoui64(propval->data);
- }
-
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- /* Does this resource contain any 'svn' or 'custom'
- properties (e.g. ones actually created and set by the
- user)? */
- if (supports_deadprop_count)
- {
- propval = apr_hash_get(resource->propset,
- SVN_RA_NEON__PROP_DEADPROP_COUNT,
- APR_HASH_KEY_STRING);
-
- if (propval == NULL)
- {
- /* we thought that the server supported the
- deadprop-count property. apparently not. */
- return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
- _("Server response missing the "
- "expected deadprop-count "
- "property"));
- }
- else
- {
- apr_int64_t prop_count = svn__atoui64(propval->data);
- entry->has_props = (prop_count > 0);
- }
- }
- else
- {
- /* The server doesn't support the deadprop_count prop,
- fallback */
- for (h = apr_hash_first(pool, resource->propset);
- h; h = apr_hash_next(h))
- {
- const void *kkey;
- apr_hash_this(h, &kkey, NULL, NULL);
-
- if (strncmp((const char *) kkey, SVN_DAV_PROP_NS_CUSTOM,
- sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1) == 0
- || strncmp((const char *) kkey, SVN_DAV_PROP_NS_SVN,
- sizeof(SVN_DAV_PROP_NS_SVN) - 1) == 0)
- entry->has_props = TRUE;
- }
- }
- }
-
- if (dirent_fields & SVN_DIRENT_CREATED_REV)
- {
- /* created_rev & friends */
- propval = apr_hash_get(resource->propset,
- SVN_RA_NEON__PROP_VERSION_NAME,
- APR_HASH_KEY_STRING);
- if (propval != NULL)
- entry->created_rev = SVN_STR_TO_REV(propval->data);
- }
-
- if (dirent_fields & SVN_DIRENT_TIME)
- {
- propval = apr_hash_get(resource->propset,
- SVN_RA_NEON__PROP_CREATIONDATE,
- APR_HASH_KEY_STRING);
- if (propval != NULL)
- SVN_ERR(svn_time_from_cstring(&(entry->time),
- propval->data, pool));
- }
-
- if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
- {
- propval = apr_hash_get(resource->propset,
- SVN_RA_NEON__PROP_CREATOR_DISPLAYNAME,
- APR_HASH_KEY_STRING);
- if (propval != NULL)
- entry->last_author = propval->data;
- }
-
- apr_hash_set(*dirents,
- svn_path_uri_decode(svn_relpath_basename(childname,
- pool),
- pool),
- APR_HASH_KEY_STRING, entry);
- }
- }
-
- if (props)
- {
- SVN_ERR(svn_ra_neon__get_props_resource(&rsrc, ras, final_url,
- NULL, NULL /* all props */,
- pool));
-
- *props = apr_hash_make(pool);
- SVN_ERR(filter_props(*props, rsrc, TRUE, pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-svn_error_t *svn_ra_neon__get_latest_revnum(svn_ra_session_t *session,
- svn_revnum_t *latest_revnum,
- apr_pool_t *pool)
-{
- svn_ra_neon__session_t *ras = session->priv;
- return svn_ra_neon__get_baseline_info(NULL, NULL, latest_revnum,
- ras, ras->root.path,
- SVN_INVALID_REVNUM, pool);
-}
-
-/* ------------------------------------------------------------------------- */
-
-
-svn_error_t *svn_ra_neon__change_rev_prop(svn_ra_session_t *session,
- svn_revnum_t rev,
- const char *name,
- const svn_string_t *const *old_value_p,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- svn_ra_neon__session_t *ras = session->priv;
- svn_error_t *err;
- apr_hash_t *prop_changes = NULL;
- apr_array_header_t *prop_deletes = NULL;
- apr_hash_t *prop_old_values = NULL;
- const char *proppatch_target;
-
- if (old_value_p)
- {
- svn_boolean_t capable;
- SVN_ERR(svn_ra_neon__has_capability(session, &capable,
- SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
- pool));
-
- /* How did you get past the same check in svn_ra_change_rev_prop2()? */
- SVN_ERR_ASSERT(capable);
- }
-
- /* Main objective: do a PROPPATCH (allprops) on a baseline object */
-
- /* ### A Word From Our Sponsor: see issue #916.
-
- Be it heretofore known that this Subversion behavior is
- officially in violation of WebDAV/DeltaV. DeltaV has *no*
- concept of unversioned properties, anywhere. If you proppatch
- something, some new version of *something* is created.
-
- In particular, we've decided that a 'baseline' maps to an svn
- revision; if we attempted to proppatch a baseline, a *normal*
- DeltaV server would do an auto-checkout, patch the working
- baseline, auto-checkin, and create a new baseline. But
- mod_dav_svn just changes the baseline destructively.
- */
-
- if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(ras))
- {
- proppatch_target = apr_psprintf(pool, "%s/%ld", ras->rev_stub, rev);
- }
- else
- {
- svn_ra_neon__resource_t *baseline;
- static const ne_propname wanted_props[] =
- {
- { "DAV:", "auto-version" },
- { NULL }
- };
- /* Get the baseline resource. */
- SVN_ERR(svn_ra_neon__get_baseline_props(NULL, &baseline,
- ras,
- ras->url->data,
- rev,
- wanted_props, /* DAV:auto-version */
- pool));
- /* ### TODO: if we got back some value for the baseline's
- 'DAV:auto-version' property, interpret it. We *don't* want
- to attempt the PROPPATCH if the deltaV server is going to do
- auto-versioning and create a new baseline! */
-
- proppatch_target = baseline->url;
- }
-
- if (old_value_p)
- {
- svn_dav__two_props_t *both_values;
-
- both_values = apr_palloc(pool, sizeof(*both_values));
- both_values->old_value_p = old_value_p;
- both_values->new_value = value;
-
- prop_old_values = apr_hash_make(pool);
- apr_hash_set(prop_old_values, name, APR_HASH_KEY_STRING, both_values);
- }
- else
- {
- if (value)
- {
- prop_changes = apr_hash_make(pool);
- apr_hash_set(prop_changes, name, APR_HASH_KEY_STRING, value);
- }
- else
- {
- prop_deletes = apr_array_make(pool, 1, sizeof(const char *));
- APR_ARRAY_PUSH(prop_deletes, const char *) = name;
- }
- }
-
- err = svn_ra_neon__do_proppatch(ras, proppatch_target, prop_changes,
- prop_deletes, prop_old_values, NULL, pool);
- if (err)
- return
- svn_error_create
- (SVN_ERR_RA_DAV_REQUEST_FAILED, err,
- _("DAV request failed; it's possible that the repository's "
- "pre-revprop-change hook either failed or is non-existent"));
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *svn_ra_neon__rev_proplist(svn_ra_session_t *session,
- svn_revnum_t rev,
- apr_hash_t **props,
- apr_pool_t *pool)
-{
- svn_ra_neon__session_t *ras = session->priv;
- svn_ra_neon__resource_t *bln;
- const char *label;
- const char *url;
-
- *props = apr_hash_make(pool);
-
- /* Main objective: do a PROPFIND (allprops) on a baseline object. If we
- have HTTP v2 support available, we can build the URI of that object.
- Otherwise, we have to hunt for a bit. (We pass NULL for 'which_props'
- in these functions because we want 'em all.) */
- if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(ras))
- {
- url = apr_psprintf(pool, "%s/%ld", ras->rev_stub, rev);
- label = NULL;
- }
- else
- {
- SVN_ERR(svn_ra_neon__get_vcc(&url, ras, ras->url->data, pool));
- label = apr_psprintf(pool, "%ld", rev);
- }
-
- SVN_ERR(svn_ra_neon__get_props_resource(&bln, ras, url,
- label, NULL, pool));
-
- /* Build a new property hash, based on the one in the baseline
- resource. In particular, convert the xml-property-namespaces
- into ones that the client understands. Strip away the DAV:
- liveprops as well. */
- return filter_props(*props, bln, FALSE, pool);
-}
-
-
-svn_error_t *svn_ra_neon__rev_prop(svn_ra_session_t *session,
- svn_revnum_t rev,
- const char *name,
- svn_string_t **value,
- apr_pool_t *pool)
-{
- apr_hash_t *props;
-
- /* We just call svn_ra_neon__rev_proplist() and filter its results here
- * because sending the property name to the server may create an error
- * if it has a colon in its name. While more costly this allows DAV
- * clients to still gain access to all the allowed property names.
- * See Issue #1807 for more details. */
- SVN_ERR(svn_ra_neon__rev_proplist(session, rev, &props, pool));
-
- *value = apr_hash_get(props, name, APR_HASH_KEY_STRING);
-
- return SVN_NO_ERROR;
-}
-
-
-
-
-/* -------------------------------------------------------------------------
-**
-** UPDATE HANDLING
-**
-** ### docco...
-**
-** DTD of the update report:
-** ### open/add file/dir. first child is always checked-in/href (vsn_url).
-** ### next are subdir elems, possibly fetch-file, then fetch-prop.
-*/
-
-/* Determine whether we're receiving the expected XML response.
- Return CHILD when interested in receiving the child's contents
- or one of SVN_RA_NEON__XML_INVALID and SVN_RA_NEON__XML_DECLINE
- when respectively this is the incorrect response or
- the element (and its children) are uninteresting */
-static int validate_element(svn_ra_neon__xml_elmid parent,
- svn_ra_neon__xml_elmid child)
-{
- /* We're being very strict with the validity of XML elements here. If
- something exists that we don't know about, then we might not update
- the client properly. We also make various assumptions in the element
- processing functions, and the strong validation enables those
- assumptions. */
-
- switch (parent)
- {
- case ELEM_root:
- if (child == ELEM_update_report)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_update_report:
- if (child == ELEM_target_revision
- || child == ELEM_open_directory
- || child == ELEM_resource_walk)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_resource_walk:
- if (child == ELEM_resource)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_resource:
- if (child == ELEM_checked_in)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_open_directory:
- if (child == ELEM_absent_directory
- || child == ELEM_open_directory
- || child == ELEM_add_directory
- || child == ELEM_absent_file
- || child == ELEM_open_file
- || child == ELEM_add_file
- || child == ELEM_fetch_props
- || child == ELEM_set_prop
- || child == ELEM_remove_prop
- || child == ELEM_delete_entry
- || child == ELEM_SVN_prop
- || child == ELEM_checked_in)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_add_directory:
- if (child == ELEM_absent_directory
- || child == ELEM_add_directory
- || child == ELEM_absent_file
- || child == ELEM_add_file
- || child == ELEM_remove_prop
- || child == ELEM_set_prop
- || child == ELEM_SVN_prop
- || child == ELEM_checked_in)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_open_file:
- if (child == ELEM_checked_in
- || child == ELEM_fetch_file
- || child == ELEM_SVN_prop
- || child == ELEM_txdelta
- || child == ELEM_fetch_props
- || child == ELEM_set_prop
- || child == ELEM_remove_prop)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_add_file:
- if (child == ELEM_checked_in
- || child == ELEM_txdelta
- || child == ELEM_set_prop
- || child == ELEM_remove_prop
- || child == ELEM_SVN_prop)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_checked_in:
- if (child == ELEM_href)
- return child;
- else
- return SVN_RA_NEON__XML_INVALID;
-
- case ELEM_set_prop:
- /* Prop name is an attribute, prop value is CDATA, so no child elts. */
- return child;
-
- case ELEM_SVN_prop:
- /* if (child == ELEM_version_name
- || child == ELEM_creationdate
- || child == ELEM_creator_displayname
- || child == ELEM_md5_checksum
- || child == ELEM_repository_uuid
- || child == ELEM_remove_prop)
- return child;
- else
- return SVN_RA_NEON__XML_DECLINE;
- */
- /* ### TODO: someday uncomment the block above, and make the
- else clause return NE_XML_IGNORE. But first, neon needs to
- define that value. :-) */
- return child;
-
- default:
- return SVN_RA_NEON__XML_DECLINE;
- }
-
- /* NOTREACHED */
-}
-
-static void push_dir(report_baton_t *rb,
- void *baton,
- svn_stringbuf_t *pathbuf,
- apr_pool_t *pool)
-{
- dir_item_t *di = apr_array_push(rb->dirs);
-
- memset(di, 0, sizeof(*di));
- di->baton = baton;
- di->pathbuf = pathbuf;
- di->pool = pool;
-}
-
-/* This implements the `ne_xml_startelm_cb' prototype. */
-static svn_error_t *
-start_element(int *elem, void *userdata, int parent, const char *nspace,
- const char *elt_name, const char **atts)
-{
- report_baton_t *rb = userdata;
- const char *att;
- svn_revnum_t base;
- const char *name;
- const char *bc_url;
- svn_stringbuf_t *cpath = NULL;
- svn_revnum_t crev = SVN_INVALID_REVNUM;
- dir_item_t *parent_dir;
- void *new_dir_baton;
- svn_stringbuf_t *pathbuf;
- apr_pool_t *subpool;
- const char *base_checksum = NULL;
- const svn_ra_neon__xml_elm_t *elm;
-
- elm = svn_ra_neon__lookup_xml_elem(report_elements, nspace, elt_name);
- *elem = elm ? validate_element(parent, elm->id) : SVN_RA_NEON__XML_DECLINE;
- if (*elem < 1) /* not a valid element */
- return SVN_NO_ERROR;
-
- switch (elm->id)
- {
- case ELEM_update_report:
- att = svn_xml_get_attr_value("send-all", atts);
- if (att && (strcmp(att, "true") == 0))
- rb->receiving_all = TRUE;
- break;
-
- case ELEM_target_revision:
- att = svn_xml_get_attr_value("rev", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing rev attr in target-revision"
- " element"));
- SVN_ERR((*rb->editor->set_target_revision)(rb->edit_baton,
- SVN_STR_TO_REV(att),
- rb->pool));
- break;
-
- case ELEM_absent_directory:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-directory"
- " element"));
-
- parent_dir = &TOP_DIR(rb);
- pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, parent_dir->pool);
- svn_path_add_component(pathbuf, name);
-
- SVN_ERR((*rb->editor->absent_directory)(pathbuf->data,
- parent_dir->baton,
- parent_dir->pool));
- break;
-
- case ELEM_absent_file:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-file"
- " element"));
- parent_dir = &TOP_DIR(rb);
- pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, parent_dir->pool);
- svn_path_add_component(pathbuf, name);
-
- SVN_ERR((*rb->editor->absent_file)(pathbuf->data,
- parent_dir->baton,
- parent_dir->pool));
- break;
-
- case ELEM_resource:
- att = svn_xml_get_attr_value("path", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing path attr in resource element"));
- svn_stringbuf_set(rb->current_wcprop_path, att);
- rb->in_resource = TRUE;
- break;
-
- case ELEM_open_directory:
- att = svn_xml_get_attr_value("rev", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing rev attr in open-directory"
- " element"));
- base = SVN_STR_TO_REV(att);
-
- if (DIR_DEPTH(rb) == 0)
- {
- /* pathbuf has to live for the whole edit! */
- pathbuf = svn_stringbuf_create("", rb->pool);
-
- /* During switch operations, we need to invalidate the
- tree's version resource URLs in case something goes
- wrong. */
- if (rb->is_switch && rb->ras->callbacks->invalidate_wc_props)
- {
- SVN_ERR(rb->ras->callbacks->invalidate_wc_props
- (rb->ras->callback_baton, rb->target,
- SVN_RA_NEON__LP_VSN_URL, rb->pool));
- }
-
- subpool = svn_pool_create(rb->pool);
- SVN_ERR((*rb->editor->open_root)(rb->edit_baton, base,
- subpool, &new_dir_baton));
-
- /* push the new baton onto the directory baton stack */
- push_dir(rb, new_dir_baton, pathbuf, subpool);
- }
- else
- {
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-directory"
- " element"));
- svn_stringbuf_set(rb->namestr, name);
-
- parent_dir = &TOP_DIR(rb);
- subpool = svn_pool_create(parent_dir->pool);
-
- pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
- svn_path_add_component(pathbuf, rb->namestr->data);
-
- SVN_ERR((*rb->editor->open_directory)(pathbuf->data,
- parent_dir->baton, base,
- subpool,
- &new_dir_baton));
-
- /* push the new baton onto the directory baton stack */
- push_dir(rb, new_dir_baton, pathbuf, subpool);
- }
-
- /* Property fetching is NOT implied in replacement. */
- TOP_DIR(rb).fetch_props = FALSE;
- break;
-
- case ELEM_add_directory:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-directory"
- " element"));
- svn_stringbuf_set(rb->namestr, name);
-
- att = svn_xml_get_attr_value("copyfrom-path", atts);
- if (att != NULL)
- {
- cpath = rb->cpathstr;
- svn_stringbuf_set(cpath, att);
-
- att = svn_xml_get_attr_value("copyfrom-rev", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing copyfrom-rev attr in"
- " add-directory element"));
- crev = SVN_STR_TO_REV(att);
- }
-
- parent_dir = &TOP_DIR(rb);
- subpool = svn_pool_create(parent_dir->pool);
-
- pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
- svn_path_add_component(pathbuf, rb->namestr->data);
-
- SVN_ERR((*rb->editor->add_directory)(pathbuf->data, parent_dir->baton,
- cpath ? cpath->data : NULL,
- crev, subpool,
- &new_dir_baton));
-
- /* push the new baton onto the directory baton stack */
- push_dir(rb, new_dir_baton, pathbuf, subpool);
-
- /* Property fetching is implied in addition. */
- TOP_DIR(rb).fetch_props = TRUE;
-
- bc_url = svn_xml_get_attr_value("bc-url", atts);
-
- /* If we are not in send-all mode, we're just told to fetch the
- props later. In that case, we can at least do a pre-emptive
- depth-1 propfind on the directory right now; this prevents
- individual propfinds on added-files later on, thus reducing
- the number of network turnarounds. */
- if ((! rb->receiving_all) && bc_url)
- {
- apr_hash_t *bc_children;
- SVN_ERR(svn_ra_neon__get_props(&bc_children,
- rb->ras,
- bc_url,
- SVN_RA_NEON__DEPTH_ONE,
- NULL, NULL /* allprops */,
- TOP_DIR(rb).pool));
-
- /* re-index the results into a more usable hash.
- bc_children maps bc-url->resource_t, but we want the
- dir_item_t's hash to map vc-url->resource_t. */
- if (bc_children)
- {
- apr_hash_index_t *hi;
- TOP_DIR(rb).children = apr_hash_make(TOP_DIR(rb).pool);
-
- for (hi = apr_hash_first(TOP_DIR(rb).pool, bc_children);
- hi; hi = apr_hash_next(hi))
- {
- void *val;
- svn_ra_neon__resource_t *rsrc;
- const svn_string_t *vc_url;
-
- apr_hash_this(hi, NULL, NULL, &val);
- rsrc = val;
-
- vc_url = apr_hash_get(rsrc->propset,
- SVN_RA_NEON__PROP_CHECKED_IN,
- APR_HASH_KEY_STRING);
- if (vc_url)
- apr_hash_set(TOP_DIR(rb).children,
- vc_url->data, vc_url->len,
- rsrc->propset);
- }
- }
- }
-
- break;
-
- case ELEM_open_file:
- att = svn_xml_get_attr_value("rev", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing rev attr in open-file"
- " element"));
- base = SVN_STR_TO_REV(att);
-
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-file"
- " element"));
- svn_stringbuf_set(rb->namestr, name);
-
- parent_dir = &TOP_DIR(rb);
- rb->file_pool = svn_pool_create(parent_dir->pool);
- rb->result_checksum = NULL;
-
- /* Add this file's name into the directory's path buffer. It will be
- removed in end_element() */
- svn_path_add_component(parent_dir->pathbuf, rb->namestr->data);
-
- SVN_ERR((*rb->editor->open_file)(parent_dir->pathbuf->data,
- parent_dir->baton, base,
- rb->file_pool,
- &rb->file_baton));
-
- /* Property fetching is NOT implied in replacement. */
- rb->fetch_props = FALSE;
-
- break;
-
- case ELEM_add_file:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-file"
- " element"));
- svn_stringbuf_set(rb->namestr, name);
-
- att = svn_xml_get_attr_value("copyfrom-path", atts);
- if (att != NULL)
- {
- cpath = rb->cpathstr;
- svn_stringbuf_set(cpath, att);
-
- att = svn_xml_get_attr_value("copyfrom-rev", atts);
- if (att == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing copyfrom-rev attr in add-file"
- " element"));
- crev = SVN_STR_TO_REV(att);
- }
-
- parent_dir = &TOP_DIR(rb);
- rb->file_pool = svn_pool_create(parent_dir->pool);
- rb->result_checksum = NULL;
-
- /* Add this file's name into the directory's path buffer. It will be
- removed in end_element() */
- svn_path_add_component(parent_dir->pathbuf, rb->namestr->data);
-
- SVN_ERR((*rb->editor->add_file)(parent_dir->pathbuf->data,
- parent_dir->baton,
- cpath ? cpath->data : NULL,
- crev, rb->file_pool,
- &rb->file_baton));
-
- /* Property fetching is implied in addition. */
- rb->fetch_props = TRUE;
-
- break;
-
- case ELEM_txdelta:
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
- <fetch-file>s and such) when *not* in "send-all" mode. As a
- client, we're smart enough to know that's wrong, so when not
- in "receiving-all" mode, we'll ignore <txdelta> tags
- altogether. */
- if (! rb->receiving_all)
- break;
-
- base_checksum = svn_xml_get_attr_value("base-checksum", atts);
-
- SVN_ERR((*rb->editor->apply_textdelta)(rb->file_baton,
- base_checksum,
- rb->file_pool,
- &(rb->whandler),
- &(rb->whandler_baton)));
-
- rb->svndiff_decoder = svn_txdelta_parse_svndiff(rb->whandler,
- rb->whandler_baton,
- TRUE, rb->file_pool);
-
- rb->base64_decoder = svn_base64_decode(rb->svndiff_decoder,
- rb->file_pool);
- break;
-
- case ELEM_set_prop:
- {
- const char *encoding = svn_xml_get_attr_value("encoding", atts);
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in set-prop element"));
- svn_stringbuf_set(rb->namestr, name);
- if (encoding)
- svn_stringbuf_set(rb->encoding, encoding);
- else
- svn_stringbuf_setempty(rb->encoding);
- }
-
- break;
-
- case ELEM_remove_prop:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in remove-prop element"));
- svn_stringbuf_set(rb->namestr, name);
-
- /* Removing a prop. */
- if (rb->file_baton == NULL)
- SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
- rb->namestr->data,
- NULL, TOP_DIR(rb).pool));
- else
- SVN_ERR(rb->editor->change_file_prop(rb->file_baton, rb->namestr->data,
- NULL, rb->file_pool));
- break;
-
- case ELEM_fetch_props:
- if (!rb->fetch_content)
- {
- /* If this is just a status check, the specifics of the
- property change are uninteresting. Simply call our
- editor function with bogus data so it registers a
- property mod. */
- svn_stringbuf_set(rb->namestr, SVN_PROP_PREFIX "BOGOSITY");
-
- if (rb->file_baton == NULL)
- SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
- rb->namestr->data,
- NULL, TOP_DIR(rb).pool));
- else
- SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
- rb->namestr->data,
- NULL, rb->file_pool));
- }
- else
- {
- /* Note that we need to fetch props for this... */
- if (rb->file_baton == NULL)
- TOP_DIR(rb).fetch_props = TRUE; /* ...directory. */
- else
- rb->fetch_props = TRUE; /* ...file. */
- }
- break;
-
- case ELEM_fetch_file:
- base_checksum = svn_xml_get_attr_value("base-checksum", atts);
- rb->result_checksum = NULL;
-
- /* If we aren't expecting to see the file contents inline, we
- should ignore server requests to fetch them.
-
- ### This conditional was added to counteract a little bug in
- Subversion 0.33.0's mod_dav_svn whereby both the <txdelta>
- and <fetch-file> tags were being transmitted. Someday, we
- should remove the conditional again to give the server the
- option of sending inline text-deltas for some files while
- telling the client to fetch others. */
- if (! rb->receiving_all)
- {
- /* assert: rb->href->len > 0 */
- SVN_ERR(simple_fetch_file(rb->ras,
- rb->href->data,
- TOP_DIR(rb).pathbuf->data,
- rb->fetch_content,
- rb->file_baton,
- base_checksum,
- rb->editor,
- rb->ras->callbacks->get_wc_prop,
- rb->ras->callback_baton,
- rb->file_pool));
- }
- break;
-
- case ELEM_delete_entry:
- name = svn_xml_get_attr_value("name", atts);
- if (name == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in delete-entry"
- " element"));
- svn_stringbuf_set(rb->namestr, name);
-
- att = svn_xml_get_attr_value("rev", atts);
- if (att) /* Not available on older repositories! */
- crev = SVN_STR_TO_REV(att);
-
- parent_dir = &TOP_DIR(rb);
-
- /* Pool use is a little non-standard here. When lots of items in the
- same directory get deleted each one will trigger a call to
- editor->delete_entry, but we don't have a pool that readily fits
- the usual iteration pattern and so memory use could grow without
- bound (see issue 1635). To avoid such growth we use a temporary,
- short-lived, pool. */
- subpool = svn_pool_create(parent_dir->pool);
-
- pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
- svn_path_add_component(pathbuf, rb->namestr->data);
-
- SVN_ERR((*rb->editor->delete_entry)(pathbuf->data,
- crev,
- parent_dir->baton,
- subpool));
- svn_pool_destroy(subpool);
- break;
-
- default:
- break;
- }
-
- *elem = elm->id;
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-add_node_props(report_baton_t *rb, apr_pool_t *pool)
-{
- svn_ra_neon__resource_t *rsrc = NULL;
- apr_hash_t *props = NULL;
-
- /* Do nothing if parsing a send-all-style report, because the properties
- already come inline. */
- if (rb->receiving_all)
- return SVN_NO_ERROR;
-
- /* Do nothing (else) if we aren't fetching content. */
- if (!rb->fetch_content)
- return SVN_NO_ERROR;
-
- if (rb->file_baton)
- {
- const char *lock_token = apr_hash_get(rb->lock_tokens,
- TOP_DIR(rb).pathbuf->data,
- TOP_DIR(rb).pathbuf->len);
-
- /* Workaround a buglet in older versions of mod_dav_svn in that it
- will not send remove-prop in the update report when a lock
- property disappears when send-all is false. */
- if (lock_token)
- {
- svn_lock_t *lock;
- SVN_ERR(svn_ra_neon__get_lock_internal(rb->ras, &lock,
- TOP_DIR(rb).pathbuf->data,
- pool));
- if (! (lock
- && lock->token
- && (strcmp(lock->token, lock_token) == 0)))
- SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
- SVN_PROP_ENTRY_LOCK_TOKEN,
- NULL, pool));
- }
-
- /* If we aren't supposed to be fetching props, don't. */
- if (! rb->fetch_props)
- return SVN_NO_ERROR;
-
- /* Check to see if your parent directory already has your props
- stored, possibly from a depth-1 propfind. Otherwise just do
- a propfind directly on the file url. */
- if ( ! ((TOP_DIR(rb).children)
- && (props = apr_hash_get(TOP_DIR(rb).children, rb->href->data,
- APR_HASH_KEY_STRING))) )
- {
- SVN_ERR(svn_ra_neon__get_props_resource(&rsrc,
- rb->ras,
- rb->href->data,
- NULL,
- NULL,
- pool));
- props = rsrc->propset;
- }
-
- SVN_ERR(add_props(props,
- rb->editor->change_file_prop,
- rb->file_baton,
- pool));
- }
- else
- {
- if (! TOP_DIR(rb).fetch_props)
- return SVN_NO_ERROR;
-
- /* Check to see if your props are already stored, possibly from
- a depth-1 propfind. Otherwise just do a propfind directly on
- the directory url. */
- if ( ! ((TOP_DIR(rb).children)
- && (props = apr_hash_get(TOP_DIR(rb).children,
- TOP_DIR(rb).vsn_url,
- APR_HASH_KEY_STRING))) )
- {
- SVN_ERR(svn_ra_neon__get_props_resource(&rsrc,
- rb->ras,
- TOP_DIR(rb).vsn_url,
- NULL,
- NULL,
- pool));
- props = rsrc->propset;
- }
-
- SVN_ERR(add_props(props,
- rb->editor->change_dir_prop,
- TOP_DIR(rb).baton,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-/* This implements the `svn_ra_neon__cdata_cb_t' prototype. */
-static svn_error_t *
-cdata_handler(void *userdata, int state, const char *cdata, size_t len)
-{
- report_baton_t *rb = userdata;
-
- switch(state)
- {
- case ELEM_href:
- case ELEM_set_prop:
- case ELEM_md5_checksum:
- case ELEM_version_name:
- case ELEM_creationdate:
- case ELEM_creator_displayname:
- svn_stringbuf_appendbytes(rb->cdata_accum, cdata, len);
- break;
-
- case ELEM_txdelta:
- {
- apr_size_t nlen = len;
-
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
- <fetch-file>s and such) when *not* in "send-all" mode. As a
- client, we're smart enough to know that's wrong, so when not
- in "receiving-all" mode, we'll ignore <txdelta> tags
- altogether. */
- if (! rb->receiving_all)
- break;
-
- SVN_ERR(svn_stream_write(rb->base64_decoder, cdata, &nlen));
- if (nlen != len)
- {
- /* Short write without associated error? "Can't happen." */
- return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
- _("Error writing to '%s': unexpected EOF"),
- rb->namestr->data);
- }
- }
- break;
- }
-
- return 0; /* no error */
-}
-
-/* This implements the `svn_ra_neon_endelm_cb_t' prototype. */
-static svn_error_t *
-end_element(void *userdata, int state,
- const char *nspace, const char *elt_name)
-{
- report_baton_t *rb = userdata;
- const svn_delta_editor_t *editor = rb->editor;
- const svn_ra_neon__xml_elm_t *elm;
-
- elm = svn_ra_neon__lookup_xml_elem(report_elements, nspace, elt_name);
-
- if (elm == NULL)
- return SVN_NO_ERROR;
-
- switch (elm->id)
- {
- case ELEM_resource:
- rb->in_resource = FALSE;
- break;
-
- case ELEM_update_report:
- /* End of report; close up the editor. */
- SVN_ERR((*rb->editor->close_edit)(rb->edit_baton, rb->pool));
- rb->edit_baton = NULL;
- break;
-
- case ELEM_add_directory:
- case ELEM_open_directory:
-
- /* fetch node props, unless this is the top dir and the real
- target of the operation is not the top dir. */
- if (! ((DIR_DEPTH(rb) == 1) && *rb->target))
- SVN_ERR(add_node_props(rb, TOP_DIR(rb).pool));
-
- /* Close the directory on top of the stack, and pop it. Also,
- destroy the subpool used exclusive by this directory and its
- children. */
- SVN_ERR((*rb->editor->close_directory)(TOP_DIR(rb).baton,
- TOP_DIR(rb).pool));
- svn_pool_destroy(TOP_DIR(rb).pool);
- apr_array_pop(rb->dirs);
- break;
-
- case ELEM_add_file:
- /* we wait until the close element to do the work. this allows us to
- retrieve the href before fetching. */
-
- /* fetch file */
- if (! rb->receiving_all)
- {
- SVN_ERR(simple_fetch_file(rb->ras,
- rb->href->data,
- TOP_DIR(rb).pathbuf->data,
- rb->fetch_content,
- rb->file_baton,
- NULL, /* no base checksum in an add */
- rb->editor,
- NULL, NULL, /* dav_prop callback */
- rb->file_pool));
-
- /* fetch node props as necessary. */
- SVN_ERR(add_node_props(rb, rb->file_pool));
- }
-
- /* close the file and mark that we are no longer operating on a file */
- SVN_ERR((*rb->editor->close_file)(rb->file_baton,
- rb->result_checksum,
- rb->file_pool));
- rb->file_baton = NULL;
-
- /* Yank this file out of the directory's path buffer. */
- svn_path_remove_component(TOP_DIR(rb).pathbuf);
- svn_pool_destroy(rb->file_pool);
- rb->file_pool = NULL;
- break;
-
- case ELEM_txdelta:
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
- <fetch-file>s and such) when *not* in "send-all" mode. As a
- client, we're smart enough to know that's wrong, so when not
- in "receiving-all" mode, we'll ignore <txdelta> tags
- altogether. */
- if (! rb->receiving_all)
- break;
-
- SVN_ERR(svn_stream_close(rb->base64_decoder));
- rb->whandler = NULL;
- rb->whandler_baton = NULL;
- rb->svndiff_decoder = NULL;
- rb->base64_decoder = NULL;
- break;
-
- case ELEM_open_file:
- /* fetch node props as necessary. */
- SVN_ERR(add_node_props(rb, rb->file_pool));
-
- /* close the file and mark that we are no longer operating on a file */
- SVN_ERR((*rb->editor->close_file)(rb->file_baton,
- rb->result_checksum,
- rb->file_pool));
- rb->file_baton = NULL;
-
- /* Yank this file out of the directory's path buffer. */
- svn_path_remove_component(TOP_DIR(rb).pathbuf);
- svn_pool_destroy(rb->file_pool);
- rb->file_pool = NULL;
- break;
-
- case ELEM_set_prop:
- {
- svn_string_t decoded_value;
- const svn_string_t *decoded_value_p;
- apr_pool_t *pool;
-
- if (rb->file_baton)
- pool = rb->file_pool;
- else
- pool = TOP_DIR(rb).pool;
-
- decoded_value.data = rb->cdata_accum->data;
- decoded_value.len = rb->cdata_accum->len;
-
- /* Determine the cdata encoding, if any. */
- if (svn_stringbuf_isempty(rb->encoding))
- {
- decoded_value_p = &decoded_value;
- }
- else if (strcmp(rb->encoding->data, "base64") == 0)
- {
- decoded_value_p = svn_base64_decode_string(&decoded_value, pool);
- svn_stringbuf_setempty(rb->encoding);
- }
- else
- {
- return svn_error_createf(SVN_ERR_XML_UNKNOWN_ENCODING, NULL,
- _("Unknown XML encoding: '%s'"),
- rb->encoding->data);
- }
-
- /* Set the prop. */
- if (rb->file_baton)
- {
- SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
- rb->namestr->data,
- decoded_value_p, pool));
- }
- else
- {
- SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
- rb->namestr->data,
- decoded_value_p, pool));
- }
- }
-
- svn_stringbuf_setempty(rb->cdata_accum);
- break;
-
- case ELEM_href:
- if (rb->fetch_content)
- /* record the href that we just found */
- SVN_ERR(svn_ra_neon__copy_href(rb->href, rb->cdata_accum->data,
- rb->scratch_pool));
-
- svn_stringbuf_setempty(rb->cdata_accum);
-
- /* do nothing if we aren't fetching content. */
- if (!rb->fetch_content)
- break;
-
- /* if we're within a <resource> tag, then just call the generic
- RA set_wcprop_callback directly; no need to use the
- update-editor. */
- if (rb->in_resource)
- {
- svn_string_t href_val;
- href_val.data = rb->href->data;
- href_val.len = rb->href->len;
-
- if (rb->ras->callbacks->set_wc_prop != NULL)
- SVN_ERR(rb->ras->callbacks->set_wc_prop
- (rb->ras->callback_baton,
- rb->current_wcprop_path->data,
- SVN_RA_NEON__LP_VSN_URL,
- &href_val,
- rb->scratch_pool));
- svn_pool_clear(rb->scratch_pool);
- }
- /* else we're setting a wcprop in the context of an editor drive. */
- else if (rb->file_baton == NULL)
- {
- /* Update the wcprop here, unless this is the top directory
- and the real target of this operation is something other
- than the top directory. */
- if (! ((DIR_DEPTH(rb) == 1) && *rb->target))
- {
- SVN_ERR(simple_store_vsn_url(rb->href->data, TOP_DIR(rb).baton,
- rb->editor->change_dir_prop,
- TOP_DIR(rb).pool));
-
- /* save away the URL in case a fetch-props arrives after all of
- the subdir processing. we will need this copy of the URL to
- fetch the properties (i.e. rb->href will be toast by then). */
- TOP_DIR(rb).vsn_url = apr_pmemdup(TOP_DIR(rb).pool,
- rb->href->data,
- rb->href->len + 1);
- }
- }
- else
- {
- SVN_ERR(simple_store_vsn_url(rb->href->data, rb->file_baton,
- rb->editor->change_file_prop,
- rb->file_pool));
- }
- break;
-
- case ELEM_md5_checksum:
- /* We only care about file checksums. */
- if (rb->file_baton)
- {
- rb->result_checksum = apr_pstrdup(rb->file_pool,
- rb->cdata_accum->data);
- }
- svn_stringbuf_setempty(rb->cdata_accum);
- break;
-
- case ELEM_version_name:
- case ELEM_creationdate:
- case ELEM_creator_displayname:
- {
- /* The name of the xml tag is the property that we want to set. */
- apr_pool_t *pool =
- rb->file_baton ? rb->file_pool : TOP_DIR(rb).pool;
- prop_setter_t setter =
- rb->file_baton ? editor->change_file_prop : editor->change_dir_prop;
- const char *name = apr_pstrcat(pool, elm->nspace, elm->name,
- (char *)NULL);
- void *baton = rb->file_baton ? rb->file_baton : TOP_DIR(rb).baton;
- svn_string_t valstr;
-
- valstr.data = rb->cdata_accum->data;
- valstr.len = rb->cdata_accum->len;
- SVN_ERR(set_special_wc_prop(name, &valstr, setter, baton, pool));
- svn_stringbuf_setempty(rb->cdata_accum);
- }
- break;
-
- default:
- break;
- }
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * reporter_set_path(void *report_baton,
- const char *path,
- svn_revnum_t revision,
- svn_depth_t depth,
- svn_boolean_t start_empty,
- const char *lock_token,
- apr_pool_t *pool)
-{
- report_baton_t *rb = report_baton;
- const char *entry;
- svn_stringbuf_t *qpath = NULL;
- const char *tokenstring = "";
- const char *depthstring = apr_psprintf(pool, "depth=\"%s\"",
- svn_depth_to_word(depth));
-
- if (lock_token)
- {
- tokenstring = apr_psprintf(pool, "lock-token=\"%s\"", lock_token);
- apr_hash_set(rb->lock_tokens,
- apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), path),
- APR_HASH_KEY_STRING,
- apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), lock_token));
- }
-
- svn_xml_escape_cdata_cstring(&qpath, path, pool);
- if (start_empty)
- entry = apr_psprintf(pool,
- "<S:entry rev=\"%ld\" %s %s"
- " start-empty=\"true\">%s</S:entry>" DEBUG_CR,
- revision, depthstring, tokenstring, qpath->data);
- else
- entry = apr_psprintf(pool,
- "<S:entry rev=\"%ld\" %s %s>"
- "%s</S:entry>" DEBUG_CR,
- revision, depthstring, tokenstring, qpath->data);
-
- return svn_error_trace(svn_io_file_write_full(rb->tmpfile, entry,
- strlen(entry), NULL, pool));
-}
-
-
-static svn_error_t * reporter_link_path(void *report_baton,
- const char *path,
- const char *url,
- svn_revnum_t revision,
- svn_depth_t depth,
- svn_boolean_t start_empty,
- const char *lock_token,
- apr_pool_t *pool)
-{
- report_baton_t *rb = report_baton;
- const char *entry;
- svn_stringbuf_t *qpath = NULL, *qlinkpath = NULL;
- const char *bc_relative;
- const char *tokenstring = "";
- const char *depthstring = apr_psprintf(pool, "depth=\"%s\"",
- svn_depth_to_word(depth));
-
- if (lock_token)
- {
- tokenstring = apr_psprintf(pool, "lock-token=\"%s\"", lock_token);
- apr_hash_set(rb->lock_tokens,
- apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), path),
- APR_HASH_KEY_STRING,
- apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), lock_token));
- }
-
- /* Convert the copyfrom_* url/rev "public" pair into a Baseline
- Collection (BC) URL that represents the revision -- and a
- relative path under that BC. */
- SVN_ERR(svn_ra_neon__get_baseline_info(NULL, &bc_relative, NULL, rb->ras,
- url, revision, pool));
-
-
- svn_xml_escape_cdata_cstring(&qpath, path, pool);
- svn_xml_escape_attr_cstring(&qlinkpath, bc_relative, pool);
- if (start_empty)
- entry = apr_psprintf(pool,
- "<S:entry rev=\"%ld\" %s %s"
- " linkpath=\"/%s\" start-empty=\"true\""
- ">%s</S:entry>" DEBUG_CR,
- revision, depthstring, tokenstring,
- qlinkpath->data, qpath->data);
- else
- entry = apr_psprintf(pool,
- "<S:entry rev=\"%ld\" %s %s"
- " linkpath=\"/%s\">%s</S:entry>" DEBUG_CR,
- revision, depthstring, tokenstring,
- qlinkpath->data, qpath->data);
-
- return svn_error_trace(svn_io_file_write_full(rb->tmpfile, entry,
- strlen(entry), NULL, pool));
-}
-
-
-static svn_error_t * reporter_delete_path(void *report_baton,
- const char *path,
- apr_pool_t *pool)
-{
- report_baton_t *rb = report_baton;
- const char *s;
- svn_stringbuf_t *qpath = NULL;
-
- svn_xml_escape_cdata_cstring(&qpath, path, pool);
- s = apr_psprintf(pool,
- "<S:missing>%s</S:missing>" DEBUG_CR,
- qpath->data);
-
- return svn_error_trace(svn_io_file_write_full(rb->tmpfile, s, strlen(s),
- NULL, pool));
-}
-
-
-static svn_error_t * reporter_abort_report(void *report_baton,
- apr_pool_t *pool)
-{
- report_baton_t *rb = report_baton;
-
- SVN_ERR(svn_io_file_close(rb->tmpfile, pool));
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * reporter_finish_report(void *report_baton,
- apr_pool_t *pool)
-{
- report_baton_t *rb = report_baton;
- svn_error_t *err;
- const char *report_target;
- apr_hash_t *request_headers = apr_hash_make(pool);
- apr_hash_set(request_headers, "Accept-Encoding", APR_HASH_KEY_STRING,
- "svndiff1;q=0.9,svndiff;q=0.8");
-
-
-#define SVN_RA_NEON__REPORT_TAIL "</S:update-report>" DEBUG_CR
- /* write the final closing gunk to our request body. */
- SVN_ERR(svn_io_file_write_full(rb->tmpfile,
- SVN_RA_NEON__REPORT_TAIL,
- sizeof(SVN_RA_NEON__REPORT_TAIL) - 1,
- NULL, pool));
-#undef SVN_RA_NEON__REPORT_TAIL
-
- /* get the editor process prepped */
- rb->dirs = apr_array_make(rb->pool, 5, sizeof(dir_item_t));
- rb->namestr = MAKE_BUFFER(rb->pool);
- rb->cpathstr = MAKE_BUFFER(rb->pool);
- rb->encoding = MAKE_BUFFER(rb->pool);
- rb->href = MAKE_BUFFER(rb->pool);
-
- /* Got HTTP v2 support? We'll report against the "me resource". */
- if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(rb->ras))
- {
- report_target = rb->ras->me_resource;
- }
- /* Else, get the VCC. (If this doesn't work out for us, don't
- forget to remove the tmpfile before returning the error.) */
- else if ((err = svn_ra_neon__get_vcc(&report_target, rb->ras,
- rb->ras->url->data, pool)))
- {
- /* We're done with the file. this should delete it. Note: it
- isn't a big deal if this line is never executed -- the pool
- will eventually get it. We're just being proactive here. */
- return svn_error_trace(
- svn_error_compose_create(err,
- svn_io_file_close(rb->tmpfile,
- pool)));
- }
-
- /* dispatch the REPORT. */
- err = svn_ra_neon__parsed_request(rb->ras, "REPORT", report_target,
- NULL, rb->tmpfile, NULL,
- start_element,
- cdata_handler,
- end_element,
- rb,
- request_headers, NULL,
- rb->spool_response, pool);
-
- /* We're done with the file. Proactively close/delete the thing. */
- SVN_ERR(svn_error_compose_create(err,
- svn_io_file_close(rb->tmpfile, pool)));
-
- /* We got the whole HTTP response thing done. *Whew*. Our edit
- baton should have been closed by now, so return a failure if it
- hasn't been. */
- if (rb->edit_baton)
- {
- return svn_error_createf(
- SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("REPORT response handling failed to complete the editor drive"));
- }
-
- return SVN_NO_ERROR;
-}
-
-static const svn_ra_reporter3_t ra_neon_reporter = {
- reporter_set_path,
- reporter_delete_path,
- reporter_link_path,
- reporter_finish_report,
- reporter_abort_report
-};
-
-
-/* Make a generic REPORTER / REPORT_BATON for reporting the state of
- the working copy against REVISION during updates or status checks.
- The server will drive EDITOR / EDIT_BATON to indicate how to
- transform the working copy into the requested target.
-
- SESSION is the RA session in use. TARGET is an optional single
- path component will restrict the scope of the operation to an entry
- in the directory represented by the SESSION's URL, or empty if the
- entire directory is meant to be the target.
-
- DEPTH is the requested depth of the operation. It will be
- transmitted to the server, which (if it understands depths) can use
- the information to limit the information it sends back. Also store
- DEPTH in the REPORT_BATON: that way, if the server is old and does
- not understand depth requests, the client can notice this when the
- response starts streaming in, and adjust accordingly (as of this
- writnig, by wrapping REPORTER->editor and REPORTER->edit_baton in a
- filtering editor that simply tosses out the data the client doesn't
- want).
-
- If SEND_COPYFROM_ARGS is set, then ask the server to transmit
- copyfrom args in add_file() in add_directory() calls.
-
- If IGNORE_ANCESTRY is set, the server will transmit real diffs
- between the working copy and the target even if those objects are
- not historically related. Otherwise, the response will generally
- look like a giant delete followed by a giant add.
-
- RESOURCE_WALK controls whether to ask the DAV server to supply an
- entire tree's worth of version-resource-URL working copy cache
- updates.
-
- FETCH_CONTENT is used by the REPORT response parser to determine
- whether it should bother getting the contents of files represented
- in the delta response (of if a directory delta is all that is of
- interest).
-
- If SEND_ALL is set, the server will be asked to embed contents into
- the main response.
-
- If SPOOL_RESPONSE is set, the REPORT response will be cached to
- disk in a tmpfile (in full), then read back and parsed.
-
- Oh, and do all this junk in POOL. */
-static svn_error_t *
-make_reporter(svn_ra_session_t *session,
- const svn_ra_reporter3_t **reporter,
- void **report_baton,
- svn_revnum_t revision,
- const char *target,
- const char *dst_path,
- svn_depth_t depth,
- svn_boolean_t send_copyfrom_args,
- svn_boolean_t ignore_ancestry,
- svn_boolean_t resource_walk,
- const svn_delta_editor_t *editor,
- void *edit_baton,
- svn_boolean_t fetch_content,
- svn_boolean_t send_all,
- svn_boolean_t spool_response,
- apr_pool_t *pool)
-{
- svn_ra_neon__session_t *ras = session->priv;
- report_baton_t *rb;
- const char *s;
- svn_stringbuf_t *xml_s;
- const svn_delta_editor_t *filter_editor;
- void *filter_baton;
- svn_boolean_t has_target = *target != '\0';
- svn_boolean_t server_supports_depth;
-
- SVN_ERR(svn_ra_neon__has_capability(session, &server_supports_depth,
- SVN_RA_CAPABILITY_DEPTH, pool));
- /* We can skip the depth filtering when the user requested
- depth_files or depth_infinity because the server will
- transmit the right stuff anyway. */
- if ((depth != svn_depth_files)
- && (depth != svn_depth_infinity)
- && ! server_supports_depth)
- {
- SVN_ERR(svn_delta_depth_filter_editor(&filter_editor,
- &filter_baton,
- editor,
- edit_baton,
- depth,
- has_target,
- pool));
- editor = filter_editor;
- edit_baton = filter_baton;
- }
-
- rb = apr_pcalloc(pool, sizeof(*rb));
- rb->ras = ras;
- rb->pool = pool;
- rb->scratch_pool = svn_pool_create(pool);
- rb->editor = editor;
- rb->edit_baton = edit_baton;
- rb->fetch_content = fetch_content;
- rb->in_resource = FALSE;
- rb->current_wcprop_path = svn_stringbuf_create("", pool);
- rb->is_switch = dst_path != NULL;
- rb->target = target;
- rb->receiving_all = FALSE;
- rb->spool_response = spool_response;
- rb->whandler = NULL;
- rb->whandler_baton = NULL;
- rb->svndiff_decoder = NULL;
- rb->base64_decoder = NULL;
- rb->cdata_accum = svn_stringbuf_create("", pool);
- rb->send_copyfrom_args = send_copyfrom_args;
- rb->lock_tokens = apr_hash_make(pool);
-
- /* Neon "pulls" request body content from the caller. The reporter is
- organized where data is "pushed" into self. To match these up, we use
- an intermediate file -- push data into the file, then let Neon pull
- from the file.
-
- Note: one day we could spin up a thread and use a pipe between this
- code and Neon. We write to a pipe, Neon reads from the pipe. Each
- thread can block on the pipe, waiting for the other to complete its
- work.
- */
-
- /* Create a temp file in the system area to hold the contents. Note that
- we need a file since we will be rewinding it. The file will be closed
- and deleted when the pool is cleaned up. */
- SVN_ERR(svn_io_open_unique_file3(&rb->tmpfile, NULL, NULL,
- svn_io_file_del_on_pool_cleanup,
- pool, pool));
-
- /* prep the file */
- s = apr_psprintf(pool, "<S:update-report send-all=\"%s\" xmlns:S=\""
- SVN_XML_NAMESPACE "\">" DEBUG_CR,
- send_all ? "true" : "false");
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
-
- /* always write the original source path. this is part of the "new
- style" update-report syntax. if the tmpfile is used in an "old
- style' update-report request, older servers will just ignore this
- unknown xml element. */
- xml_s = NULL;
- svn_xml_escape_cdata_cstring(&xml_s, ras->url->data, pool);
- s = apr_psprintf(pool, "<S:src-path>%s</S:src-path>" DEBUG_CR, xml_s->data);
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
-
- /* an invalid revnum means "latest". we can just omit the target-revision
- element in that case. */
- if (SVN_IS_VALID_REVNUM(revision))
- {
- s = apr_psprintf(pool,
- "<S:target-revision>%ld</S:target-revision>" DEBUG_CR,
- revision);
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
- }
-
- /* Pre-0.36 servers don't like to see an empty target string. */
- if (*target)
- {
- xml_s = NULL;
- svn_xml_escape_cdata_cstring(&xml_s, target, pool);
- s = apr_psprintf(pool, "<S:update-target>%s</S:update-target>" DEBUG_CR,
- xml_s->data);
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
- }
-
-
- /* A NULL dst_path is also no problem; this is only passed during a
- 'switch' operation. If NULL, we don't mention it in the custom
- report, and mod_dav_svn automatically runs dir_delta() on two
- identical paths. */
- if (dst_path)
- {
- xml_s = NULL;
- svn_xml_escape_cdata_cstring(&xml_s, dst_path, pool);
- s = apr_psprintf(pool, "<S:dst-path>%s</S:dst-path>" DEBUG_CR,
- xml_s->data);
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
- }
-
- /* Old servers know "recursive" but not "depth"; help them DTRT. */
- if (depth == svn_depth_files || depth == svn_depth_empty)
- {
- const char *data = "<S:recursive>no</S:recursive>" DEBUG_CR;
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
- NULL, pool));
- }
-
- /* mod_dav_svn defaults to svn_depth_infinity, but we always send anyway. */
- {
- s = apr_psprintf(pool, "<S:depth>%s</S:depth>" DEBUG_CR,
- svn_depth_to_word(depth));
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
- }
-
- /* mod_dav_svn will use ancestry in diffs unless it finds this element. */
- if (ignore_ancestry)
- {
- const char *data = "<S:ignore-ancestry>yes</S:ignore-ancestry>" DEBUG_CR;
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
- NULL, pool));
- }
-
- /* mod_dav_svn 1.5 and later won't send copyfrom args unless it
- finds this element. older mod_dav_svn modules should just
- ignore the unknown element. */
- if (send_copyfrom_args)
- {
- const char *data =
- "<S:send-copyfrom-args>yes</S:send-copyfrom-args>" DEBUG_CR;
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
- NULL, pool));
- }
-
- /* If we want a resource walk to occur, note that now. */
- if (resource_walk)
- {
- const char *data = "<S:resource-walk>yes</S:resource-walk>" DEBUG_CR;
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
- NULL, pool));
- }
-
- /* When in 'send-all' mode, mod_dav_svn will assume that it should
- calculate and transmit real text-deltas (instead of empty windows
- that merely indicate "text is changed") unless it finds this
- element. When not in 'send-all' mode, mod_dav_svn will never
- send text-deltas at all.
-
- NOTE: Do NOT count on servers actually obeying this, as some exist
- which obey send-all, but do not check for this directive at all! */
- if (send_all && (! fetch_content))
- {
- const char *data = "<S:text-deltas>no</S:text-deltas>" DEBUG_CR;
- SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
- NULL, pool));
- }
-
- *reporter = &ra_neon_reporter;
- *report_baton = rb;
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t * svn_ra_neon__do_update(svn_ra_session_t *session,
- const svn_ra_reporter3_t **reporter,
- void **report_baton,
- svn_revnum_t revision_to_update_to,
- const char *update_target,
- svn_depth_t depth,
- svn_boolean_t send_copyfrom_args,
- const svn_delta_editor_t *wc_update,
- void *wc_update_baton,
- apr_pool_t *pool)
-{
- return make_reporter(session,
- reporter,
- report_baton,
- revision_to_update_to,
- update_target,
- NULL,
- depth,
- send_copyfrom_args,
- FALSE,
- FALSE,
- wc_update,
- wc_update_baton,
- TRUE, /* fetch_content */
- TRUE, /* send_all */
- FALSE, /* spool_response */
- pool);
-}
-
-
-svn_error_t * svn_ra_neon__do_status(svn_ra_session_t *session,
- const svn_ra_reporter3_t **reporter,
- void **report_baton,
- const char *status_target,
- svn_revnum_t revision,
- svn_depth_t depth,
- const svn_delta_editor_t *wc_status,
- void *wc_status_baton,
- apr_pool_t *pool)
-{
- return make_reporter(session,
- reporter,
- report_baton,
- revision,
- status_target,
- NULL,
- depth,
- FALSE,
- FALSE,
- FALSE,
- wc_status,
- wc_status_baton,
- FALSE, /* fetch_content */
- TRUE, /* send_all */
- FALSE, /* spool_response */
- pool);
-}
-
-
-svn_error_t * svn_ra_neon__do_switch(svn_ra_session_t *session,
- const svn_ra_reporter3_t **reporter,
- void **report_baton,
- svn_revnum_t revision_to_update_to,
- const char *update_target,
- svn_depth_t depth,
- const char *switch_url,
- const svn_delta_editor_t *wc_update,
- void *wc_update_baton,
- apr_pool_t *pool)
-{
- return make_reporter(session,
- reporter,
- report_baton,
- revision_to_update_to,
- update_target,
- switch_url,
- depth,
- FALSE, /* ### TODO(sussman): no copyfrom args */
- TRUE,
- FALSE, /* ### Disabled, pre-1.2 servers sometimes
- return incorrect resource-walk data */
- wc_update,
- wc_update_baton,
- TRUE, /* fetch_content */
- TRUE, /* send_all */
- FALSE, /* spool_response */
- pool);
-}
-
-
-svn_error_t * svn_ra_neon__do_diff(svn_ra_session_t *session,
- const svn_ra_reporter3_t **reporter,
- void **report_baton,
- svn_revnum_t revision,
- const char *diff_target,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- svn_boolean_t text_deltas,
- const char *versus_url,
- const svn_delta_editor_t *wc_diff,
- void *wc_diff_baton,
- apr_pool_t *pool)
-{
- return make_reporter(session,
- reporter,
- report_baton,
- revision,
- diff_target,
- versus_url,
- depth,
- FALSE,
- ignore_ancestry,
- FALSE,
- wc_diff,
- wc_diff_baton,
- text_deltas, /* fetch_content */
- FALSE, /* send_all */
- TRUE, /* spool_response */
- pool);
-}