summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_neon/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_neon/commit.c')
-rw-r--r--subversion/libsvn_ra_neon/commit.c1631
1 files changed, 0 insertions, 1631 deletions
diff --git a/subversion/libsvn_ra_neon/commit.c b/subversion/libsvn_ra_neon/commit.c
deleted file mode 100644
index ce05b60..0000000
--- a/subversion/libsvn_ra_neon/commit.c
+++ /dev/null
@@ -1,1631 +0,0 @@
-/*
- * commit.c : routines for committing changes to the server
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- */
-
-
-
-#include <apr_pools.h>
-#include <apr_hash.h>
-#include <apr_uuid.h>
-
-#define APR_WANT_STDIO
-#define APR_WANT_STRFUNC
-#include <apr_want.h>
-
-#include "svn_pools.h"
-#include "svn_error.h"
-#include "svn_delta.h"
-#include "svn_io.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_props.h"
-
-#include "svn_private_config.h"
-
-#include "ra_neon.h"
-
-
-#define APPLY_TO_VERSION "<D:apply-to-version/>"
-
-/*
- * version_rsrc_t: identify the relevant pieces of a resource on the server
- *
- * NOTE: If you tweak this structure, please update dup_resource() to
- * ensure that it continues to create complete deep copies!
- */
-typedef struct version_rsrc_t
-{
- svn_revnum_t revision; /* resource's revision, or SVN_INVALID_REVNUM
- if it's new or is the HEAD */
- const char *url; /* public/viewable/original resource URL */
- const char *vsn_url; /* version resource URL that we stored
- locally; NULL if this is a just-added resource */
- const char *wr_url; /* working resource URL for this resource;
- NULL for resources not (yet) checked out */
- const char *name; /* basename of the resource */
- apr_pool_t *pool; /* pool in which this resource is allocated */
-
-} version_rsrc_t;
-
-
-typedef struct commit_ctx_t
-{
- /* Pool for the whole of the commit context. */
- apr_pool_t *pool;
-
- /* Pointer to the RA session baton. */
- svn_ra_neon__session_t *ras;
-
- /* Commit anchor repository relpath. */
- const char *anchor_relpath;
-
- /* A hash of revision properties (log messages, etc.) we need to set
- on the commit transaction. */
- apr_hash_t *revprop_table;
-
- /* A hash mapping svn_string_t * paths (those which are valid as
- target in the MERGE response) to svn_node_kind_t kinds. */
- apr_hash_t *valid_targets;
-
- /* The (potential) author of this commit. */
- const char *user;
-
- /* The commit callback and baton */
- svn_commit_callback2_t callback;
- void *callback_baton;
-
- /* The hash of lock tokens owned by the working copy, and whether
- or not to keep them after this commit is complete. */
- apr_hash_t *lock_tokens;
- svn_boolean_t keep_locks;
-
- /* HTTP v2 stuff */
- const char *txn_url; /* txn URL (!svn/txn/TXN_NAME) */
- const char *txn_root_url; /* commit anchor txn root URL */
-
- /* HTTP v1 stuff (only valid when 'txn_url' is NULL) */
- const char *activity_url; /* activity base URL... */
-
-} commit_ctx_t;
-
-
-/* Are we using HTTPv2 semantics for this commit? */
-#define USING_HTTPV2_COMMIT_SUPPORT(commit_ctx) ((commit_ctx)->txn_url != NULL)
-
-
-typedef struct put_baton_t
-{
- apr_file_t *tmpfile; /* may be NULL for content-less file */
- svn_stringbuf_t *fname; /* may be NULL for content-less file */
- const char *base_checksum; /* hex md5 of base text; may be null */
- apr_off_t progress;
- svn_ra_neon__session_t *ras;
- apr_pool_t *pool;
-} put_baton_t;
-
-typedef struct resource_baton_t
-{
- commit_ctx_t *cc;
- version_rsrc_t *rsrc;
- svn_revnum_t base_revision; /* base revision */
- apr_hash_t *prop_changes; /* name/values pairs of new/changed properties. */
- apr_array_header_t *prop_deletes; /* names of properties to delete. */
- svn_boolean_t created; /* set if this is an add rather than an update */
- svn_boolean_t copied; /* set if this object was copied */
- apr_pool_t *pool; /* the pool from open_foo() / add_foo() */
- put_baton_t *put_baton; /* baton for this file's PUT request */
- const char *token; /* file's lock token, if available */
- const char *txn_root_url; /* URL under !svn/txr/ (HTTPv2 only) */
- const char *local_relpath; /* path relative to the root of the commit
- (used for the get_wc_prop() and push_wc_prop()
- callbacks). */
-} resource_baton_t;
-
-/* this property will be fetched from the server when we don't find it
- cached in the WC property store. */
-static const ne_propname fetch_props[] =
-{
- { "DAV:", "checked-in" },
- { NULL }
-};
-
-static const ne_propname log_message_prop = { SVN_DAV_PROP_NS_SVN, "log" };
-
-/* Return a deep copy of BASE allocated from POOL. */
-static version_rsrc_t * dup_resource(version_rsrc_t *base, apr_pool_t *pool)
-{
- version_rsrc_t *rsrc = apr_pcalloc(pool, sizeof(*rsrc));
- rsrc->revision = base->revision;
- rsrc->url = base->url ?
- apr_pstrdup(pool, base->url) : NULL;
- rsrc->vsn_url = base->vsn_url ?
- apr_pstrdup(pool, base->vsn_url) : NULL;
- rsrc->wr_url = base->wr_url ?
- apr_pstrdup(pool, base->wr_url) : NULL;
- rsrc->name = base->name ?
- apr_pstrdup(pool, base->name) : NULL;
- rsrc->pool = pool;
- return rsrc;
-}
-
-/* Get the version resource URL for RSRC, storing it in
- RSRC->vsn_url. Use POOL for all temporary allocations. */
-static svn_error_t * get_version_url(commit_ctx_t *cc,
- const char *local_relpath,
- const version_rsrc_t *parent,
- version_rsrc_t *rsrc,
- svn_boolean_t force,
- apr_pool_t *pool)
-{
- svn_ra_neon__resource_t *propres;
- const char *url;
- const svn_string_t *url_str;
-
- if (!force)
- {
- if (cc->ras->callbacks->get_wc_prop != NULL)
- {
- const svn_string_t *vsn_url_value;
-
- SVN_ERR(cc->ras->callbacks->get_wc_prop(cc->ras->callback_baton,
- local_relpath,
- SVN_RA_NEON__LP_VSN_URL,
- &vsn_url_value,
- pool));
- if (vsn_url_value != NULL)
- {
- rsrc->vsn_url = apr_pstrdup(rsrc->pool, vsn_url_value->data);
- return SVN_NO_ERROR;
- }
- }
-
- /* If we know the version resource URL of the parent and it is
- the same revision as RSRC, use that as a base to calculate
- the version resource URL of RSRC. */
- if (parent && parent->vsn_url && parent->revision == rsrc->revision)
- {
- rsrc->vsn_url = svn_path_url_add_component2(parent->vsn_url,
- rsrc->name,
- rsrc->pool);
- return SVN_NO_ERROR;
- }
-
- /* whoops. it wasn't there. go grab it from the server. */
- }
-
- if (rsrc->revision == SVN_INVALID_REVNUM)
- {
- /* We aren't trying to get a specific version -- use the HEAD. We
- fetch the version URL from the public URL. */
- url = rsrc->url;
- }
- else
- {
- const char *bc_url;
- const char *bc_relative;
-
- /* The version URL comes from a resource in the Baseline Collection. */
- SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, NULL,
- cc->ras, rsrc->url,
- rsrc->revision, pool));
- url = svn_path_url_add_component2(bc_url, bc_relative, pool);
- }
-
- /* Get the DAV:checked-in property, which contains the URL of the
- Version Resource */
- SVN_ERR(svn_ra_neon__get_props_resource(&propres, cc->ras, url,
- NULL, fetch_props, pool));
- url_str = apr_hash_get(propres->propset,
- SVN_RA_NEON__PROP_CHECKED_IN,
- APR_HASH_KEY_STRING);
- if (url_str == NULL)
- {
- /* ### need a proper SVN_ERR here */
- return svn_error_create(APR_EGENERAL, NULL,
- _("Could not fetch the Version Resource URL "
- "(needed during an import or when it is "
- "missing from the local, cached props)"));
- }
-
- /* ensure we get the proper lifetime for this URL since it is going into
- a resource object. */
- rsrc->vsn_url = apr_pstrdup(rsrc->pool, url_str->data);
-
- if (cc->ras->callbacks->push_wc_prop != NULL)
- {
- /* Now we can store the new version-url. */
- SVN_ERR(cc->ras->callbacks->push_wc_prop(cc->ras->callback_baton,
- local_relpath,
- SVN_RA_NEON__LP_VSN_URL,
- url_str,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-/* When FORCE is true, then we force a query to the server, ignoring any
- cached property. */
-static svn_error_t * get_activity_collection(commit_ctx_t *cc,
- const svn_string_t **collection,
- svn_boolean_t force,
- apr_pool_t *pool)
-{
- if (!force && cc->ras->callbacks->get_wc_prop != NULL)
- {
- /* with a get_wc_prop, we can just ask for the activity URL from the
- property store. */
-
- /* get the URL where we should create activities */
- SVN_ERR(cc->ras->callbacks->get_wc_prop(cc->ras->callback_baton,
- "",
- SVN_RA_NEON__LP_ACTIVITY_COLL,
- collection,
- pool));
-
- if (*collection != NULL)
- {
- /* the property was there. return it. */
- return SVN_NO_ERROR;
- }
-
- /* property not found for some reason. get it from the server. */
- }
-
- /* use our utility function to fetch the activity URL */
- SVN_ERR(svn_ra_neon__get_activity_collection(collection,
- cc->ras,
- pool));
-
- if (cc->ras->callbacks->push_wc_prop != NULL)
- {
- /* save the (new) activity collection URL into the directory */
- SVN_ERR(cc->ras->callbacks->push_wc_prop(cc->ras->callback_baton,
- "",
- SVN_RA_NEON__LP_ACTIVITY_COLL,
- *collection,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * create_activity(commit_ctx_t *cc,
- apr_pool_t *pool)
-{
- const svn_string_t * activity_collection;
- const char *uuid_buf = svn_uuid_generate(pool);
- int code;
- const char *url;
-
- /* get the URL where we'll create activities, construct the URL for
- the activity, and create the activity. The URL for our activity
- will be ACTIVITY_COLL/UUID */
- SVN_ERR(get_activity_collection(cc, &activity_collection, FALSE, pool));
- url = svn_path_url_add_component2(activity_collection->data,
- uuid_buf, pool);
- SVN_ERR(svn_ra_neon__simple_request(&code, cc->ras,
- "MKACTIVITY", url, NULL, NULL,
- 201 /* Created */,
- 404 /* Not Found */, pool));
-
- /* if we get a 404, then it generally means that the cached activity
- collection no longer exists. Retry the sequence, but force a query
- to the server for the activity collection. */
- if (code == 404)
- {
- SVN_ERR(get_activity_collection(cc, &activity_collection, TRUE, pool));
- url = svn_path_url_add_component2(activity_collection->data,
- uuid_buf, pool);
- SVN_ERR(svn_ra_neon__simple_request(&code, cc->ras,
- "MKACTIVITY", url, NULL, NULL,
- 201, 0, pool));
- }
-
- cc->activity_url = apr_pstrdup(cc->pool, url);
-
- return SVN_NO_ERROR;
-}
-
-/* Add a child resource. POOL should be as "temporary" as possible,
- but probably not as far as requiring a new temp pool. */
-static svn_error_t * add_child(version_rsrc_t **child,
- commit_ctx_t *cc,
- const version_rsrc_t *parent,
- const char *parent_local_relpath,
- const char *name,
- int created,
- svn_revnum_t revision,
- apr_pool_t *pool)
-{
- version_rsrc_t *rsrc;
-
- /* ### todo: This from Yoshiki Hayashi <yoshiki@xemacs.org>:
-
- Probably created flag in add_child can be removed because
- revision is valid => created is false
- revision is invalid => created is true
- */
-
- rsrc = apr_pcalloc(pool, sizeof(*rsrc));
- rsrc->pool = pool;
- rsrc->revision = revision;
- rsrc->name = name;
- rsrc->url = svn_path_url_add_component2(parent->url, name, pool);
-
- /* Case 1: the resource is truly "new". Either it was added as a
- completely new object, or implicitly created via a COPY. Either
- way, it has no VR URL anywhere. However, we *can* derive its WR
- URL by the rules of deltaV: "copy structure is preserved below
- the WR you COPY to." */
- if (created || (parent->vsn_url == NULL))
- {
- rsrc->wr_url = svn_path_url_add_component2(parent->wr_url, name, pool);
- }
- /* Case 2: the resource is already under version-control somewhere.
- This means it has a VR URL already, and the WR URL won't exist
- until it's "checked out". */
- else
- {
- SVN_ERR(get_version_url(cc, svn_relpath_join(parent_local_relpath,
- name, pool),
- parent, rsrc, FALSE, pool));
- }
-
- *child = rsrc;
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * do_checkout(commit_ctx_t *cc,
- const char *vsn_url,
- svn_boolean_t allow_404,
- const char *token,
- svn_boolean_t is_vcc,
- int *code,
- const char **locn,
- apr_pool_t *pool)
-{
- svn_ra_neon__request_t *request;
- const char *body;
- apr_hash_t *extra_headers = NULL;
- svn_error_t *err = SVN_NO_ERROR;
-
- /* assert: vsn_url != NULL */
-
- /* ### send a CHECKOUT resource on vsn_url; include cc->activity_url;
- ### place result into res->wr_url and return it */
-
- /* create/prep the request */
- SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "CHECKOUT", vsn_url,
- pool));
-
- /* ### store this into cc to avoid pool growth */
- body = apr_psprintf(request->pool,
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<D:checkout xmlns:D=\"DAV:\">"
- "<D:activity-set>"
- "<D:href>%s</D:href>"
- "</D:activity-set>%s</D:checkout>",
- cc->activity_url,
- is_vcc ? APPLY_TO_VERSION: "");
-
- if (token)
- {
- extra_headers = apr_hash_make(request->pool);
- svn_ra_neon__set_header(extra_headers, "If",
- apr_psprintf(request->pool, "(<%s>)", token));
- }
-
- /* run the request and get the resulting status code (and svn_error_t) */
- err = svn_ra_neon__request_dispatch(code, request, extra_headers, body,
- 201 /* Created */,
- allow_404 ? 404 /* Not Found */ : 0,
- pool);
- if (err)
- goto cleanup;
-
- if (allow_404 && *code == 404 && request->err)
- {
- svn_error_clear(request->err);
- request->err = SVN_NO_ERROR;
- }
-
- *locn = svn_ra_neon__request_get_location(request, pool);
-
- cleanup:
- svn_ra_neon__request_destroy(request);
-
- return err;
-}
-
-
-static svn_error_t * checkout_resource(commit_ctx_t *cc,
- const char *local_relpath,
- version_rsrc_t *rsrc,
- svn_boolean_t allow_404,
- const char *token,
- svn_boolean_t is_vcc,
- apr_pool_t *pool)
-{
- int code;
- const char *locn = NULL;
- ne_uri parse;
- svn_error_t *err;
-
- if (rsrc->wr_url != NULL)
- {
- /* already checked out! */
- return NULL;
- }
-
- /* check out the Version Resource */
- err = do_checkout(cc, rsrc->vsn_url, allow_404, token,
- is_vcc, &code, &locn, pool);
-
- /* possibly run the request again, with a re-fetched Version Resource */
- if (err == NULL && allow_404 && code == 404)
- {
- locn = NULL;
-
- /* re-fetch, forcing a query to the server */
- SVN_ERR(get_version_url(cc, local_relpath, NULL, rsrc, TRUE, pool));
-
- /* do it again, but don't allow a 404 this time */
- err = do_checkout(cc, rsrc->vsn_url, FALSE, token,
- is_vcc, &code, &locn, pool);
- }
-
- /* special-case when conflicts occur */
- if (err)
- {
- /* ### TODO: it's a shame we don't have the full path from the
- ### root of the drive here, nor the type of the resource.
- ### Because we lack this information, the error message is
- ### overly generic. See issue #2740. */
- if (err->apr_err == SVN_ERR_FS_CONFLICT)
- return svn_error_createf
- (SVN_ERR_FS_OUT_OF_DATE, err,
- _("File or directory '%s' is out of date; try updating"),
- local_relpath);
- return err;
- }
-
- /* we got the header, right? */
- if (locn == NULL)
- return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("The CHECKOUT response did not contain a "
- "'Location:' header"));
-
- /* The location is an absolute URI. We want just the path portion. */
- /* ### what to do with the rest? what if it points somewhere other
- ### than the current session? */
- if (ne_uri_parse(locn, &parse) != 0)
- {
- ne_uri_free(&parse);
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Unable to parse URL '%s'"), locn);
- }
-
- rsrc->wr_url = apr_pstrdup(rsrc->pool, parse.path);
- ne_uri_free(&parse);
-
- return SVN_NO_ERROR;
-}
-
-static void record_prop_change(apr_pool_t *pool,
- resource_baton_t *r,
- const char *name,
- const svn_string_t *value)
-{
- /* copy the name into the pool so we get the right lifetime (who knows
- what the caller will do with it) */
- name = apr_pstrdup(pool, name);
-
- if (value)
- {
- /* changed/new property */
- if (r->prop_changes == NULL)
- r->prop_changes = apr_hash_make(pool);
- apr_hash_set(r->prop_changes, name, APR_HASH_KEY_STRING,
- svn_string_dup(value, pool));
- }
- else
- {
- /* deleted property. */
- if (r->prop_deletes == NULL)
- r->prop_deletes = apr_array_make(pool, 5, sizeof(char *));
- APR_ARRAY_PUSH(r->prop_deletes, const char *) = name;
- }
-}
-
-/* Send a Neon COPY request to the location identified by
- COPYFROM_PATH and COPYFROM_REVISION, using COPY_DST_URL as the
- "Destination" of that copy. */
-static svn_error_t * copy_resource(svn_ra_neon__session_t *ras,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- const char *copy_dst_url,
- apr_pool_t *scratch_pool)
-{
- const char *bc_url;
- const char *bc_relative;
- const char *copy_src_url;
-
- SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, NULL,
- ras, copyfrom_path,
- copyfrom_revision, scratch_pool));
- copy_src_url = svn_path_url_add_component2(bc_url, bc_relative,
- scratch_pool);
- SVN_ERR(svn_ra_neon__copy(ras, 1 /* overwrite */, SVN_RA_NEON__DEPTH_INFINITE,
- copy_src_url, copy_dst_url, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * do_proppatch(resource_baton_t *rb,
- apr_pool_t *pool)
-{
- apr_hash_t *extra_headers = apr_hash_make(pool);
- const char *proppatch_target =
- USING_HTTPV2_COMMIT_SUPPORT(rb->cc) ? rb->txn_root_url : rb->rsrc->wr_url;
-
- if (SVN_IS_VALID_REVNUM(rb->base_revision))
- svn_ra_neon__set_header(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
- apr_psprintf(pool, "%ld", rb->base_revision));
-
- if (rb->token)
- apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING,
- apr_psprintf(pool, "(<%s>)", rb->token));
-
- return svn_error_trace(svn_ra_neon__do_proppatch(rb->cc->ras,
- proppatch_target,
- rb->prop_changes,
- rb->prop_deletes,
- NULL, extra_headers,
- pool));
-}
-
-
-static void
-add_valid_target(commit_ctx_t *cc,
- const char *path,
- enum svn_recurse_kind kind)
-{
- apr_hash_t *hash = cc->valid_targets;
- svn_string_t *path_str = svn_string_create(path, apr_hash_pool_get(hash));
- apr_hash_set(hash, path_str->data, path_str->len, (void*)kind);
-}
-
-
-/* Helper func for commit_delete_entry. Find all keys in LOCK_TOKENS
- which are children of DIR. Returns the keys (and their vals) in
- CHILD_TOKENS. No keys or values are reallocated or dup'd. If no
- keys are children, then return an empty hash. Use POOL to allocate
- new hash. */
-static apr_hash_t *get_child_tokens(apr_hash_t *lock_tokens,
- const char *dir,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- apr_hash_t *tokens = apr_hash_make(pool);
- apr_pool_t *subpool = svn_pool_create(pool);
-
- for (hi = apr_hash_first(pool, lock_tokens); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- apr_ssize_t klen;
- void *val;
-
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, &klen, &val);
-
- if (svn_relpath__is_child(dir, key, subpool))
- apr_hash_set(tokens, key, klen, val);
- }
-
- svn_pool_destroy(subpool);
- return tokens;
-}
-
-
-/* PROPPATCH the appropriate resource in order to set the revision
- properties in CC->REVPROP_TABLE on the commit transaction. Use
- POOL for temporary allocations. */
-static svn_error_t *
-apply_revprops(commit_ctx_t *cc,
- apr_pool_t *pool)
-{
- const char *proppatch_url;
-
- if (USING_HTTPV2_COMMIT_SUPPORT(cc))
- {
- proppatch_url = cc->txn_url;
- }
- else
- {
- const char *vcc;
- version_rsrc_t vcc_rsrc = { SVN_INVALID_REVNUM };
- svn_error_t *err = NULL;
- int retry_count = 5;
-
- /* Fetch the DAV:version-controlled-configuration from the
- session's URL. */
- SVN_ERR(svn_ra_neon__get_vcc(&vcc, cc->ras, cc->ras->root.path, pool));
-
- do {
- svn_error_clear(err);
-
- vcc_rsrc.pool = pool;
- vcc_rsrc.vsn_url = vcc;
-
- /* To set the revision properties, we must checkout the latest
- baseline and get back a mutable "working" baseline. */
- err = checkout_resource(cc, "", &vcc_rsrc, FALSE, NULL, TRUE, pool);
-
- /* There's a small chance of a race condition here, if apache
- is experiencing heavy commit concurrency or if the network
- has long latency. It's possible that the value of HEAD
- changed between the time we fetched the latest baseline and
- the time we checkout that baseline. If that happens,
- apache will throw us a BAD_BASELINE error (deltaV says you
- can only checkout the latest baseline). We just ignore
- that specific error and retry a few times, asking for the
- latest baseline again. */
- if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)
- return err;
-
- } while (err && (--retry_count > 0));
-
- /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a
- real error.*/
- if (err)
- return err;
-
- proppatch_url = vcc_rsrc.wr_url;
- }
-
- return svn_error_trace(svn_ra_neon__do_proppatch(cc->ras, proppatch_url,
- cc->revprop_table,
- NULL, NULL, NULL, pool));
-}
-
-
-/*** Commit Editor Functions ***/
-
-static svn_error_t * commit_open_root(void *edit_baton,
- svn_revnum_t base_revision,
- apr_pool_t *dir_pool,
- void **root_baton)
-{
- commit_ctx_t *cc = edit_baton;
- resource_baton_t *root;
- version_rsrc_t *rsrc = NULL;
-
- root = apr_pcalloc(dir_pool, sizeof(*root));
- root->pool = dir_pool;
- root->cc = cc;
- root->base_revision = base_revision;
- root->created = FALSE;
- root->local_relpath = "";
-
- if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(cc->ras))
- {
- /* POST to the 'me' resource to create our server-side
- transaction (and, optionally, a corresponding activity). */
- svn_ra_neon__request_t *req;
- const char *header_val;
- svn_error_t *err;
-
- SVN_ERR(svn_ra_neon__request_create(&req, cc->ras, "POST",
- cc->ras->me_resource, dir_pool));
- ne_add_request_header(req->ne_req, "Content-Type", SVN_SKEL_MIME_TYPE);
-
-#ifdef SVN_DAV_SEND_VTXN_NAME
- /* Enable this to exercise the VTXN-NAME code based on a client
- supplied transaction name. */
- ne_add_request_header(req->ne_req, SVN_DAV_VTXN_NAME_HEADER,
- svn_uuid_generate(dir_pool));
-#endif
-
- err = svn_ra_neon__request_dispatch(NULL, req, NULL, "( create-txn )",
- 201, 0, dir_pool);
- if (!err)
- {
- /* Check the response headers for either the virtual transaction
- details, or the real transaction details. We need to have
- one or the other of those! */
- if ((header_val = ne_get_response_header(req->ne_req,
- SVN_DAV_VTXN_NAME_HEADER)))
- {
- cc->txn_url = svn_path_url_add_component2(cc->ras->vtxn_stub,
- header_val, cc->pool);
- cc->txn_root_url
- = svn_path_url_add_component2(cc->ras->vtxn_root_stub,
- header_val, cc->pool);
- }
- else if ((header_val
- = ne_get_response_header(req->ne_req,
- SVN_DAV_TXN_NAME_HEADER)))
- {
- cc->txn_url = svn_path_url_add_component2(cc->ras->txn_stub,
- header_val, cc->pool);
- cc->txn_root_url
- = svn_path_url_add_component2(cc->ras->txn_root_stub,
- header_val, cc->pool);
- }
- else
- err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("POST request did not return transaction "
- "information"));
- }
- svn_ra_neon__request_destroy(req);
- SVN_ERR(err);
-
- root->rsrc = NULL;
- root->txn_root_url = svn_path_url_add_component2(cc->txn_root_url,
- cc->anchor_relpath,
- dir_pool);
- }
- else
- {
- /* Use MKACTIVITY against a unique child of activity collection
- to create a server-side activity which corresponds directly
- to an FS transaction. We will check out all further
- resources within the context of this activity. */
- SVN_ERR(create_activity(cc, dir_pool));
-
- /* Create the root resource. (We don't yet know the wr_url.) */
- rsrc = apr_pcalloc(dir_pool, sizeof(*rsrc));
- rsrc->pool = dir_pool;
- rsrc->revision = base_revision;
- rsrc->url = cc->ras->root.path;
-
- SVN_ERR(get_version_url(cc, root->local_relpath, NULL, rsrc,
- FALSE, dir_pool));
-
- root->rsrc = rsrc;
- root->txn_root_url = NULL;
- }
-
- /* Find the latest baseline resource, check it out, and then apply
- the log message onto the thing. */
- SVN_ERR(apply_revprops(cc, dir_pool));
-
-
- *root_baton = root;
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * commit_delete_entry(const char *path,
- svn_revnum_t revision,
- void *parent_baton,
- apr_pool_t *pool)
-{
- resource_baton_t *parent = parent_baton;
- const char *name = svn_relpath_basename(path, NULL);
- apr_hash_t *extra_headers = apr_hash_make(pool);
- int code;
- svn_error_t *serr;
- const char *delete_target;
-
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
- {
- delete_target = svn_path_url_add_component2(parent->txn_root_url,
- name, pool);
- }
- else
- {
- /* Get the URL to the working collection. */
- SVN_ERR(checkout_resource(parent->cc, parent->local_relpath,
- parent->rsrc, TRUE, NULL, FALSE, pool));
-
- /* Create the URL for the child resource. */
- delete_target = svn_path_url_add_component2(parent->rsrc->wr_url,
- name, pool);
- }
-
- /* If we have a base revision for the server to compare against,
- pass that along in a custom header. */
- if (SVN_IS_VALID_REVNUM(revision))
- svn_ra_neon__set_header(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
- apr_psprintf(pool, "%ld", revision));
-
- /* We start out assuming that we're deleting a file; try to lookup
- the path itself in the token-hash, and if found, attach it to the
- If: header. */
- if (parent->cc->lock_tokens)
- {
- const char *token =
- apr_hash_get(parent->cc->lock_tokens, path, APR_HASH_KEY_STRING);
-
- if (token)
- {
- const char *token_header_val;
- const char *token_uri;
-
- token_uri = svn_path_url_add_component2(parent->cc->ras->url->data,
- path, pool);
- token_header_val = apr_psprintf(pool, "<%s> (<%s>)",
- token_uri, token);
- extra_headers = apr_hash_make(pool);
- apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING,
- token_header_val);
- }
- }
-
- /* dav_method_delete() always calls dav_unlock(), but if the svn
- client passed --no-unlock to 'svn commit', then we need to send a
- header which prevents mod_dav_svn from actually doing the unlock. */
- if (parent->cc->keep_locks)
- {
- apr_hash_set(extra_headers, SVN_DAV_OPTIONS_HEADER,
- APR_HASH_KEY_STRING, SVN_DAV_OPTION_KEEP_LOCKS);
- }
-
- serr = svn_ra_neon__simple_request(&code, parent->cc->ras,
- "DELETE", delete_target,
- extra_headers, NULL,
- 204 /* No Content */,
- 0, pool);
-
- /* A locking-related error most likely means we were deleting a
- directory rather than a file, and didn't send all of the
- necessary lock-tokens within the directory. */
- if (serr && ((serr->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)
- || (serr->apr_err == SVN_ERR_FS_NO_LOCK_TOKEN)
- || (serr->apr_err == SVN_ERR_FS_LOCK_OWNER_MISMATCH)
- || (serr->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED)))
- {
- /* Re-attempt the DELETE request as if the path were a
- directory. Discover all lock-tokens within the directory,
- and send them in the body of the request (which is normally
- empty). Of course, if we don't *find* any additional
- lock-tokens, don't bother to retry (it ain't gonna do any
- good).
-
- Note that we're not sending the locks in the If: header, for
- the same reason we're not sending in MERGE's headers: httpd
- has limits on the amount of data it's willing to receive in
- headers. */
-
- apr_hash_t *child_tokens = NULL;
- svn_ra_neon__request_t *request;
- const char *body;
- const char *token;
- svn_stringbuf_t *locks_list;
- svn_error_t *err = SVN_NO_ERROR;
-
- if (parent->cc->lock_tokens)
- child_tokens = get_child_tokens(parent->cc->lock_tokens, path, pool);
-
- /* No kiddos? Return the original error. Else, clear it so it
- doesn't get leaked. */
- if ((! child_tokens) || (apr_hash_count(child_tokens) == 0))
- return serr;
- else
- svn_error_clear(serr);
-
- /* In preparation of directory locks, go ahead and add the actual
- target's lock token to those of its children. */
- if ((token = apr_hash_get(parent->cc->lock_tokens, path,
- APR_HASH_KEY_STRING)))
- {
- /* ### copy PATH into the right pool. which? */
- apr_hash_set(child_tokens, path, APR_HASH_KEY_STRING, token);
- }
-
- SVN_ERR(svn_ra_neon__request_create(&request, parent->cc->ras, "DELETE",
- delete_target, pool));
-
- err = svn_ra_neon__assemble_locktoken_body(&locks_list,
- child_tokens, request->pool);
- if (err)
- goto cleanup;
-
- body = apr_psprintf(request->pool,
- "<?xml version=\"1.0\" encoding=\"utf-8\"?> %s",
- locks_list->data);
-
- err = svn_ra_neon__request_dispatch(&code, request, NULL, body,
- 204 /* Created */,
- 404 /* Not Found */,
- pool);
- cleanup:
- svn_ra_neon__request_destroy(request);
- SVN_ERR(err);
- }
- else if (serr)
- return serr;
-
- /* Add this path to the valid targets hash. */
- add_valid_target(parent->cc, path, svn_nonrecursive);
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * commit_add_dir(const char *path,
- void *parent_baton,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- apr_pool_t *dir_pool,
- void **child_baton)
-{
- resource_baton_t *parent = parent_baton;
- resource_baton_t *child;
- int code;
- const char *name = svn_relpath_basename(path, dir_pool);
- apr_pool_t *workpool = svn_pool_create(dir_pool);
- version_rsrc_t *rsrc = NULL;
- const char *mkcol_target = NULL;
-
- /* create a child object that contains all the resource urls */
- child = apr_pcalloc(dir_pool, sizeof(*child));
- child->pool = dir_pool;
- child->base_revision = SVN_INVALID_REVNUM;
- child->cc = parent->cc;
- child->created = TRUE;
- child->local_relpath = svn_relpath_join(parent->local_relpath,
- name, dir_pool);
-
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
- {
- child->rsrc = NULL;
- child->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
- name, dir_pool);
- mkcol_target = child->txn_root_url;
- }
- else
- {
- /* check out the parent resource so that we can create the new collection
- as one of its children. */
- SVN_ERR(checkout_resource(parent->cc, parent->local_relpath,
- parent->rsrc, TRUE, NULL, FALSE, dir_pool));
- SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
- name, 1, SVN_INVALID_REVNUM, workpool));
-
- child->rsrc = dup_resource(rsrc, dir_pool);
- child->txn_root_url = NULL;
- mkcol_target = child->rsrc->wr_url;
- }
-
- if (! copyfrom_path)
- {
- /* This a new directory with no history, so just create a new,
- empty collection */
- SVN_ERR(svn_ra_neon__simple_request(&code, parent->cc->ras, "MKCOL",
- mkcol_target, NULL, NULL,
- 201 /* Created */, 0, workpool));
- }
- else
- {
- /* This add has history, so we need to do a COPY. */
- SVN_ERR(copy_resource(parent->cc->ras, copyfrom_path, copyfrom_revision,
- mkcol_target, workpool));
-
- /* Remember that this object was copied. */
- child->copied = TRUE;
- }
-
- /* Add this path to the valid targets hash. */
- add_valid_target(parent->cc, path,
- copyfrom_path ? svn_recursive : svn_nonrecursive);
-
- svn_pool_destroy(workpool);
- *child_baton = child;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_open_dir(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *dir_pool,
- void **child_baton)
-{
- resource_baton_t *parent = parent_baton;
- resource_baton_t *child = apr_pcalloc(dir_pool, sizeof(*child));
- const char *name = svn_relpath_basename(path, dir_pool);
- version_rsrc_t *rsrc = NULL;
-
- child->pool = dir_pool;
- child->base_revision = base_revision;
- child->cc = parent->cc;
- child->created = FALSE;
- child->local_relpath = svn_relpath_join(parent->local_relpath,
- name, dir_pool);
-
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
- {
- child->rsrc = NULL;
- child->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
- name, dir_pool);
- }
- else
- {
- apr_pool_t *workpool = svn_pool_create(dir_pool);
-
- SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
- name, 0, base_revision, workpool));
- child->rsrc = dup_resource(rsrc, dir_pool);
- child->txn_root_url = NULL;
-
- svn_pool_destroy(workpool);
- }
-
- /* We don't do any real work here -- open_dir() just sets up the
- baton for this directory for use later when we operate on one of
- its children. */
- *child_baton = child;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_change_dir_prop(void *dir_baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- resource_baton_t *dir = dir_baton;
-
- /* record the change. it will be applied at close_dir time. */
- record_prop_change(dir->pool, dir, name, value);
-
- if (! USING_HTTPV2_COMMIT_SUPPORT(dir->cc))
- {
- /* We might as well CHECKOUT now (if we haven't already). Why
- wait? */
- SVN_ERR(checkout_resource(dir->cc, dir->local_relpath, dir->rsrc,
- TRUE, NULL, FALSE, pool));
- }
-
- /* Add the path to the valid targets hash. */
- add_valid_target(dir->cc, dir->local_relpath, svn_nonrecursive);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_close_dir(void *dir_baton,
- apr_pool_t *pool)
-{
- resource_baton_t *dir = dir_baton;
-
- /* Perform all of the property changes on the directory. Note that we
- checked out the directory when the first prop change was noted. */
- return do_proppatch(dir, pool);
-}
-
-static svn_error_t * commit_add_file(const char *path,
- void *parent_baton,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- apr_pool_t *file_pool,
- void **file_baton)
-{
- resource_baton_t *parent = parent_baton;
- resource_baton_t *file;
- const char *name = svn_relpath_basename(path, file_pool);
- apr_pool_t *workpool = svn_pool_create(file_pool);
- const char *put_target = NULL;
-
- /*
- ** To add a new file into the repository, we CHECKOUT the parent
- ** collection, then PUT the file as a member of the resulting working
- ** collection.
- **
- ** If the file was copied from elsewhere, then we will use the COPY
- ** method to copy into the working collection.
- */
-
- /* Construct a file_baton that contains all the resource urls. */
- file = apr_pcalloc(file_pool, sizeof(*file));
- file->base_revision = SVN_INVALID_REVNUM;
- file->pool = file_pool;
- file->cc = parent->cc;
- file->created = TRUE;
- file->local_relpath = svn_relpath_join(parent->local_relpath,
- name, file_pool);
-
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
- {
- file->rsrc = NULL;
- file->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
- name, file_pool);
- put_target = file->txn_root_url;
- }
- else
- {
- version_rsrc_t *rsrc = NULL;
-
- /* Do the parent CHECKOUT first */
- SVN_ERR(checkout_resource(parent->cc, parent->local_relpath, parent->rsrc,
- TRUE, NULL, FALSE, workpool));
- SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
- name, 1, SVN_INVALID_REVNUM, workpool));
-
- file->rsrc = dup_resource(rsrc, file_pool);
- file->txn_root_url = NULL;
- put_target = file->rsrc->wr_url;
- }
-
- if (parent->cc->lock_tokens)
- file->token = apr_hash_get(parent->cc->lock_tokens, path,
- APR_HASH_KEY_STRING);
-
- /* If the parent directory existed before this commit then there may
- be a file with this URL already. We need to ensure such a file
- does not exist, which we do by attempting a PROPFIND in both
- public URL (the path in HEAD) and the working URL (the path
- within the transaction), since we cannot differentiate between
- deleted items.
-
- ### For now, we'll assume that if this path has already been
- ### added to the valid targets hash, that addition occurred
- ### during the "delete" phase (if that's not the case, this
- ### editor is being driven incorrectly, as we should never visit
- ### the same path twice except in a delete+add situation). */
- if ((! parent->created)
- && (! apr_hash_get(file->cc->valid_targets, path, APR_HASH_KEY_STRING)))
- {
- static const ne_propname restype_props[] =
- {
- { "DAV:", "resourcetype" },
- { NULL }
- };
- svn_ra_neon__resource_t *res;
- const char *public_url;
- svn_error_t *err1, *err2;
-
- public_url = svn_path_url_add_component2(file->cc->ras->url->data,
- path, workpool);
- err1 = svn_ra_neon__get_props_resource(&res, parent->cc->ras, put_target,
- NULL, restype_props, workpool);
- err2 = svn_ra_neon__get_props_resource(&res, parent->cc->ras, public_url,
- NULL, restype_props, workpool);
- if (! err1 && ! err2)
- {
- /* If the PROPFINDs succeed the file already exists */
- return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL,
- _("File '%s' already exists"), path);
- }
- else if ((err1 && (err1->apr_err == SVN_ERR_FS_NOT_FOUND))
- || (err2 && (err2->apr_err == SVN_ERR_FS_NOT_FOUND)))
- {
- svn_error_clear(err1);
- svn_error_clear(err2);
- }
- else
- {
- /* A real error */
- return svn_error_compose_create(err1, err2);
- }
- }
-
- if (! copyfrom_path)
- {
- /* This a truly new file. */
-
- /* Wait for apply_txdelta() before doing a PUT. It might arrive
- a "long time" from now -- certainly after many other
- operations -- we don't want to start a PUT just yet. */
- }
- else
- {
- /* This add has history, so we need to do a COPY. */
- SVN_ERR(copy_resource(parent->cc->ras, copyfrom_path, copyfrom_revision,
- put_target, workpool));
-
- /* Remember that this object was copied. */
- file->copied = TRUE;
- }
-
- /* Add this path to the valid targets hash. */
- add_valid_target(parent->cc, path, svn_nonrecursive);
-
- svn_pool_destroy(workpool);
-
- /* return the file_baton */
- *file_baton = file;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_open_file(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *file_pool,
- void **file_baton)
-{
- resource_baton_t *parent = parent_baton;
- resource_baton_t *file;
- const char *name = svn_relpath_basename(path, file_pool);
-
- file = apr_pcalloc(file_pool, sizeof(*file));
- file->pool = file_pool;
- file->base_revision = base_revision;
- file->cc = parent->cc;
- file->created = FALSE;
- file->local_relpath = svn_relpath_join(parent->local_relpath,
- name, file_pool);
-
- if (parent->cc->lock_tokens)
- file->token = apr_hash_get(parent->cc->lock_tokens, path,
- APR_HASH_KEY_STRING);
-
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
- {
- file->rsrc = NULL;
- file->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
- name, file_pool);
- }
- else
- {
- version_rsrc_t *rsrc = NULL;
- apr_pool_t *workpool = svn_pool_create(file_pool);
-
- SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
- name, 0, base_revision, workpool));
- file->rsrc = dup_resource(rsrc, file_pool);
- file->txn_root_url = NULL;
-
- /* Do the CHECKOUT now. */
- SVN_ERR(checkout_resource(parent->cc, file->local_relpath, file->rsrc,
- TRUE, file->token, FALSE, workpool));
-
- svn_pool_destroy(workpool);
- }
-
- /* Wait for apply_txdelta() before doing a PUT. It might arrive
- a "long time" from now -- certainly after many other
- operations -- we don't want to start a PUT just yet. */
-
- *file_baton = file;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_stream_write(void *baton,
- const char *data,
- apr_size_t *len)
-{
- put_baton_t *pb = baton;
- svn_ra_neon__session_t *ras = pb->ras;
- apr_status_t status;
-
- if (ras->callbacks && ras->callbacks->cancel_func)
- SVN_ERR(ras->callbacks->cancel_func(ras->callback_baton));
-
- /* drop the data into our temp file */
- status = apr_file_write_full(pb->tmpfile, data, *len, NULL);
- if (status)
- return svn_error_wrap_apr(status,
- _("Could not write svndiff to temp file"));
-
- if (ras->progress_func)
- {
- pb->progress += *len;
- ras->progress_func(pb->progress, -1, ras->progress_baton, pb->pool);
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-commit_apply_txdelta(void *file_baton,
- const char *base_checksum,
- apr_pool_t *pool,
- svn_txdelta_window_handler_t *handler,
- void **handler_baton)
-{
- resource_baton_t *file = file_baton;
- put_baton_t *baton;
- svn_stream_t *stream;
-
- baton = apr_pcalloc(file->pool, sizeof(*baton));
- baton->ras = file->cc->ras;
- baton->pool = file->pool;
- file->put_baton = baton;
-
- if (base_checksum)
- baton->base_checksum = apr_pstrdup(file->pool, base_checksum);
- else
- baton->base_checksum = NULL;
-
- /* ### oh, hell. Neon's request body support is either text (a C string),
- ### or a FILE*. since we are getting binary data, we must use a FILE*
- ### for now. isn't that special? */
- SVN_ERR(svn_io_open_unique_file3(&baton->tmpfile, NULL, NULL,
- svn_io_file_del_on_pool_cleanup,
- file->pool, pool));
-
- stream = svn_stream_create(baton, pool);
- svn_stream_set_write(stream, commit_stream_write);
-
- svn_txdelta_to_svndiff2(handler, handler_baton, stream, 0, pool);
-
- /* Add this path to the valid targets hash. */
- add_valid_target(file->cc, file->local_relpath, svn_nonrecursive);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_change_file_prop(void *file_baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- resource_baton_t *file = file_baton;
-
- /* Record the change. It will be applied at close_file() time. */
- record_prop_change(file->pool, file, name, value);
-
- if (! USING_HTTPV2_COMMIT_SUPPORT(file->cc))
- {
- /* We might as well CHECKOUT now (if we haven't already). Why
- wait? */
- SVN_ERR(checkout_resource(file->cc, file->local_relpath, file->rsrc,
- TRUE, file->token, FALSE, pool));
- }
-
- /* Add the path to the valid targets hash. */
- add_valid_target(file->cc, file->local_relpath, svn_nonrecursive);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t * commit_close_file(void *file_baton,
- const char *text_checksum,
- apr_pool_t *pool)
-{
- resource_baton_t *file = file_baton;
- commit_ctx_t *cc = file->cc;
-
- /* If this is a newly added file, not copied, and the editor driver
- didn't call apply_textdelta(), then we'll pretend they *did* call
- apply_textdelta() and described a zero-byte empty file. */
- if ((! file->put_baton) && file->created && (! file->copied))
- {
- /* Make a dummy put_baton, with NULL fields to indicate that
- we're dealing with a content-less (zero-byte) file. */
- file->put_baton = apr_pcalloc(file->pool, sizeof(*(file->put_baton)));
- }
-
- if (file->put_baton)
- {
- svn_error_t *err = SVN_NO_ERROR;
- put_baton_t *pb = file->put_baton;
- apr_hash_t *extra_headers;
- svn_ra_neon__request_t *request;
- int code;
- const char *public_url =
- svn_path_url_add_component2(file->cc->ras->url->data,
- file->local_relpath, pool);
- const char *put_target =
- USING_HTTPV2_COMMIT_SUPPORT(cc) ? file->txn_root_url
- : file->rsrc->wr_url;
-
- /* create/prep the request */
- SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "PUT",
- put_target, pool));
-
- extra_headers = apr_hash_make(request->pool);
-
- if (file->token)
- svn_ra_neon__set_header(extra_headers, "If",
- apr_psprintf(pool, "<%s> (<%s>)",
- public_url, file->token));
-
- if (pb->base_checksum)
- svn_ra_neon__set_header(extra_headers,
- SVN_DAV_BASE_FULLTEXT_MD5_HEADER,
- pb->base_checksum);
-
- if (text_checksum)
- svn_ra_neon__set_header(extra_headers,
- SVN_DAV_RESULT_FULLTEXT_MD5_HEADER,
- text_checksum);
-
- if (SVN_IS_VALID_REVNUM(file->base_revision))
- svn_ra_neon__set_header(extra_headers,
- SVN_DAV_VERSION_NAME_HEADER,
- apr_psprintf(pool, "%ld", file->base_revision));
-
- if (pb->tmpfile)
- {
- svn_ra_neon__set_header(extra_headers, "Content-Type",
- SVN_SVNDIFF_MIME_TYPE);
-
- /* Give the file to neon. The provider will rewind the file. */
- err = svn_ra_neon__set_neon_body_provider(request, pb->tmpfile);
- if (err)
- goto cleanup;
- }
- else
- {
- ne_set_request_body_buffer(request->ne_req, "", 0);
- }
-
- /* run the request and get the resulting status code (and svn_error_t) */
- err = svn_ra_neon__request_dispatch(&code, request, extra_headers, NULL,
- 201 /* Created */,
- 204 /* No Content */,
- pool);
-
- if (err && (err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED))
- {
- switch (code)
- {
- case 423:
- err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, err,
- _("No lock on path '%s'"
- " (Status %d on PUT Request)"),
- put_target, code);
- default:
- break;
- }
- }
- cleanup:
- svn_ra_neon__request_destroy(request);
- SVN_ERR(err);
-
- if (pb->tmpfile)
- {
- /* 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. */
- (void) apr_file_close(pb->tmpfile);
- }
- }
-
- /* Perform all of the property changes on the file. Note that we
- checked out the file when the first prop change was noted. */
- return do_proppatch(file, pool);
-}
-
-
-static svn_error_t * commit_close_edit(void *edit_baton,
- apr_pool_t *pool)
-{
- commit_ctx_t *cc = edit_baton;
- svn_commit_info_t *commit_info = svn_create_commit_info(pool);
- const char *merge_resource_url =
- USING_HTTPV2_COMMIT_SUPPORT(cc) ? cc->txn_url : cc->activity_url;
-
- SVN_ERR(svn_ra_neon__merge_activity(&(commit_info->revision),
- &(commit_info->date),
- &(commit_info->author),
- &(commit_info->post_commit_err),
- cc->ras,
- cc->ras->root.path,
- merge_resource_url,
- cc->valid_targets,
- cc->lock_tokens,
- cc->keep_locks,
- cc->ras->callbacks->push_wc_prop == NULL,
- pool));
-
- /* DELETE any activity that might be left on the server. */
- if (cc->activity_url)
- {
- SVN_ERR(svn_ra_neon__simple_request(NULL, cc->ras, "DELETE",
- cc->activity_url, NULL, NULL,
- 204 /* No Content */,
- 404 /* Not Found */, pool));
- }
-
- if (cc->callback && commit_info->revision != SVN_INVALID_REVNUM)
- SVN_ERR(cc->callback(commit_info, cc->callback_baton, pool));
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t * commit_abort_edit(void *edit_baton,
- apr_pool_t *pool)
-{
- commit_ctx_t *cc = edit_baton;
- const char *delete_target = NULL;
-
- /* If we started an activity and/or transaction, we need to (try to)
- delete it. (If it doesn't exist, that's okay, too.) */
- if (USING_HTTPV2_COMMIT_SUPPORT(cc))
- delete_target = cc->txn_url;
- else
- delete_target = cc->activity_url;
-
- if (delete_target)
- SVN_ERR(svn_ra_neon__simple_request(NULL, cc->ras, "DELETE",
- delete_target, NULL, NULL,
- 204 /* No Content */,
- 404 /* Not Found */, pool));
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t * svn_ra_neon__get_commit_editor(svn_ra_session_t *session,
- const svn_delta_editor_t **editor,
- void **edit_baton,
- apr_hash_t *revprop_table,
- svn_commit_callback2_t callback,
- void *callback_baton,
- apr_hash_t *lock_tokens,
- svn_boolean_t keep_locks,
- apr_pool_t *pool)
-{
- svn_ra_neon__session_t *ras = session->priv;
- svn_delta_editor_t *commit_editor;
- commit_ctx_t *cc;
- apr_hash_index_t *hi;
-
- /* Build the main commit editor's baton. */
- cc = apr_pcalloc(pool, sizeof(*cc));
- cc->pool = pool;
- cc->ras = ras;
- cc->valid_targets = apr_hash_make(pool);
- cc->callback = callback;
- cc->callback_baton = callback_baton;
- cc->lock_tokens = lock_tokens;
- cc->keep_locks = keep_locks;
-
- /* Dup the revprops into POOL, in case the caller clears the pool
- they're in before driving the editor that this function returns. */
- cc->revprop_table = apr_hash_make(pool);
- for (hi = apr_hash_first(pool, revprop_table); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- apr_ssize_t klen;
- void *val;
-
- apr_hash_this(hi, &key, &klen, &val);
- apr_hash_set(cc->revprop_table, apr_pstrdup(pool, key), klen,
- svn_string_dup(val, pool));
- }
-
- /* Calculate the commit anchor's repository relpath. */
- SVN_ERR(svn_ra_neon__get_path_relative_to_root(session,
- &(cc->anchor_relpath),
- ras->url->data, pool));
-
- /* Set up the editor. */
- commit_editor = svn_delta_default_editor(pool);
- commit_editor->open_root = commit_open_root;
- commit_editor->delete_entry = commit_delete_entry;
- commit_editor->add_directory = commit_add_dir;
- commit_editor->open_directory = commit_open_dir;
- commit_editor->change_dir_prop = commit_change_dir_prop;
- commit_editor->close_directory = commit_close_dir;
- commit_editor->add_file = commit_add_file;
- commit_editor->open_file = commit_open_file;
- commit_editor->apply_textdelta = commit_apply_txdelta;
- commit_editor->change_file_prop = commit_change_file_prop;
- commit_editor->close_file = commit_close_file;
- commit_editor->close_edit = commit_close_edit;
- commit_editor->abort_edit = commit_abort_edit;
-
- *editor = commit_editor;
- *edit_baton = cc;
- return SVN_NO_ERROR;
-}