~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_ra_serf/commit.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 */
23
23
 
24
24
#include <apr_uri.h>
25
 
 
26
 
#include <expat.h>
27
 
 
28
25
#include <serf.h>
29
26
 
 
27
#include "svn_hash.h"
30
28
#include "svn_pools.h"
31
29
#include "svn_ra.h"
32
30
#include "svn_dav.h"
41
39
#include "svn_private_config.h"
42
40
#include "private/svn_dep_compat.h"
43
41
#include "private/svn_fspath.h"
 
42
#include "private/svn_skel.h"
44
43
 
45
44
#include "ra_serf.h"
46
45
#include "../libsvn_ra/ra_loader.h"
47
46
 
48
 
 
49
 
/* Structure associated with a CHECKOUT request. */
50
 
typedef struct checkout_context_t {
51
 
 
52
 
  apr_pool_t *pool;
53
 
 
54
 
  const char *activity_url;
55
 
  const char *checkout_url;
56
 
  const char *resource_url;
57
 
 
58
 
  svn_ra_serf__simple_request_context_t progress;
59
 
 
60
 
} checkout_context_t;
61
47
 
62
48
/* Baton passed back with the commit editor. */
63
49
typedef struct commit_context_t {
82
68
 
83
69
  /* HTTP v1 stuff (only valid when 'txn_url' is NULL) */
84
70
  const char *activity_url;      /* activity base URL... */
85
 
  checkout_context_t *baseline;  /* checkout for the baseline */
 
71
  const char *baseline_url;      /* the working-baseline resource */
86
72
  const char *checked_in_url;    /* checked-in root to base CHECKOUTs from */
87
73
  const char *vcc_url;           /* vcc url */
88
74
 
110
96
  /* In HTTP v2, this is the file/directory version we think we're changing. */
111
97
  svn_revnum_t base_revision;
112
98
 
113
 
  svn_ra_serf__simple_request_context_t progress;
114
99
} proppatch_context_t;
115
100
 
116
101
typedef struct delete_context_t {
122
107
  apr_hash_t *lock_token_hash;
123
108
  svn_boolean_t keep_locks;
124
109
 
125
 
  svn_ra_serf__simple_request_context_t progress;
126
110
} delete_context_t;
127
111
 
128
112
/* Represents a directory. */
162
146
  apr_hash_t *changed_props;
163
147
  apr_hash_t *removed_props;
164
148
 
165
 
  /* The checked out context for this directory.  May be NULL; if so
 
149
  /* The checked-out working resource for this directory.  May be NULL; if so
166
150
     call checkout_dir() first.  */
167
 
  checkout_context_t *checkout;
 
151
  const char *working_url;
168
152
 
169
153
} dir_context_t;
170
154
 
184
168
  const char *relpath;
185
169
  const char *name;
186
170
 
187
 
  /* The checked out context for this file. */
188
 
  checkout_context_t *checkout;
 
171
  /* The checked-out working resource for this file. */
 
172
  const char *working_url;
189
173
 
190
174
  /* The base revision of the file. */
191
175
  svn_revnum_t base_revision;
219
203
/* Setup routines and handlers for various requests we'll invoke. */
220
204
 
221
205
static svn_error_t *
222
 
return_response_err(svn_ra_serf__handler_t *handler,
223
 
                    svn_ra_serf__simple_request_context_t *ctx)
 
206
return_response_err(svn_ra_serf__handler_t *handler)
224
207
{
225
208
  svn_error_t *err;
226
209
 
 
210
  /* We should have captured SLINE and LOCATION in the HANDLER.  */
 
211
  SVN_ERR_ASSERT(handler->handler_pool != NULL);
 
212
 
227
213
  /* Ye Olde Fallback Error */
228
214
  err = svn_error_compose_create(
229
 
            ctx->server_error.error,
 
215
            handler->server_error != NULL
 
216
              ? handler->server_error->error
 
217
              : SVN_NO_ERROR,
230
218
            svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
231
219
                              _("%s of '%s': %d %s"),
232
220
                              handler->method, handler->path,
233
 
                              ctx->status, ctx->reason));
 
221
                              handler->sline.code, handler->sline.reason));
234
222
 
235
223
  /* Try to return one of the standard errors for 301, 404, etc.,
236
224
     then look for an error embedded in the response.  */
237
 
  return svn_error_compose_create(svn_ra_serf__error_on_status(ctx->status,
238
 
                                                               handler->path,
239
 
                                                               ctx->location),
 
225
  return svn_error_compose_create(svn_ra_serf__error_on_status(
 
226
                                    handler->sline,
 
227
                                    handler->path,
 
228
                                    handler->location),
240
229
                                  err);
241
230
}
242
231
 
247
236
                     serf_bucket_alloc_t *alloc,
248
237
                     apr_pool_t *pool)
249
238
{
250
 
  checkout_context_t *ctx = baton;
 
239
  const char *activity_url = baton;
251
240
  serf_bucket_t *body_bkt;
252
241
 
253
242
  body_bkt = serf_bucket_aggregate_create(alloc);
259
248
  svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:activity-set", NULL);
260
249
  svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL);
261
250
 
 
251
  SVN_ERR_ASSERT(activity_url != NULL);
262
252
  svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc,
263
 
                                     ctx->activity_url, strlen(ctx->activity_url));
 
253
                                     activity_url,
 
254
                                     strlen(activity_url));
264
255
 
265
256
  svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href");
266
257
  svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:activity-set");
271
262
  return SVN_NO_ERROR;
272
263
}
273
264
 
274
 
/* Implements svn_ra_serf__response_handler_t */
275
 
static svn_error_t *
276
 
handle_checkout(serf_request_t *request,
277
 
                serf_bucket_t *response,
278
 
                void *handler_baton,
279
 
                apr_pool_t *pool)
280
 
{
281
 
  checkout_context_t *ctx = handler_baton;
282
 
 
283
 
  svn_error_t *err = svn_ra_serf__handle_status_only(request, response,
284
 
                                                     &ctx->progress, pool);
285
 
 
286
 
  /* These handler functions are supposed to return an APR_EOF status
287
 
     wrapped in a svn_error_t to indicate to serf that the response was
288
 
     completely read. While we have to return this status code to our
289
 
     caller, we should treat it as the normal case for now. */
290
 
  if (err && ! APR_STATUS_IS_EOF(err->apr_err))
291
 
    return err;
292
 
 
293
 
  /* Get the resulting location. */
294
 
  if (ctx->progress.done && ctx->progress.status == 201)
 
265
 
 
266
/* Using the HTTPv1 protocol, perform a CHECKOUT of NODE_URL within the
 
267
   given COMMIT_CTX. The resulting working resource will be returned in
 
268
   *WORKING_URL, allocated from RESULT_POOL. All temporary allocations
 
269
   are performed in SCRATCH_POOL.
 
270
 
 
271
   ### are these URLs actually repos relpath values? or fspath? or maybe
 
272
   ### the abspath portion of the full URL.
 
273
 
 
274
   This function operates synchronously.
 
275
 
 
276
   Strictly speaking, we could perform "all" of the CHECKOUT requests
 
277
   when the commit starts, and only block when we need a specific
 
278
   answer. Or, at a minimum, send off these individual requests async
 
279
   and block when we need the answer (eg PUT or PROPPATCH).
 
280
 
 
281
   However: the investment to speed this up is not worthwhile, given
 
282
   that CHECKOUT (and the related round trip) is completely obviated
 
283
   in HTTPv2.
 
284
*/
 
285
static svn_error_t *
 
286
checkout_node(const char **working_url,
 
287
              const commit_context_t *commit_ctx,
 
288
              const char *node_url,
 
289
              apr_pool_t *result_pool,
 
290
              apr_pool_t *scratch_pool)
 
291
{
 
292
  svn_ra_serf__handler_t handler = { 0 };
 
293
  apr_status_t status;
 
294
  apr_uri_t uri;
 
295
 
 
296
  /* HANDLER_POOL is the scratch pool since we don't need to remember
 
297
     anything from the handler. We just want the working resource.  */
 
298
  handler.handler_pool = scratch_pool;
 
299
  handler.session = commit_ctx->session;
 
300
  handler.conn = commit_ctx->conn;
 
301
 
 
302
  handler.body_delegate = create_checkout_body;
 
303
  handler.body_delegate_baton = (/* const */ void *)commit_ctx->activity_url;
 
304
  handler.body_type = "text/xml";
 
305
 
 
306
  handler.response_handler = svn_ra_serf__expect_empty_body;
 
307
  handler.response_baton = &handler;
 
308
 
 
309
  handler.method = "CHECKOUT";
 
310
  handler.path = node_url;
 
311
 
 
312
  SVN_ERR(svn_ra_serf__context_run_one(&handler, scratch_pool));
 
313
 
 
314
  if (handler.sline.code != 201)
 
315
    return svn_error_trace(return_response_err(&handler));
 
316
 
 
317
  if (handler.location == NULL)
 
318
    return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
 
319
                            _("No Location header received"));
 
320
 
 
321
  /* We only want the path portion of the Location header.
 
322
     (code.google.com sometimes returns an 'http:' scheme for an
 
323
     'https:' transaction ... we'll work around that by stripping the
 
324
     scheme, host, and port here and re-adding the correct ones
 
325
     later.  */
 
326
  status = apr_uri_parse(scratch_pool, handler.location, &uri);
 
327
  if (status)
 
328
    return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
 
329
                            _("Error parsing Location header value"));
 
330
 
 
331
  *working_url = svn_urlpath__canonicalize(uri.path, result_pool);
 
332
 
 
333
  return SVN_NO_ERROR;
 
334
}
 
335
 
 
336
 
 
337
/* This is a wrapper around checkout_node() (which see for
 
338
   documentation) which simply retries the CHECKOUT request when it
 
339
   fails due to an SVN_ERR_APMOD_BAD_BASELINE error return from the
 
340
   server.
 
341
 
 
342
   See http://subversion.tigris.org/issues/show_bug.cgi?id=4127 for
 
343
   details.
 
344
*/
 
345
static svn_error_t *
 
346
retry_checkout_node(const char **working_url,
 
347
                    const commit_context_t *commit_ctx,
 
348
                    const char *node_url,
 
349
                    apr_pool_t *result_pool,
 
350
                    apr_pool_t *scratch_pool)
 
351
{
 
352
  svn_error_t *err = SVN_NO_ERROR;
 
353
  int retry_count = 5; /* Magic, arbitrary number. */
 
354
 
 
355
  do
295
356
    {
296
 
      serf_bucket_t *hdrs;
297
 
      apr_uri_t uri;
298
 
      const char *location;
299
 
      apr_status_t status;
300
 
 
301
 
      hdrs = serf_bucket_response_get_headers(response);
302
 
      location = serf_bucket_headers_get(hdrs, "Location");
303
 
      if (!location)
304
 
        return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
305
 
                                _("No Location header received"));
306
 
 
307
 
      status = apr_uri_parse(pool, location, &uri);
308
 
 
309
 
      if (status)
310
 
        err = svn_error_compose_create(svn_error_wrap_apr(status, NULL), err);
311
 
 
312
 
      ctx->resource_url = svn_urlpath__canonicalize(uri.path, ctx->pool);
 
357
      svn_error_clear(err);
 
358
 
 
359
      err = checkout_node(working_url, commit_ctx, node_url,
 
360
                          result_pool, scratch_pool);
 
361
 
 
362
      /* There's a small chance of a race condition here if Apache is
 
363
         experiencing heavy commit concurrency or if the network has
 
364
         long latency.  It's possible that the value of HEAD changed
 
365
         between the time we fetched the latest baseline and the time
 
366
         we try to CHECKOUT that baseline.  If that happens, Apache
 
367
         will throw us a BAD_BASELINE error (deltaV says you can only
 
368
         checkout the latest baseline).  We just ignore that specific
 
369
         error and retry a few times, asking for the latest baseline
 
370
         again. */
 
371
      if (err && (err->apr_err != SVN_ERR_APMOD_BAD_BASELINE))
 
372
        return err;
313
373
    }
 
374
  while (err && retry_count--);
314
375
 
315
376
  return err;
316
377
}
317
378
 
 
379
 
318
380
static svn_error_t *
319
 
checkout_dir(dir_context_t *dir)
 
381
checkout_dir(dir_context_t *dir,
 
382
             apr_pool_t *scratch_pool)
320
383
{
321
 
  checkout_context_t *checkout_ctx;
322
 
  svn_ra_serf__handler_t *handler;
323
384
  svn_error_t *err;
324
385
  dir_context_t *p_dir = dir;
 
386
  const char *checkout_url;
 
387
  const char **working;
325
388
 
326
 
  if (dir->checkout)
 
389
  if (dir->working_url)
327
390
    {
328
391
      return SVN_NO_ERROR;
329
392
    }
330
393
 
331
 
  /* Is this directory or one of our parent dirs newly added? 
 
394
  /* Is this directory or one of our parent dirs newly added?
332
395
   * If so, we're already implicitly checked out. */
333
396
  while (p_dir)
334
397
    {
335
398
      if (p_dir->added)
336
399
        {
 
400
          /* Calculate the working_url by skipping the shared ancestor bewteen
 
401
           * the parent->relpath and dir->relpath.  This is safe since an
 
402
           * add is guaranteed to have a parent that is checked out. */
 
403
          dir_context_t *parent = p_dir->parent_dir;
 
404
          const char *relpath = svn_relpath_skip_ancestor(parent->relpath,
 
405
                                                          dir->relpath);
 
406
 
337
407
          /* Implicitly checkout this dir now. */
338
 
          dir->checkout = apr_pcalloc(dir->pool, sizeof(*dir->checkout));
339
 
          dir->checkout->pool = dir->pool;
340
 
          dir->checkout->progress.pool = dir->pool;
341
 
          dir->checkout->activity_url = dir->commit->activity_url;
342
 
          dir->checkout->resource_url =
343
 
            svn_path_url_add_component2(dir->parent_dir->checkout->resource_url,
344
 
                                        dir->name, dir->pool);
345
 
 
 
408
          SVN_ERR_ASSERT(parent->working_url);
 
409
          dir->working_url = svn_path_url_add_component2(
 
410
                                   parent->working_url,
 
411
                                   relpath, dir->pool);
346
412
          return SVN_NO_ERROR;
347
413
        }
348
414
      p_dir = p_dir->parent_dir;
349
415
    }
350
416
 
351
 
  /* Checkout our directory into the activity URL now. */
352
 
  handler = apr_pcalloc(dir->pool, sizeof(*handler));
353
 
  handler->session = dir->commit->session;
354
 
  handler->conn = dir->commit->conn;
355
 
 
356
 
  checkout_ctx = apr_pcalloc(dir->pool, sizeof(*checkout_ctx));
357
 
  checkout_ctx->pool = dir->pool;
358
 
  checkout_ctx->progress.pool = dir->pool;
359
 
 
360
 
  checkout_ctx->activity_url = dir->commit->activity_url;
361
 
 
362
417
  /* We could be called twice for the root: once to checkout the baseline;
363
418
   * once to checkout the directory itself if we need to do so.
 
419
   * Note: CHECKOUT_URL should live longer than HANDLER.
364
420
   */
365
 
  if (!dir->parent_dir && !dir->commit->baseline)
 
421
  if (!dir->parent_dir && !dir->commit->baseline_url)
366
422
    {
367
 
      checkout_ctx->checkout_url = dir->commit->vcc_url;
368
 
      dir->commit->baseline = checkout_ctx;
 
423
      checkout_url = dir->commit->vcc_url;
 
424
      working = &dir->commit->baseline_url;
369
425
    }
370
426
  else
371
427
    {
372
 
      checkout_ctx->checkout_url = dir->url;
373
 
      dir->checkout = checkout_ctx;
 
428
      checkout_url = dir->url;
 
429
      working = &dir->working_url;
374
430
    }
375
431
 
376
 
  handler->body_delegate = create_checkout_body;
377
 
  handler->body_delegate_baton = checkout_ctx;
378
 
  handler->body_type = "text/xml";
379
 
 
380
 
  handler->response_handler = handle_checkout;
381
 
  handler->response_baton = checkout_ctx;
382
 
 
383
 
  handler->method = "CHECKOUT";
384
 
  handler->path = checkout_ctx->checkout_url;
385
 
 
386
 
  svn_ra_serf__request_create(handler);
387
 
 
388
 
  err = svn_ra_serf__context_run_wait(&checkout_ctx->progress.done,
389
 
                                      dir->commit->session,
390
 
                                      dir->pool);
 
432
  /* Checkout our directory into the activity URL now. */
 
433
  err = retry_checkout_node(working, dir->commit, checkout_url,
 
434
                            dir->pool, scratch_pool);
391
435
  if (err)
392
436
    {
393
437
      if (err->apr_err == SVN_ERR_FS_CONFLICT)
394
 
        SVN_ERR_W(err, apr_psprintf(dir->pool,
 
438
        SVN_ERR_W(err, apr_psprintf(scratch_pool,
395
439
                  _("Directory '%s' is out of date; try updating"),
396
 
                  svn_dirent_local_style(dir->relpath, dir->pool)));
 
440
                  svn_dirent_local_style(dir->relpath, scratch_pool)));
397
441
      return err;
398
442
    }
399
443
 
400
 
  if (checkout_ctx->progress.status != 201)
401
 
    {
402
 
      return return_response_err(handler, &checkout_ctx->progress);
403
 
    }
404
 
 
405
444
  return SVN_NO_ERROR;
406
445
}
407
446
 
419
458
 * BASE_REVISION, and set *CHECKED_IN_URL to the concatenation of that
420
459
 * with RELPATH.
421
460
 *
422
 
 * Allocate the result in POOL, and use POOL for temporary allocation.
 
461
 * Allocate the result in RESULT_POOL, and use SCRATCH_POOL for
 
462
 * temporary allocation.
423
463
 */
424
464
static svn_error_t *
425
465
get_version_url(const char **checked_in_url,
426
466
                svn_ra_serf__session_t *session,
427
 
                svn_ra_serf__connection_t *conn,
428
467
                const char *relpath,
429
468
                svn_revnum_t base_revision,
430
469
                const char *parent_vsn_url,
431
 
                apr_pool_t *pool)
 
470
                apr_pool_t *result_pool,
 
471
                apr_pool_t *scratch_pool)
432
472
{
433
473
  const char *root_checkout;
434
474
 
436
476
    {
437
477
      const svn_string_t *current_version;
438
478
 
439
 
      SVN_ERR(session->wc_callbacks->get_wc_prop(session->wc_callback_baton,
440
 
                                                 relpath,
441
 
                                                 SVN_RA_SERF__WC_CHECKED_IN_URL,
442
 
                                                 &current_version, pool));
 
479
      SVN_ERR(session->wc_callbacks->get_wc_prop(
 
480
                session->wc_callback_baton,
 
481
                relpath,
 
482
                SVN_RA_SERF__WC_CHECKED_IN_URL,
 
483
                &current_version, scratch_pool));
443
484
 
444
485
      if (current_version)
445
486
        {
446
487
          *checked_in_url =
447
 
            svn_urlpath__canonicalize(current_version->data, pool);
 
488
            svn_urlpath__canonicalize(current_version->data, result_pool);
448
489
          return SVN_NO_ERROR;
449
490
        }
450
491
    }
455
496
    }
456
497
  else
457
498
    {
458
 
      svn_ra_serf__propfind_context_t *propfind_ctx;
459
 
      apr_hash_t *props;
460
499
      const char *propfind_url;
461
 
 
462
 
      props = apr_hash_make(pool);
 
500
      svn_ra_serf__connection_t *conn = session->conns[0];
463
501
 
464
502
      if (SVN_IS_VALID_REVNUM(base_revision))
465
503
        {
466
 
          const char *bc_url, *bc_relpath;
467
 
 
468
504
          /* mod_dav_svn can't handle the "Label:" header that
469
505
             svn_ra_serf__deliver_props() is going to try to use for
470
506
             this lookup, so we'll do things the hard(er) way, by
471
507
             looking up the version URL from a resource in the
472
508
             baseline collection. */
473
 
          SVN_ERR(svn_ra_serf__get_baseline_info(&bc_url, &bc_relpath,
474
 
                                                 session, conn,
475
 
                                                 session->session_url.path,
476
 
                                                 base_revision, NULL, pool));
477
 
          propfind_url = svn_path_url_add_component2(bc_url, bc_relpath, pool);
 
509
          /* ### conn==NULL for session->conns[0]. same as CONN.  */
 
510
          SVN_ERR(svn_ra_serf__get_stable_url(&propfind_url,
 
511
                                              NULL /* latest_revnum */,
 
512
                                              session, NULL /* conn */,
 
513
                                              NULL /* url */, base_revision,
 
514
                                              scratch_pool, scratch_pool));
478
515
        }
479
516
      else
480
517
        {
481
518
          propfind_url = session->session_url.path;
482
519
        }
483
520
 
484
 
      /* ### switch to svn_ra_serf__retrieve_props  */
485
 
      SVN_ERR(svn_ra_serf__deliver_props(&propfind_ctx, props, session, conn,
486
 
                                         propfind_url, base_revision, "0",
487
 
                                         checked_in_props, NULL, pool));
488
 
      SVN_ERR(svn_ra_serf__wait_for_props(propfind_ctx, session, pool));
489
 
 
490
 
      /* We wouldn't get here if the url wasn't found (404), so the checked-in
491
 
         property should have been set. */
492
 
      root_checkout =
493
 
          svn_ra_serf__get_ver_prop(props, propfind_url,
494
 
                                    base_revision, "DAV:", "checked-in");
495
 
 
 
521
      SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout,
 
522
                                          conn, propfind_url, base_revision,
 
523
                                          "checked-in",
 
524
                                          scratch_pool, scratch_pool));
496
525
      if (!root_checkout)
497
526
        return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
498
527
                                 _("Path '%s' not present"),
499
528
                                 session->session_url.path);
500
529
 
501
 
      root_checkout = svn_urlpath__canonicalize(root_checkout, pool);
 
530
      root_checkout = svn_urlpath__canonicalize(root_checkout, scratch_pool);
502
531
    }
503
532
 
504
 
  *checked_in_url = svn_path_url_add_component2(root_checkout, relpath, pool);
 
533
  *checked_in_url = svn_path_url_add_component2(root_checkout, relpath,
 
534
                                                result_pool);
505
535
 
506
536
  return SVN_NO_ERROR;
507
537
}
508
538
 
509
539
static svn_error_t *
510
 
checkout_file(file_context_t *file)
 
540
checkout_file(file_context_t *file,
 
541
              apr_pool_t *scratch_pool)
511
542
{
512
 
  svn_ra_serf__handler_t *handler;
513
543
  svn_error_t *err;
514
544
  dir_context_t *parent_dir = file->parent_dir;
 
545
  const char *checkout_url;
515
546
 
516
547
  /* Is one of our parent dirs newly added?  If so, we're already
517
548
   * implicitly checked out.
521
552
      if (parent_dir->added)
522
553
        {
523
554
          /* Implicitly checkout this file now. */
524
 
          file->checkout = apr_pcalloc(file->pool, sizeof(*file->checkout));
525
 
          file->checkout->pool = file->pool;
526
 
          file->checkout->progress.pool = file->pool;
527
 
          file->checkout->activity_url = file->commit->activity_url;
528
 
          file->checkout->resource_url =
529
 
            svn_path_url_add_component2(parent_dir->checkout->resource_url,
530
 
                                        svn_relpath__is_child(parent_dir->relpath,
531
 
                                                              file->relpath,
532
 
                                                              file->pool),
533
 
                                        file->pool);
 
555
          file->working_url = svn_path_url_add_component2(
 
556
                                    parent_dir->working_url,
 
557
                                    svn_relpath_skip_ancestor(
 
558
                                      parent_dir->relpath, file->relpath),
 
559
                                    file->pool);
534
560
          return SVN_NO_ERROR;
535
561
        }
536
562
      parent_dir = parent_dir->parent_dir;
537
563
    }
538
564
 
 
565
  SVN_ERR(get_version_url(&checkout_url,
 
566
                          file->commit->session,
 
567
                          file->relpath, file->base_revision,
 
568
                          NULL, scratch_pool, scratch_pool));
 
569
 
539
570
  /* Checkout our file into the activity URL now. */
540
 
  handler = apr_pcalloc(file->pool, sizeof(*handler));
541
 
  handler->session = file->commit->session;
542
 
  handler->conn = file->commit->conn;
543
 
 
544
 
  file->checkout = apr_pcalloc(file->pool, sizeof(*file->checkout));
545
 
  file->checkout->pool = file->pool;
546
 
  file->checkout->progress.pool = file->pool;
547
 
 
548
 
  file->checkout->activity_url = file->commit->activity_url;
549
 
 
550
 
  SVN_ERR(get_version_url(&(file->checkout->checkout_url),
551
 
                          file->commit->session, file->commit->conn,
552
 
                          file->relpath, file->base_revision,
553
 
                          NULL, file->pool));
554
 
 
555
 
  handler->body_delegate = create_checkout_body;
556
 
  handler->body_delegate_baton = file->checkout;
557
 
  handler->body_type = "text/xml";
558
 
 
559
 
  handler->response_handler = handle_checkout;
560
 
  handler->response_baton = file->checkout;
561
 
 
562
 
  handler->method = "CHECKOUT";
563
 
  handler->path = file->checkout->checkout_url;
564
 
 
565
 
  svn_ra_serf__request_create(handler);
566
 
 
567
 
  /* There's no need to wait here as we only need this when we start the
568
 
   * PROPPATCH or PUT of the file.
569
 
   */
570
 
  err = svn_ra_serf__context_run_wait(&file->checkout->progress.done,
571
 
                                      file->commit->session,
572
 
                                      file->pool);
 
571
  err = retry_checkout_node(&file->working_url, file->commit, checkout_url,
 
572
                            file->pool, scratch_pool);
573
573
  if (err)
574
574
    {
575
575
      if (err->apr_err == SVN_ERR_FS_CONFLICT)
576
 
        SVN_ERR_W(err, apr_psprintf(file->pool,
 
576
        SVN_ERR_W(err, apr_psprintf(scratch_pool,
577
577
                  _("File '%s' is out of date; try updating"),
578
 
                  svn_dirent_local_style(file->relpath, file->pool)));
 
578
                  svn_dirent_local_style(file->relpath, scratch_pool)));
579
579
      return err;
580
580
    }
581
581
 
582
 
  if (file->checkout->progress.status != 201)
583
 
    {
584
 
      return return_response_err(handler, &file->checkout->progress);
585
 
    }
586
 
 
587
582
  return SVN_NO_ERROR;
588
583
}
589
584
 
788
783
  return SVN_NO_ERROR;
789
784
}
790
785
 
 
786
/* Possible add the lock-token "If:" precondition header to HEADERS if
 
787
   an examination of COMMIT_CTX and RELPATH indicates that this is the
 
788
   right thing to do.
 
789
 
 
790
   Generally speaking, if the client provided a lock token for
 
791
   RELPATH, it's the right thing to do.  There is a notable instance
 
792
   where this is not the case, however.  If the file at RELPATH was
 
793
   explicitly deleted in this commit already, then mod_dav removed its
 
794
   lock token when it fielded the DELETE request, so we don't want to
 
795
   set the lock precondition again.  (See
 
796
   http://subversion.tigris.org/issues/show_bug.cgi?id=3674 for details.)
 
797
*/
 
798
static svn_error_t *
 
799
maybe_set_lock_token_header(serf_bucket_t *headers,
 
800
                            commit_context_t *commit_ctx,
 
801
                            const char *relpath,
 
802
                            apr_pool_t *pool)
 
803
{
 
804
  const char *token;
 
805
 
 
806
  if (! (relpath && commit_ctx->lock_tokens))
 
807
    return SVN_NO_ERROR;
 
808
 
 
809
  if (! svn_hash_gets(commit_ctx->deleted_entries, relpath))
 
810
    {
 
811
      token = svn_hash_gets(commit_ctx->lock_tokens, relpath);
 
812
      if (token)
 
813
        {
 
814
          const char *token_header;
 
815
          const char *token_uri;
 
816
          apr_uri_t uri = commit_ctx->session->session_url;
 
817
 
 
818
          /* Supplying the optional URI affects apache response when
 
819
             the lock is broken, see issue 4369.  When present any URI
 
820
             must be absolute (RFC 2518 9.4). */
 
821
          uri.path = (char *)svn_path_url_add_component2(uri.path, relpath,
 
822
                                                         pool);
 
823
          token_uri = apr_uri_unparse(pool, &uri, 0);
 
824
 
 
825
          token_header = apr_pstrcat(pool, "<", token_uri, "> (<", token, ">)",
 
826
                                     (char *)NULL);
 
827
          serf_bucket_headers_set(headers, "If", token_header);
 
828
        }
 
829
    }
 
830
 
 
831
  return SVN_NO_ERROR;
 
832
}
 
833
 
791
834
static svn_error_t *
792
835
setup_proppatch_headers(serf_bucket_t *headers,
793
836
                        void *baton,
802
845
                                           proppatch->base_revision));
803
846
    }
804
847
 
805
 
  if (proppatch->relpath && proppatch->commit->lock_tokens)
806
 
    {
807
 
      const char *token;
808
 
 
809
 
      token = apr_hash_get(proppatch->commit->lock_tokens, proppatch->relpath,
810
 
                           APR_HASH_KEY_STRING);
811
 
 
812
 
      if (token)
813
 
        {
814
 
          const char *token_header;
815
 
 
816
 
          token_header = apr_pstrcat(pool, "(<", token, ">)", (char *)NULL);
817
 
 
818
 
          serf_bucket_headers_set(headers, "If", token_header);
819
 
        }
820
 
    }
 
848
  SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit,
 
849
                                      proppatch->relpath, pool));
821
850
 
822
851
  return SVN_NO_ERROR;
823
852
}
921
950
  struct proppatch_body_baton_t pbb;
922
951
 
923
952
  handler = apr_pcalloc(pool, sizeof(*handler));
 
953
  handler->handler_pool = pool;
924
954
  handler->method = "PROPPATCH";
925
955
  handler->path = proppatch->path;
926
956
  handler->conn = commit->conn;
935
965
  handler->body_delegate_baton = &pbb;
936
966
 
937
967
  handler->response_handler = svn_ra_serf__handle_multistatus_only;
938
 
  handler->response_baton = &proppatch->progress;
939
 
 
940
 
  svn_ra_serf__request_create(handler);
941
 
 
942
 
  /* If we don't wait for the response, our pool will be gone! */
943
 
  SVN_ERR(svn_ra_serf__context_run_wait(&proppatch->progress.done,
944
 
                                        commit->session, pool));
945
 
 
946
 
  if (proppatch->progress.status != 207 ||
947
 
      proppatch->progress.server_error.error)
 
968
  handler->response_baton = handler;
 
969
 
 
970
  SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
 
971
 
 
972
  if (handler->sline.code != 207
 
973
      || (handler->server_error != NULL
 
974
          && handler->server_error->error != NULL))
948
975
    {
949
 
      return svn_error_create(SVN_ERR_RA_DAV_PROPPATCH_FAILED,
950
 
        return_response_err(handler, &proppatch->progress),
951
 
        _("At least one property change failed; repository is unchanged"));
 
976
      return svn_error_create(
 
977
               SVN_ERR_RA_DAV_PROPPATCH_FAILED,
 
978
               return_response_err(handler),
 
979
               _("At least one property change failed; repository"
 
980
                 " is unchanged"));
952
981
    }
953
982
 
954
983
  return SVN_NO_ERROR;
1020
1049
                              ctx->result_checksum);
1021
1050
    }
1022
1051
 
1023
 
  if (ctx->commit->lock_tokens)
1024
 
    {
1025
 
      const char *token;
1026
 
 
1027
 
      token = apr_hash_get(ctx->commit->lock_tokens, ctx->relpath,
1028
 
                           APR_HASH_KEY_STRING);
1029
 
 
1030
 
      if (token)
1031
 
        {
1032
 
          const char *token_header;
1033
 
 
1034
 
          token_header = apr_pstrcat(pool, "(<", token, ">)", (char *)NULL);
1035
 
 
1036
 
          serf_bucket_headers_set(headers, "If", token_header);
1037
 
        }
1038
 
    }
 
1052
  SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit,
 
1053
                                      ctx->relpath, pool));
1039
1054
 
1040
1055
  return APR_SUCCESS;
1041
1056
}
1056
1071
 
1057
1072
  serf_bucket_headers_set(headers, "Destination", absolute_uri);
1058
1073
 
1059
 
  serf_bucket_headers_set(headers, "Depth", "0");
1060
 
  serf_bucket_headers_set(headers, "Overwrite", "T");
 
1074
  serf_bucket_headers_setn(headers, "Depth", "0");
 
1075
  serf_bucket_headers_setn(headers, "Overwrite", "T");
1061
1076
 
1062
1077
  return SVN_NO_ERROR;
1063
1078
}
1081
1096
  else
1082
1097
    {
1083
1098
      uri.path = (char *)svn_path_url_add_component2(
1084
 
                                    dir->parent_dir->checkout->resource_url,
 
1099
                                    dir->parent_dir->working_url,
1085
1100
                                    dir->name, pool);
1086
1101
    }
1087
1102
  absolute_uri = apr_uri_unparse(pool, &uri, 0);
1088
1103
 
1089
1104
  serf_bucket_headers_set(headers, "Destination", absolute_uri);
1090
1105
 
1091
 
  serf_bucket_headers_set(headers, "Depth", "infinity");
1092
 
  serf_bucket_headers_set(headers, "Overwrite", "T");
 
1106
  serf_bucket_headers_setn(headers, "Depth", "infinity");
 
1107
  serf_bucket_headers_setn(headers, "Overwrite", "T");
1093
1108
 
1094
1109
  /* Implicitly checkout this dir now. */
1095
 
  dir->checkout = apr_pcalloc(dir->pool, sizeof(*dir->checkout));
1096
 
  dir->checkout->pool = dir->pool;
1097
 
  dir->checkout->progress.pool = dir->pool;
1098
 
  dir->checkout->activity_url = dir->commit->activity_url;
1099
 
  dir->checkout->resource_url = apr_pstrdup(dir->checkout->pool, uri.path);
 
1110
  dir->working_url = apr_pstrdup(dir->pool, uri.path);
1100
1111
 
1101
1112
  return SVN_NO_ERROR;
1102
1113
}
1113
1124
 
1114
1125
  if (ctx->lock_token_hash)
1115
1126
    {
1116
 
      ctx->lock_token = apr_hash_get(ctx->lock_token_hash, ctx->path,
1117
 
                                     APR_HASH_KEY_STRING);
 
1127
      ctx->lock_token = svn_hash_gets(ctx->lock_token_hash, ctx->path);
1118
1128
 
1119
1129
      if (ctx->lock_token)
1120
1130
        {
1126
1136
          serf_bucket_headers_set(headers, "If", token_header);
1127
1137
 
1128
1138
          if (ctx->keep_locks)
1129
 
            serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
1130
 
                                    SVN_DAV_OPTION_KEEP_LOCKS);
 
1139
            serf_bucket_headers_setn(headers, SVN_DAV_OPTIONS_HEADER,
 
1140
                                     SVN_DAV_OPTION_KEEP_LOCKS);
1131
1141
        }
1132
1142
    }
1133
1143
 
1182
1192
                     serf_bucket_alloc_t *alloc,
1183
1193
                     apr_pool_t *pool)
1184
1194
{
1185
 
  *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
 
1195
  apr_hash_t *revprops = baton;
 
1196
  svn_skel_t *request_skel;
 
1197
  svn_stringbuf_t *skel_str;
 
1198
 
 
1199
  request_skel = svn_skel__make_empty_list(pool);
 
1200
  if (revprops)
 
1201
    {
 
1202
      svn_skel_t *proplist_skel;
 
1203
 
 
1204
      SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, revprops, pool));
 
1205
      svn_skel__prepend(proplist_skel, request_skel);
 
1206
      svn_skel__prepend_str("create-txn-with-props", request_skel, pool);
 
1207
      skel_str = svn_skel__unparse(request_skel, pool);
 
1208
      *body_bkt = SERF_BUCKET_SIMPLE_STRING(skel_str->data, alloc);
 
1209
    }
 
1210
  else
 
1211
    {
 
1212
      *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
 
1213
    }
 
1214
 
1186
1215
  return SVN_NO_ERROR;
1187
1216
}
1188
1217
 
1206
1235
/* Handler baton for POST request. */
1207
1236
typedef struct post_response_ctx_t
1208
1237
{
1209
 
  svn_ra_serf__simple_request_context_t *request_ctx;
 
1238
  svn_ra_serf__handler_t *handler;
1210
1239
  commit_context_t *commit_ctx;
1211
1240
} post_response_ctx_t;
1212
1241
 
1251
1280
 
1252
1281
 
1253
1282
/* A custom serf_response_handler_t which is mostly a wrapper around
1254
 
   svn_ra_serf__handle_status_only -- it just notices POST response
 
1283
   svn_ra_serf__expect_empty_body -- it just notices POST response
1255
1284
   headers, too.
 
1285
 
1256
1286
   Implements svn_ra_serf__response_handler_t */
1257
1287
static svn_error_t *
1258
1288
post_response_handler(serf_request_t *request,
1259
1289
                      serf_bucket_t *response,
1260
1290
                      void *baton,
1261
 
                      apr_pool_t *pool)
 
1291
                      apr_pool_t *scratch_pool)
1262
1292
{
1263
1293
  post_response_ctx_t *prc = baton;
1264
1294
  serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
1267
1297
  serf_bucket_headers_do(hdrs, post_headers_iterator_callback, prc);
1268
1298
 
1269
1299
  /* Execute the 'real' response handler to XML-parse the repsonse body. */
1270
 
  return svn_ra_serf__handle_status_only(request, response,
1271
 
                                         prc->request_ctx, pool);
 
1300
  return svn_ra_serf__expect_empty_body(request, response,
 
1301
                                        prc->handler, scratch_pool);
1272
1302
}
1273
1303
 
1274
1304
 
1286
1316
  proppatch_context_t *proppatch_ctx;
1287
1317
  dir_context_t *dir;
1288
1318
  apr_hash_index_t *hi;
1289
 
  const char *proppatch_target;
 
1319
  const char *proppatch_target = NULL;
1290
1320
 
1291
1321
  if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session))
1292
1322
    {
1293
 
      svn_ra_serf__simple_request_context_t *post_ctx;
1294
1323
      post_response_ctx_t *prc;
1295
1324
      const char *rel_path;
 
1325
      svn_boolean_t post_with_revprops
 
1326
        = (NULL != svn_hash_gets(ctx->session->supported_posts,
 
1327
                                 "create-txn-with-props"));
1296
1328
 
1297
1329
      /* Create our activity URL now on the server. */
1298
1330
      handler = apr_pcalloc(ctx->pool, sizeof(*handler));
 
1331
      handler->handler_pool = ctx->pool;
1299
1332
      handler->method = "POST";
1300
1333
      handler->body_type = SVN_SKEL_MIME_TYPE;
1301
1334
      handler->body_delegate = create_txn_post_body;
1302
 
      handler->body_delegate_baton = NULL;
 
1335
      handler->body_delegate_baton =
 
1336
        post_with_revprops ? ctx->revprop_table : NULL;
1303
1337
      handler->header_delegate = setup_post_headers;
1304
1338
      handler->header_delegate_baton = NULL;
1305
1339
      handler->path = ctx->session->me_resource;
1306
1340
      handler->conn = ctx->session->conns[0];
1307
1341
      handler->session = ctx->session;
1308
1342
 
1309
 
      post_ctx = apr_pcalloc(ctx->pool, sizeof(*post_ctx));
1310
 
      post_ctx->pool = ctx->pool;
1311
 
 
1312
1343
      prc = apr_pcalloc(ctx->pool, sizeof(*prc));
1313
 
      prc->request_ctx = post_ctx;
 
1344
      prc->handler = handler;
1314
1345
      prc->commit_ctx = ctx;
1315
1346
 
1316
1347
      handler->response_handler = post_response_handler;
1317
1348
      handler->response_baton = prc;
1318
1349
 
1319
 
      svn_ra_serf__request_create(handler);
1320
 
 
1321
 
      SVN_ERR(svn_ra_serf__context_run_wait(&post_ctx->done, ctx->session,
1322
 
                                            ctx->pool));
1323
 
 
1324
 
      if (post_ctx->status != 201)
 
1350
      SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
 
1351
 
 
1352
      if (handler->sline.code != 201)
1325
1353
        {
1326
1354
          apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
1327
 
          switch(post_ctx->status)
 
1355
 
 
1356
          switch (handler->sline.code)
1328
1357
            {
1329
1358
              case 403:
1330
1359
                status = SVN_ERR_RA_DAV_FORBIDDEN;
1337
1366
          return svn_error_createf(status, NULL,
1338
1367
                                   _("%s of '%s': %d %s (%s://%s)"),
1339
1368
                                   handler->method, handler->path,
1340
 
                                   post_ctx->status, post_ctx->reason,
 
1369
                                   handler->sline.code, handler->sline.reason,
1341
1370
                                   ctx->session->session_url.scheme,
1342
1371
                                   ctx->session->session_url.hostinfo);
1343
1372
        }
1366
1395
      dir->removed_props = apr_hash_make(dir->pool);
1367
1396
      dir->url = apr_pstrdup(dir->pool, ctx->txn_root_url);
1368
1397
 
1369
 
      proppatch_target = ctx->txn_url;
 
1398
      /* If we included our revprops in the POST, we need not
 
1399
         PROPPATCH them. */
 
1400
      proppatch_target = post_with_revprops ? NULL : ctx->txn_url;
1370
1401
    }
1371
1402
  else
1372
1403
    {
1373
 
      svn_ra_serf__simple_request_context_t *mkact_ctx;
1374
1404
      const char *activity_str = ctx->session->activity_collection_url;
1375
1405
 
1376
1406
      if (!activity_str)
1377
 
        {
1378
 
          svn_ra_serf__options_context_t *opt_ctx;
1379
 
 
1380
 
          SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, ctx->session,
1381
 
                                                  ctx->session->conns[0],
1382
 
                                                  ctx->session->session_url.path,
1383
 
                                                  ctx->pool));
1384
 
 
1385
 
          SVN_ERR(svn_ra_serf__context_run_wait(
1386
 
                      svn_ra_serf__get_options_done_ptr(opt_ctx),
1387
 
                      ctx->session, ctx->pool));
1388
 
 
1389
 
          activity_str = svn_ra_serf__options_get_activity_collection(opt_ctx);
1390
 
        }
 
1407
        SVN_ERR(svn_ra_serf__v1_get_activity_collection(&activity_str,
 
1408
                                                        ctx->session->conns[0],
 
1409
                                                        ctx->pool,
 
1410
                                                        ctx->pool));
1391
1411
 
1392
1412
      /* Cache the result. */
1393
1413
      if (activity_str)
1408
1428
 
1409
1429
      /* Create our activity URL now on the server. */
1410
1430
      handler = apr_pcalloc(ctx->pool, sizeof(*handler));
 
1431
      handler->handler_pool = ctx->pool;
1411
1432
      handler->method = "MKACTIVITY";
1412
1433
      handler->path = ctx->activity_url;
1413
1434
      handler->conn = ctx->session->conns[0];
1414
1435
      handler->session = ctx->session;
1415
1436
 
1416
 
      mkact_ctx = apr_pcalloc(ctx->pool, sizeof(*mkact_ctx));
1417
 
      mkact_ctx->pool = ctx->pool;
1418
 
 
1419
 
      handler->response_handler = svn_ra_serf__handle_status_only;
1420
 
      handler->response_baton = mkact_ctx;
1421
 
 
1422
 
      svn_ra_serf__request_create(handler);
1423
 
 
1424
 
      SVN_ERR(svn_ra_serf__context_run_wait(&mkact_ctx->done, ctx->session,
1425
 
                                            ctx->pool));
1426
 
 
1427
 
      if (mkact_ctx->status != 201)
 
1437
      handler->response_handler = svn_ra_serf__expect_empty_body;
 
1438
      handler->response_baton = handler;
 
1439
 
 
1440
      SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
 
1441
 
 
1442
      if (handler->sline.code != 201)
1428
1443
        {
1429
1444
          apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
1430
 
          switch(mkact_ctx->status)
 
1445
 
 
1446
          switch (handler->sline.code)
1431
1447
            {
1432
1448
              case 403:
1433
1449
                status = SVN_ERR_RA_DAV_FORBIDDEN;
1440
1456
          return svn_error_createf(status, NULL,
1441
1457
                                   _("%s of '%s': %d %s (%s://%s)"),
1442
1458
                                   handler->method, handler->path,
1443
 
                                   mkact_ctx->status, mkact_ctx->reason,
 
1459
                                   handler->sline.code, handler->sline.reason,
1444
1460
                                   ctx->session->session_url.scheme,
1445
1461
                                   ctx->session->session_url.hostinfo);
1446
1462
        }
1461
1477
      dir->removed_props = apr_hash_make(dir->pool);
1462
1478
 
1463
1479
      SVN_ERR(get_version_url(&dir->url, dir->commit->session,
1464
 
                              dir->commit->conn, dir->relpath,
 
1480
                              dir->relpath,
1465
1481
                              dir->base_revision, ctx->checked_in_url,
1466
 
                              dir->pool));
 
1482
                              dir->pool, dir->pool /* scratch_pool */));
1467
1483
      ctx->checked_in_url = dir->url;
1468
1484
 
1469
1485
      /* Checkout our root dir */
1470
 
      SVN_ERR(checkout_dir(dir));
 
1486
      SVN_ERR(checkout_dir(dir, dir->pool /* scratch_pool */));
1471
1487
 
1472
 
      proppatch_target = ctx->baseline->resource_url;
 
1488
      proppatch_target = ctx->baseline_url;
1473
1489
    }
1474
1490
 
1475
 
 
1476
 
  /* PROPPATCH our revprops and pass them along.  */
1477
 
  proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
1478
 
  proppatch_ctx->pool = dir_pool;
1479
 
  proppatch_ctx->progress.pool = dir_pool;
1480
 
  proppatch_ctx->commit = ctx;
1481
 
  proppatch_ctx->path = proppatch_target;
1482
 
  proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
1483
 
  proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
1484
 
  proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
1485
 
 
1486
 
  for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
1487
 
       hi = apr_hash_next(hi))
 
1491
  /* Unless this is NULL -- which means we don't need to PROPPATCH the
 
1492
     transaction with our revprops -- then, you know, PROPPATCH the
 
1493
     transaction with our revprops.  */
 
1494
  if (proppatch_target)
1488
1495
    {
1489
 
      const void *key;
1490
 
      void *val;
1491
 
      const char *name;
1492
 
      svn_string_t *value;
1493
 
      const char *ns;
1494
 
 
1495
 
      apr_hash_this(hi, &key, NULL, &val);
1496
 
      name = key;
1497
 
      value = val;
1498
 
 
1499
 
      if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
1500
 
        {
1501
 
          ns = SVN_DAV_PROP_NS_SVN;
1502
 
          name += sizeof(SVN_PROP_PREFIX) - 1;
1503
 
        }
1504
 
      else
1505
 
        {
1506
 
          ns = SVN_DAV_PROP_NS_CUSTOM;
1507
 
        }
1508
 
 
1509
 
      svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,
1510
 
                            ns, name, value, proppatch_ctx->pool);
 
1496
      proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
 
1497
      proppatch_ctx->pool = dir_pool;
 
1498
      proppatch_ctx->commit = ctx;
 
1499
      proppatch_ctx->path = proppatch_target;
 
1500
      proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
 
1501
      proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
 
1502
      proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
 
1503
 
 
1504
      for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
 
1505
           hi = apr_hash_next(hi))
 
1506
        {
 
1507
          const char *name = svn__apr_hash_index_key(hi);
 
1508
          svn_string_t *value = svn__apr_hash_index_val(hi);
 
1509
          const char *ns;
 
1510
 
 
1511
          if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
 
1512
            {
 
1513
              ns = SVN_DAV_PROP_NS_SVN;
 
1514
              name += sizeof(SVN_PROP_PREFIX) - 1;
 
1515
            }
 
1516
          else
 
1517
            {
 
1518
              ns = SVN_DAV_PROP_NS_CUSTOM;
 
1519
            }
 
1520
 
 
1521
          svn_ra_serf__set_prop(proppatch_ctx->changed_props,
 
1522
                                proppatch_ctx->path,
 
1523
                                ns, name, value, proppatch_ctx->pool);
 
1524
        }
 
1525
 
 
1526
      SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
1511
1527
    }
1512
1528
 
1513
 
  SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
1514
 
 
1515
1529
  *root_baton = dir;
1516
1530
 
1517
1531
  return SVN_NO_ERROR;
1537
1551
  else
1538
1552
    {
1539
1553
      /* Ensure our directory has been checked out */
1540
 
      SVN_ERR(checkout_dir(dir));
1541
 
      delete_target = svn_path_url_add_component2(dir->checkout->resource_url,
 
1554
      SVN_ERR(checkout_dir(dir, pool /* scratch_pool */));
 
1555
      delete_target = svn_path_url_add_component2(dir->working_url,
1542
1556
                                                  svn_relpath_basename(path,
1543
1557
                                                                       NULL),
1544
1558
                                                  pool);
1546
1560
 
1547
1561
  /* DELETE our entry */
1548
1562
  delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx));
1549
 
  delete_ctx->progress.pool = pool;
1550
1563
  delete_ctx->path = apr_pstrdup(pool, path);
1551
1564
  delete_ctx->revision = revision;
1552
1565
  delete_ctx->lock_token_hash = dir->commit->lock_tokens;
1553
1566
  delete_ctx->keep_locks = dir->commit->keep_locks;
1554
1567
 
1555
1568
  handler = apr_pcalloc(pool, sizeof(*handler));
 
1569
  handler->handler_pool = pool;
1556
1570
  handler->session = dir->commit->session;
1557
1571
  handler->conn = dir->commit->conn;
1558
1572
 
1559
 
  handler->response_handler = svn_ra_serf__handle_status_only;
1560
 
  handler->response_baton = &delete_ctx->progress;
 
1573
  handler->response_handler = svn_ra_serf__expect_empty_body;
 
1574
  handler->response_baton = handler;
1561
1575
 
1562
1576
  handler->header_delegate = setup_delete_headers;
1563
1577
  handler->header_delegate_baton = delete_ctx;
1565
1579
  handler->method = "DELETE";
1566
1580
  handler->path = delete_target;
1567
1581
 
1568
 
  svn_ra_serf__request_create(handler);
1569
 
 
1570
 
  err = svn_ra_serf__context_run_wait(&delete_ctx->progress.done,
1571
 
                                      dir->commit->session, pool);
 
1582
  err = svn_ra_serf__context_run_one(handler, pool);
1572
1583
 
1573
1584
  if (err &&
1574
1585
      (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN ||
1586
1597
      handler->body_delegate_baton = delete_ctx;
1587
1598
      handler->body_type = "text/xml";
1588
1599
 
1589
 
      svn_ra_serf__request_create(handler);
1590
 
 
1591
 
      delete_ctx->progress.done = 0;
1592
 
 
1593
 
      SVN_ERR(svn_ra_serf__context_run_wait(&delete_ctx->progress.done,
1594
 
                                            dir->commit->session, pool));
 
1600
      SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
1595
1601
    }
1596
1602
  else if (err)
1597
1603
    {
1599
1605
    }
1600
1606
 
1601
1607
  /* 204 No Content: item successfully deleted */
1602
 
  if (delete_ctx->progress.status != 204)
 
1608
  if (handler->sline.code != 204)
1603
1609
    {
1604
 
      return return_response_err(handler, &delete_ctx->progress);
 
1610
      return svn_error_trace(return_response_err(handler));
1605
1611
    }
1606
1612
 
1607
 
  apr_hash_set(dir->commit->deleted_entries,
1608
 
               apr_pstrdup(dir->commit->pool, path), APR_HASH_KEY_STRING,
1609
 
               (void*)1);
 
1613
  svn_hash_sets(dir->commit->deleted_entries,
 
1614
                apr_pstrdup(dir->commit->pool, path), (void *)1);
1610
1615
 
1611
1616
  return SVN_NO_ERROR;
1612
1617
}
1622
1627
  dir_context_t *parent = parent_baton;
1623
1628
  dir_context_t *dir;
1624
1629
  svn_ra_serf__handler_t *handler;
1625
 
  svn_ra_serf__simple_request_context_t *add_dir_ctx;
1626
1630
  apr_status_t status;
1627
1631
  const char *mkcol_target;
1628
1632
 
1649
1653
  else
1650
1654
    {
1651
1655
      /* Ensure our parent is checked out. */
1652
 
      SVN_ERR(checkout_dir(parent));
 
1656
      SVN_ERR(checkout_dir(parent, dir->pool /* scratch_pool */));
1653
1657
 
1654
1658
      dir->url = svn_path_url_add_component2(parent->commit->checked_in_url,
1655
1659
                                             dir->name, dir->pool);
1656
1660
      mkcol_target = svn_path_url_add_component2(
1657
 
                               parent->checkout->resource_url,
 
1661
                               parent->working_url,
1658
1662
                               dir->name, dir->pool);
1659
1663
    }
1660
1664
 
1661
1665
  handler = apr_pcalloc(dir->pool, sizeof(*handler));
 
1666
  handler->handler_pool = dir->pool;
1662
1667
  handler->conn = dir->commit->conn;
1663
1668
  handler->session = dir->commit->session;
1664
1669
 
1665
 
  add_dir_ctx = apr_pcalloc(dir->pool, sizeof(*add_dir_ctx));
1666
 
  add_dir_ctx->pool = dir->pool;
1667
 
 
1668
 
  handler->response_handler = svn_ra_serf__handle_status_only;
1669
 
  handler->response_baton = add_dir_ctx;
 
1670
  handler->response_handler = svn_ra_serf__expect_empty_body;
 
1671
  handler->response_baton = handler;
1670
1672
  if (!dir->copy_path)
1671
1673
    {
1672
1674
      handler->method = "MKCOL";
1675
1677
  else
1676
1678
    {
1677
1679
      apr_uri_t uri;
1678
 
      const char *rel_copy_path, *basecoll_url, *req_url;
 
1680
      const char *req_url;
1679
1681
 
1680
1682
      status = apr_uri_parse(dir->pool, dir->copy_path, &uri);
1681
1683
      if (status)
1685
1687
                                   dir->copy_path);
1686
1688
        }
1687
1689
 
1688
 
      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &rel_copy_path,
1689
 
                                             dir->commit->session,
1690
 
                                             dir->commit->conn,
1691
 
                                             uri.path, dir->copy_revision,
1692
 
                                             NULL, dir_pool));
1693
 
      req_url = svn_path_url_add_component2(basecoll_url, rel_copy_path,
1694
 
                                            dir->pool);
 
1690
      /* ### conn==NULL for session->conns[0]. same as commit->conn.  */
 
1691
      SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
 
1692
                                          dir->commit->session,
 
1693
                                          NULL /* conn */,
 
1694
                                          uri.path, dir->copy_revision,
 
1695
                                          dir_pool, dir_pool));
1695
1696
 
1696
1697
      handler->method = "COPY";
1697
1698
      handler->path = req_url;
1700
1701
      handler->header_delegate_baton = dir;
1701
1702
    }
1702
1703
 
1703
 
  svn_ra_serf__request_create(handler);
1704
 
 
1705
 
  SVN_ERR(svn_ra_serf__context_run_wait(&add_dir_ctx->done,
1706
 
                                        dir->commit->session, dir->pool));
1707
 
 
1708
 
  switch (add_dir_ctx->status)
 
1704
  SVN_ERR(svn_ra_serf__context_run_one(handler, dir->pool));
 
1705
 
 
1706
  switch (handler->sline.code)
1709
1707
    {
1710
1708
      case 201: /* Created:    item was successfully copied */
1711
1709
      case 204: /* No Content: item successfully replaced an existing target */
1712
1710
        break;
1713
1711
 
1714
1712
      case 403:
1715
 
        SVN_ERR(add_dir_ctx->server_error.error);
1716
1713
        return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL,
1717
1714
                                _("Access to '%s' forbidden"),
1718
1715
                                 handler->path);
1719
1716
      default:
1720
 
        SVN_ERR(add_dir_ctx->server_error.error);
1721
1717
        return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
1722
1718
                                 _("Adding directory failed: %s on %s "
1723
1719
                                   "(%d %s)"),
1724
1720
                                 handler->method, handler->path,
1725
 
                                 add_dir_ctx->status, add_dir_ctx->reason);
 
1721
                                 handler->sline.code, handler->sline.reason);
1726
1722
    }
1727
1723
 
1728
1724
  *child_baton = dir;
1762
1758
  else
1763
1759
    {
1764
1760
      SVN_ERR(get_version_url(&dir->url,
1765
 
                              dir->commit->session, dir->commit->conn,
 
1761
                              dir->commit->session,
1766
1762
                              dir->relpath, dir->base_revision,
1767
 
                              dir->commit->checked_in_url, dir->pool));
 
1763
                              dir->commit->checked_in_url,
 
1764
                              dir->pool, dir->pool /* scratch_pool */));
1768
1765
    }
1769
1766
  *child_baton = dir;
1770
1767
 
1789
1786
  else
1790
1787
    {
1791
1788
      /* Ensure we have a checked out dir. */
1792
 
      SVN_ERR(checkout_dir(dir));
 
1789
      SVN_ERR(checkout_dir(dir, pool /* scratch_pool */));
1793
1790
 
1794
 
      proppatch_target = dir->checkout->resource_url;
 
1791
      proppatch_target = dir->working_url;
1795
1792
    }
1796
1793
 
1797
1794
  name = apr_pstrdup(dir->pool, name);
1813
1810
    }
1814
1811
  else
1815
1812
    {
1816
 
      value = svn_string_create("", dir->pool);
 
1813
      value = svn_string_create_empty(dir->pool);
1817
1814
      svn_ra_serf__set_prop(dir->removed_props, proppatch_target,
1818
1815
                            ns, name, value, dir->pool);
1819
1816
    }
1839
1836
 
1840
1837
      proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
1841
1838
      proppatch_ctx->pool = pool;
1842
 
      proppatch_ctx->progress.pool = pool;
1843
1839
      proppatch_ctx->commit = dir->commit;
1844
1840
      proppatch_ctx->relpath = dir->relpath;
1845
1841
      proppatch_ctx->changed_props = dir->changed_props;
1852
1848
        }
1853
1849
      else
1854
1850
        {
1855
 
          proppatch_ctx->path = dir->checkout->resource_url;
 
1851
          proppatch_ctx->path = dir->working_url;
1856
1852
        }
1857
1853
 
1858
1854
      SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, dir->pool));
1900
1896
  else
1901
1897
    {
1902
1898
      /* Ensure our parent directory has been checked out */
1903
 
      SVN_ERR(checkout_dir(dir));
 
1899
      SVN_ERR(checkout_dir(dir, new_file->pool /* scratch_pool */));
1904
1900
 
1905
1901
      new_file->url =
1906
 
        svn_path_url_add_component2(dir->checkout->resource_url,
 
1902
        svn_path_url_add_component2(dir->working_url,
1907
1903
                                    new_file->name, new_file->pool);
1908
1904
    }
1909
1905
 
1910
1906
  while (deleted_parent && deleted_parent[0] != '\0')
1911
1907
    {
1912
 
      if (apr_hash_get(dir->commit->deleted_entries,
1913
 
                       deleted_parent, APR_HASH_KEY_STRING))
 
1908
      if (svn_hash_gets(dir->commit->deleted_entries, deleted_parent))
1914
1909
        {
1915
1910
          break;
1916
1911
        }
1920
1915
  if (! ((dir->added && !dir->copy_path) ||
1921
1916
         (deleted_parent && deleted_parent[0] != '\0')))
1922
1917
    {
1923
 
      svn_ra_serf__simple_request_context_t *head_ctx;
1924
1918
      svn_ra_serf__handler_t *handler;
1925
1919
 
1926
 
      head_ctx = apr_pcalloc(new_file->pool, sizeof(*head_ctx));
1927
 
      head_ctx->pool = new_file->pool;
1928
 
 
1929
1920
      handler = apr_pcalloc(new_file->pool, sizeof(*handler));
 
1921
      handler->handler_pool = new_file->pool;
1930
1922
      handler->session = new_file->commit->session;
1931
1923
      handler->conn = new_file->commit->conn;
1932
1924
      handler->method = "HEAD";
1933
1925
      handler->path = svn_path_url_add_component2(
1934
1926
        dir->commit->session->session_url.path,
1935
1927
        path, new_file->pool);
1936
 
      handler->response_handler = svn_ra_serf__handle_status_only;
1937
 
      handler->response_baton = head_ctx;
1938
 
      svn_ra_serf__request_create(handler);
1939
 
 
1940
 
      SVN_ERR(svn_ra_serf__context_run_wait(&head_ctx->done,
1941
 
                                            new_file->commit->session,
1942
 
                                            new_file->pool));
1943
 
 
1944
 
      if (head_ctx->status != 404)
 
1928
      handler->response_handler = svn_ra_serf__expect_empty_body;
 
1929
      handler->response_baton = handler;
 
1930
 
 
1931
      SVN_ERR(svn_ra_serf__context_run_one(handler, new_file->pool));
 
1932
 
 
1933
      if (handler->sline.code != 404)
1945
1934
        {
1946
 
          return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL,
 
1935
          if (handler->sline.code != 200)
 
1936
            {
 
1937
              svn_error_t *err;
 
1938
 
 
1939
              err = svn_ra_serf__error_on_status(handler->sline,
 
1940
                                                 handler->path,
 
1941
                                                 handler->location);
 
1942
 
 
1943
              SVN_ERR(err);
 
1944
            }
 
1945
 
 
1946
          return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
1947
1947
                                   _("File '%s' already exists"), path);
1948
1948
        }
1949
1949
    }
1985
1985
  else
1986
1986
    {
1987
1987
      /* CHECKOUT the file into our activity. */
1988
 
      SVN_ERR(checkout_file(new_file));
 
1988
      SVN_ERR(checkout_file(new_file, new_file->pool /* scratch_pool */));
1989
1989
 
1990
 
      new_file->url = new_file->checkout->resource_url;
 
1990
      new_file->url = new_file->working_url;
1991
1991
    }
1992
1992
 
1993
1993
  *file_baton = new_file;
2024
2024
  ctx->stream = svn_stream_create(ctx, pool);
2025
2025
  svn_stream_set_write(ctx->stream, svndiff_stream_write);
2026
2026
 
2027
 
  svn_txdelta_to_svndiff2(handler, handler_baton, ctx->stream, 0, pool);
 
2027
  svn_txdelta_to_svndiff3(handler, handler_baton, ctx->stream, 0,
 
2028
                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
2028
2029
 
2029
2030
  if (base_checksum)
2030
2031
    ctx->base_checksum = apr_pstrdup(ctx->pool, base_checksum);
2061
2062
    }
2062
2063
  else
2063
2064
    {
2064
 
      value = svn_string_create("", file->pool);
 
2065
      value = svn_string_create_empty(file->pool);
2065
2066
 
2066
2067
      svn_ra_serf__set_prop(file->removed_props, file->url,
2067
2068
                            ns, name, value, file->pool);
2073
2074
static svn_error_t *
2074
2075
close_file(void *file_baton,
2075
2076
           const char *text_checksum,
2076
 
           apr_pool_t *pool)
 
2077
           apr_pool_t *scratch_pool)
2077
2078
{
2078
2079
  file_context_t *ctx = file_baton;
2079
2080
  svn_boolean_t put_empty_file = FALSE;
2084
2085
  if (ctx->copy_path)
2085
2086
    {
2086
2087
      svn_ra_serf__handler_t *handler;
2087
 
      svn_ra_serf__simple_request_context_t *copy_ctx;
2088
2088
      apr_uri_t uri;
2089
 
      const char *rel_copy_path, *basecoll_url, *req_url;
 
2089
      const char *req_url;
2090
2090
 
2091
 
      status = apr_uri_parse(pool, ctx->copy_path, &uri);
 
2091
      status = apr_uri_parse(scratch_pool, ctx->copy_path, &uri);
2092
2092
      if (status)
2093
2093
        {
2094
2094
          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
2096
2096
                                   ctx->copy_path);
2097
2097
        }
2098
2098
 
2099
 
      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &rel_copy_path,
2100
 
                                             ctx->commit->session,
2101
 
                                             ctx->commit->conn,
2102
 
                                             uri.path, ctx->copy_revision,
2103
 
                                             NULL, pool));
2104
 
      req_url = svn_path_url_add_component2(basecoll_url, rel_copy_path, pool);
 
2099
      /* ### conn==NULL for session->conns[0]. same as commit->conn.  */
 
2100
      SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
 
2101
                                          ctx->commit->session,
 
2102
                                          NULL /* conn */,
 
2103
                                          uri.path, ctx->copy_revision,
 
2104
                                          scratch_pool, scratch_pool));
2105
2105
 
2106
 
      handler = apr_pcalloc(pool, sizeof(*handler));
 
2106
      handler = apr_pcalloc(scratch_pool, sizeof(*handler));
 
2107
      handler->handler_pool = scratch_pool;
2107
2108
      handler->method = "COPY";
2108
2109
      handler->path = req_url;
2109
2110
      handler->conn = ctx->commit->conn;
2110
2111
      handler->session = ctx->commit->session;
2111
2112
 
2112
 
      copy_ctx = apr_pcalloc(pool, sizeof(*copy_ctx));
2113
 
      copy_ctx->pool = pool;
2114
 
 
2115
 
      handler->response_handler = svn_ra_serf__handle_status_only;
2116
 
      handler->response_baton = copy_ctx;
 
2113
      handler->response_handler = svn_ra_serf__expect_empty_body;
 
2114
      handler->response_baton = handler;
2117
2115
 
2118
2116
      handler->header_delegate = setup_copy_file_headers;
2119
2117
      handler->header_delegate_baton = ctx;
2120
2118
 
2121
 
      svn_ra_serf__request_create(handler);
2122
 
 
2123
 
      SVN_ERR(svn_ra_serf__context_run_wait(&copy_ctx->done,
2124
 
                                            ctx->commit->session, pool));
2125
 
 
2126
 
      if (copy_ctx->status != 201 && copy_ctx->status != 204)
 
2119
      SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
 
2120
 
 
2121
      if (handler->sline.code != 201 && handler->sline.code != 204)
2127
2122
        {
2128
 
          return return_response_err(handler, copy_ctx);
 
2123
          return svn_error_trace(return_response_err(handler));
2129
2124
        }
2130
2125
    }
2131
2126
 
2139
2134
  if (ctx->stream || put_empty_file)
2140
2135
    {
2141
2136
      svn_ra_serf__handler_t *handler;
2142
 
      svn_ra_serf__simple_request_context_t *put_ctx;
2143
2137
 
2144
 
      handler = apr_pcalloc(pool, sizeof(*handler));
 
2138
      handler = apr_pcalloc(scratch_pool, sizeof(*handler));
 
2139
      handler->handler_pool = scratch_pool;
2145
2140
      handler->method = "PUT";
2146
2141
      handler->path = ctx->url;
2147
2142
      handler->conn = ctx->commit->conn;
2148
2143
      handler->session = ctx->commit->session;
2149
2144
 
2150
 
      put_ctx = apr_pcalloc(pool, sizeof(*put_ctx));
2151
 
      put_ctx->pool = pool;
2152
 
 
2153
 
      handler->response_handler = svn_ra_serf__handle_status_only;
2154
 
      handler->response_baton = put_ctx;
 
2145
      handler->response_handler = svn_ra_serf__expect_empty_body;
 
2146
      handler->response_baton = handler;
2155
2147
 
2156
2148
      if (put_empty_file)
2157
2149
        {
2163
2155
        {
2164
2156
          handler->body_delegate = create_put_body;
2165
2157
          handler->body_delegate_baton = ctx;
2166
 
          handler->body_type = "application/vnd.svn-svndiff";
 
2158
          handler->body_type = SVN_SVNDIFF_MIME_TYPE;
2167
2159
        }
2168
2160
 
2169
2161
      handler->header_delegate = setup_put_headers;
2170
2162
      handler->header_delegate_baton = ctx;
2171
2163
 
2172
 
      svn_ra_serf__request_create(handler);
2173
 
 
2174
 
      SVN_ERR(svn_ra_serf__context_run_wait(&put_ctx->done,
2175
 
                                            ctx->commit->session, pool));
2176
 
 
2177
 
      if (put_ctx->status != 204 && put_ctx->status != 201)
 
2164
      SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
 
2165
 
 
2166
      if (handler->sline.code != 204 && handler->sline.code != 201)
2178
2167
        {
2179
 
          return return_response_err(handler, put_ctx);
 
2168
          return svn_error_trace(return_response_err(handler));
2180
2169
        }
2181
2170
    }
2182
2171
 
2183
2172
  if (ctx->svndiff)
2184
 
    SVN_ERR(svn_io_file_close(ctx->svndiff, pool));
 
2173
    SVN_ERR(svn_io_file_close(ctx->svndiff, scratch_pool));
2185
2174
 
2186
2175
  /* If we had any prop changes, push them via PROPPATCH. */
2187
2176
  if (apr_hash_count(ctx->changed_props) ||
2191
2180
 
2192
2181
      proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch));
2193
2182
      proppatch->pool = ctx->pool;
2194
 
      proppatch->progress.pool = pool;
2195
2183
      proppatch->relpath = ctx->relpath;
2196
2184
      proppatch->path = ctx->url;
2197
2185
      proppatch->commit = ctx->commit;
2210
2198
           apr_pool_t *pool)
2211
2199
{
2212
2200
  commit_context_t *ctx = edit_baton;
2213
 
  svn_ra_serf__merge_context_t *merge_ctx;
2214
 
  svn_ra_serf__simple_request_context_t *delete_ctx;
2215
 
  svn_ra_serf__handler_t *handler;
2216
 
  svn_boolean_t *merge_done;
2217
2201
  const char *merge_target =
2218
2202
    ctx->activity_url ? ctx->activity_url : ctx->txn_url;
 
2203
  const svn_commit_info_t *commit_info;
 
2204
  int response_code;
2219
2205
 
2220
2206
  /* MERGE our activity */
2221
 
  SVN_ERR(svn_ra_serf__merge_create_req(&merge_ctx, ctx->session,
2222
 
                                        ctx->session->conns[0],
2223
 
                                        merge_target,
2224
 
                                        ctx->lock_tokens,
2225
 
                                        ctx->keep_locks,
2226
 
                                        pool));
2227
 
 
2228
 
  merge_done = svn_ra_serf__merge_get_done_ptr(merge_ctx);
2229
 
 
2230
 
  SVN_ERR(svn_ra_serf__context_run_wait(merge_done, ctx->session, pool));
2231
 
 
2232
 
  if (svn_ra_serf__merge_get_status(merge_ctx) != 200)
 
2207
  SVN_ERR(svn_ra_serf__run_merge(&commit_info, &response_code,
 
2208
                                 ctx->session,
 
2209
                                 ctx->session->conns[0],
 
2210
                                 merge_target,
 
2211
                                 ctx->lock_tokens,
 
2212
                                 ctx->keep_locks,
 
2213
                                 pool, pool));
 
2214
 
 
2215
  if (response_code != 200)
2233
2216
    {
2234
2217
      return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
2235
2218
                               _("MERGE request failed: returned %d "
2236
2219
                                 "(during commit)"),
2237
 
                               svn_ra_serf__merge_get_status(merge_ctx));
 
2220
                               response_code);
2238
2221
    }
2239
2222
 
2240
2223
  /* Inform the WC that we did a commit.  */
2241
2224
  if (ctx->callback)
2242
 
    SVN_ERR(ctx->callback(svn_ra_serf__merge_get_commit_info(merge_ctx),
2243
 
                          ctx->callback_baton, pool));
 
2225
    SVN_ERR(ctx->callback(commit_info, ctx->callback_baton, pool));
2244
2226
 
2245
2227
  /* If we're using activities, DELETE our completed activity.  */
2246
2228
  if (ctx->activity_url)
2247
2229
    {
 
2230
      svn_ra_serf__handler_t *handler;
 
2231
 
2248
2232
      handler = apr_pcalloc(pool, sizeof(*handler));
 
2233
      handler->handler_pool = pool;
2249
2234
      handler->method = "DELETE";
2250
2235
      handler->path = ctx->activity_url;
2251
2236
      handler->conn = ctx->conn;
2252
2237
      handler->session = ctx->session;
2253
2238
 
2254
 
      delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx));
2255
 
      delete_ctx->pool = pool;
2256
 
 
2257
 
      handler->response_handler = svn_ra_serf__handle_status_only;
2258
 
      handler->response_baton = delete_ctx;
2259
 
 
2260
 
      svn_ra_serf__request_create(handler);
2261
 
 
2262
 
      SVN_ERR(svn_ra_serf__context_run_wait(&delete_ctx->done, ctx->session,
2263
 
                                            pool));
2264
 
 
2265
 
      SVN_ERR_ASSERT(delete_ctx->status == 204);
 
2239
      handler->response_handler = svn_ra_serf__expect_empty_body;
 
2240
      handler->response_baton = handler;
 
2241
 
 
2242
      SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
 
2243
 
 
2244
      SVN_ERR_ASSERT(handler->sline.code == 204);
2266
2245
    }
2267
2246
 
2268
2247
  return SVN_NO_ERROR;
2274
2253
{
2275
2254
  commit_context_t *ctx = edit_baton;
2276
2255
  svn_ra_serf__handler_t *handler;
2277
 
  svn_ra_serf__simple_request_context_t *delete_ctx;
2278
2256
 
2279
2257
  /* If an activity or transaction wasn't even created, don't bother
2280
2258
     trying to delete it. */
2287
2265
 
2288
2266
  /* DELETE our aborted activity */
2289
2267
  handler = apr_pcalloc(pool, sizeof(*handler));
 
2268
  handler->handler_pool = pool;
2290
2269
  handler->method = "DELETE";
2291
2270
  handler->conn = ctx->session->conns[0];
2292
2271
  handler->session = ctx->session;
2293
2272
 
2294
 
  delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx));
2295
 
  delete_ctx->pool = pool;
2296
 
 
2297
 
  handler->response_handler = svn_ra_serf__handle_status_only;
2298
 
  handler->response_baton = delete_ctx;
 
2273
  handler->response_handler = svn_ra_serf__expect_empty_body;
 
2274
  handler->response_baton = handler;
2299
2275
 
2300
2276
  if (USING_HTTPV2_COMMIT_SUPPORT(ctx)) /* HTTP v2 */
2301
2277
    handler->path = ctx->txn_url;
2302
2278
  else
2303
2279
    handler->path = ctx->activity_url;
2304
2280
 
2305
 
  svn_ra_serf__request_create(handler);
2306
 
 
2307
 
  SVN_ERR(svn_ra_serf__context_run_wait(&delete_ctx->done, ctx->session,
2308
 
                                        pool));
 
2281
  SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
2309
2282
 
2310
2283
  /* 204 if deleted,
2311
2284
     403 if DELETE was forbidden (indicates MKACTIVITY was forbidden too),
2312
2285
     404 if the activity wasn't found. */
2313
 
  if (delete_ctx->status != 204 &&
2314
 
      delete_ctx->status != 403 &&
2315
 
      delete_ctx->status != 404
 
2286
  if (handler->sline.code != 204
 
2287
      && handler->sline.code != 403
 
2288
      && handler->sline.code != 404
2316
2289
      )
2317
2290
    {
2318
 
      SVN_ERR_MALFUNCTION();
 
2291
      return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
 
2292
                               _("DELETE returned unexpected status: %d"),
 
2293
                               handler->sline.code);
2319
2294
    }
2320
2295
 
2321
2296
  return SVN_NO_ERROR;
2335
2310
  svn_ra_serf__session_t *session = ra_session->priv;
2336
2311
  svn_delta_editor_t *editor;
2337
2312
  commit_context_t *ctx;
2338
 
  apr_hash_index_t *hi;
 
2313
  const char *repos_root;
 
2314
  const char *base_relpath;
 
2315
  svn_boolean_t supports_ephemeral_props;
2339
2316
 
2340
2317
  ctx = apr_pcalloc(pool, sizeof(*ctx));
2341
2318
 
2344
2321
  ctx->session = session;
2345
2322
  ctx->conn = session->conns[0];
2346
2323
 
2347
 
  ctx->revprop_table = apr_hash_make(pool);
2348
 
  for (hi = apr_hash_first(pool, revprop_table); hi; hi = apr_hash_next(hi))
 
2324
  ctx->revprop_table = svn_prop_hash_dup(revprop_table, pool);
 
2325
 
 
2326
  /* If the server supports ephemeral properties, add some carrying
 
2327
     interesting version information. */
 
2328
  SVN_ERR(svn_ra_serf__has_capability(ra_session, &supports_ephemeral_props,
 
2329
                                      SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
 
2330
                                      pool));
 
2331
  if (supports_ephemeral_props)
2349
2332
    {
2350
 
      const void *key;
2351
 
      apr_ssize_t klen;
2352
 
      void *val;
2353
 
 
2354
 
      apr_hash_this(hi, &key, &klen, &val);
2355
 
      apr_hash_set(ctx->revprop_table, apr_pstrdup(pool, key), klen,
2356
 
                   svn_string_dup(val, pool));
 
2333
      svn_hash_sets(ctx->revprop_table,
 
2334
                    apr_pstrdup(pool, SVN_PROP_TXN_CLIENT_COMPAT_VERSION),
 
2335
                    svn_string_create(SVN_VER_NUMBER, pool));
 
2336
      svn_hash_sets(ctx->revprop_table,
 
2337
                    apr_pstrdup(pool, SVN_PROP_TXN_USER_AGENT),
 
2338
                    svn_string_create(session->useragent, pool));
2357
2339
    }
2358
2340
 
2359
2341
  ctx->callback = callback;
2382
2364
  *ret_editor = editor;
2383
2365
  *edit_baton = ctx;
2384
2366
 
 
2367
  SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root, pool));
 
2368
  base_relpath = svn_uri_skip_ancestor(repos_root, session->session_url_str,
 
2369
                                       pool);
 
2370
 
 
2371
  SVN_ERR(svn_editor__insert_shims(ret_editor, edit_baton, *ret_editor,
 
2372
                                   *edit_baton, repos_root, base_relpath,
 
2373
                                   session->shim_callbacks, pool, pool));
 
2374
 
2385
2375
  return SVN_NO_ERROR;
2386
2376
}
2387
2377
 
2396
2386
  svn_ra_serf__session_t *session = ra_session->priv;
2397
2387
  proppatch_context_t *proppatch_ctx;
2398
2388
  commit_context_t *commit;
2399
 
  const char *vcc_url, *proppatch_target, *ns;
2400
 
  apr_hash_t *props;
 
2389
  const char *proppatch_target;
 
2390
  const char *ns;
2401
2391
  svn_error_t *err;
2402
2392
 
2403
2393
  if (old_value_p)
2424
2414
    }
2425
2415
  else
2426
2416
    {
2427
 
      svn_ra_serf__propfind_context_t *propfind_ctx;
 
2417
      const char *vcc_url;
2428
2418
 
2429
2419
      SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, commit->session,
2430
2420
                                        commit->conn, pool));
2431
2421
 
2432
 
      props = apr_hash_make(pool);
2433
 
 
2434
 
      /* ### switch to svn_ra_serf__retrieve_props  */
2435
 
      SVN_ERR(svn_ra_serf__deliver_props(&propfind_ctx, props, commit->session,
2436
 
                                         commit->conn, vcc_url, rev, "0",
2437
 
                                         checked_in_props, NULL, pool));
2438
 
      SVN_ERR(svn_ra_serf__wait_for_props(propfind_ctx, commit->session, pool));
2439
 
 
2440
 
      proppatch_target = svn_ra_serf__get_ver_prop(props, vcc_url, rev,
2441
 
                                                   "DAV:", "href");
 
2422
      SVN_ERR(svn_ra_serf__fetch_dav_prop(&proppatch_target,
 
2423
                                          commit->conn, vcc_url, rev,
 
2424
                                          "href",
 
2425
                                          pool, pool));
2442
2426
    }
2443
2427
 
2444
2428
  if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
2454
2438
  /* PROPPATCH our log message and pass it along.  */
2455
2439
  proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
2456
2440
  proppatch_ctx->pool = pool;
2457
 
  proppatch_ctx->progress.pool = pool;
2458
2441
  proppatch_ctx->commit = commit;
2459
2442
  proppatch_ctx->path = proppatch_target;
2460
2443
  proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
2474
2457
    }
2475
2458
  else if (old_value_p)
2476
2459
    {
2477
 
      svn_string_t *dummy_value = svn_string_create("", proppatch_ctx->pool);
 
2460
      svn_string_t *dummy_value = svn_string_create_empty(proppatch_ctx->pool);
2478
2461
 
2479
2462
      svn_ra_serf__set_prop(proppatch_ctx->previous_removed_props,
2480
2463
                            proppatch_ctx->path,
2488
2471
    }
2489
2472
  else
2490
2473
    {
2491
 
      value = svn_string_create("", proppatch_ctx->pool);
 
2474
      value = svn_string_create_empty(proppatch_ctx->pool);
2492
2475
 
2493
2476
      svn_ra_serf__set_prop(proppatch_ctx->removed_props, proppatch_ctx->path,
2494
2477
                            ns, name, value, proppatch_ctx->pool);