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

« back to all changes in this revision

Viewing changes to subversion/libsvn_ra_serf/serf.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:
27
27
#include <apr_want.h>
28
28
 
29
29
#include <apr_uri.h>
30
 
 
31
 
#include <expat.h>
32
 
 
33
30
#include <serf.h>
34
31
 
35
32
#include "svn_pools.h"
48
45
#include "private/svn_dav_protocol.h"
49
46
#include "private/svn_dep_compat.h"
50
47
#include "private/svn_fspath.h"
 
48
#include "private/svn_subr_private.h"
51
49
#include "svn_private_config.h"
52
50
 
53
51
#include "ra_serf.h"
54
52
 
55
53
 
 
54
/* Implements svn_ra__vtable_t.get_version(). */
56
55
static const svn_version_t *
57
56
ra_serf_version(void)
58
57
{
62
61
#define RA_SERF_DESCRIPTION \
63
62
    N_("Module for accessing a repository via WebDAV protocol using serf.")
64
63
 
 
64
#define RA_SERF_DESCRIPTION_VER \
 
65
    N_("Module for accessing a repository via WebDAV protocol using serf.\n" \
 
66
       "  - using serf %d.%d.%d")
 
67
 
 
68
/* Implements svn_ra__vtable_t.get_description(). */
65
69
static const char *
66
 
ra_serf_get_description(void)
 
70
ra_serf_get_description(apr_pool_t *pool)
67
71
{
68
 
  return _(RA_SERF_DESCRIPTION);
 
72
  int major, minor, patch;
 
73
 
 
74
  serf_lib_version(&major, &minor, &patch);
 
75
  return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch);
69
76
}
70
77
 
 
78
/* Implements svn_ra__vtable_t.get_schemes(). */
71
79
static const char * const *
72
80
ra_serf_get_schemes(apr_pool_t *pool)
73
81
{
103
111
 
104
112
  if (http_auth_types)
105
113
    {
106
 
      char *token, *last;
 
114
      char *token;
107
115
      char *auth_types_list = apr_palloc(pool, strlen(http_auth_types) + 1);
108
116
      apr_collapse_spaces(auth_types_list, http_auth_types);
109
 
      while ((token = apr_strtok(auth_types_list, ";", &last)) != NULL)
 
117
      while ((token = svn_cstring_tokenize(";", &auth_types_list)) != NULL)
110
118
        {
111
 
          auth_types_list = NULL;
112
119
          if (svn_cstring_casecmp("basic", token) == 0)
113
120
            *authn_types |= SERF_AUTHN_BASIC;
114
121
          else if (svn_cstring_casecmp("digest", token) == 0)
119
126
            *authn_types |= SERF_AUTHN_NEGOTIATE;
120
127
          else
121
128
            return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
122
 
                                     _("Invalid config: unknown http auth"
123
 
                                       "type '%s'"), token);
 
129
                                     _("Invalid config: unknown %s "
 
130
                                       "'%s'"),
 
131
                                     SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, token);
124
132
      }
125
133
    }
126
134
  else
131
139
 
132
140
  return SVN_NO_ERROR;
133
141
}
134
 
#define DEFAULT_HTTP_TIMEOUT 3600
 
142
 
 
143
/* Default HTTP timeout (in seconds); overridden by the 'http-timeout'
 
144
   runtime configuration variable. */
 
145
#define DEFAULT_HTTP_TIMEOUT 600
 
146
 
 
147
/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */
 
148
#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests"
 
149
 
 
150
 
135
151
static svn_error_t *
136
152
load_config(svn_ra_serf__session_t *session,
137
153
            apr_hash_t *config_hash,
144
160
  const char *timeout_str = NULL;
145
161
  const char *exceptions;
146
162
  apr_port_t proxy_port;
147
 
  svn_boolean_t is_exception = FALSE;
 
163
  svn_tristate_t chunked_requests;
148
164
 
149
165
  if (config_hash)
150
166
    {
151
 
      config = apr_hash_get(config_hash, SVN_CONFIG_CATEGORY_SERVERS,
152
 
                            APR_HASH_KEY_STRING);
153
 
      config_client = apr_hash_get(config_hash, SVN_CONFIG_CATEGORY_CONFIG,
154
 
                                   APR_HASH_KEY_STRING);
 
167
      config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS);
 
168
      config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG);
155
169
    }
156
170
  else
157
171
    {
184
198
  /* Use the default proxy-specific settings if and only if
185
199
     "http-proxy-exceptions" is not set to exclude this host. */
186
200
  svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL,
187
 
                 SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, NULL);
188
 
  if (exceptions)
189
 
    {
190
 
      apr_array_header_t *l = svn_cstring_split(exceptions, ",", TRUE, pool);
191
 
      is_exception = svn_cstring_match_glob_list(session->session_url.hostname,
192
 
                                                 l);
193
 
    }
194
 
  if (! is_exception)
195
 
    {
196
 
      /* Load the global proxy server settings, if set. */
 
201
                 SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, "");
 
202
  if (! svn_cstring_match_glob_list(session->session_url.hostname,
 
203
                                    svn_cstring_split(exceptions, ",",
 
204
                                                      TRUE, pool)))
 
205
    {
197
206
      svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL,
198
207
                     SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL);
199
208
      svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL,
214
223
  svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL,
215
224
                 SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL);
216
225
 
 
226
  /* If set, read the flag that tells us to do bulk updates or not. Defaults
 
227
     to skelta updates. */
 
228
  SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates,
 
229
                                  SVN_CONFIG_SECTION_GLOBAL,
 
230
                                  SVN_CONFIG_OPTION_HTTP_BULK_UPDATES,
 
231
                                  "auto",
 
232
                                  svn_tristate_unknown));
 
233
 
 
234
  /* Load the maximum number of parallel session connections. */
 
235
  SVN_ERR(svn_config_get_int64(config, &session->max_connections,
 
236
                               SVN_CONFIG_SECTION_GLOBAL,
 
237
                               SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
 
238
                               SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS));
 
239
 
 
240
  /* Should we use chunked transfer encoding. */ 
 
241
  SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
 
242
                                  SVN_CONFIG_SECTION_GLOBAL,
 
243
                                  OPTION_HTTP_CHUNKED_REQUESTS,
 
244
                                  "auto", svn_tristate_unknown));
 
245
 
217
246
  if (config)
218
247
    server_group = svn_config_find_group(config,
219
248
                                         session->session_url.hostname,
233
262
      svn_auth_set_parameter(session->wc_callbacks->auth_baton,
234
263
                             SVN_AUTH_PARAM_SERVER_GROUP, server_group);
235
264
 
236
 
      /* Load the group proxy server settings, overriding global settings. */
 
265
      /* Load the group proxy server settings, overriding global
 
266
         settings.  We intentionally ignore 'http-proxy-exceptions'
 
267
         here because, well, if this site was an exception, why is
 
268
         there a per-server proxy configuration for it?  */
237
269
      svn_config_get(config, &proxy_host, server_group,
238
 
                     SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL);
 
270
                     SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host);
239
271
      svn_config_get(config, &port_str, server_group,
240
 
                     SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL);
 
272
                     SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str);
241
273
      svn_config_get(config, &session->proxy_username, server_group,
242
 
                     SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL);
 
274
                     SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME,
 
275
                     session->proxy_username);
243
276
      svn_config_get(config, &session->proxy_password, server_group,
244
 
                     SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL);
 
277
                     SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD,
 
278
                     session->proxy_password);
245
279
 
246
280
      /* Load the group ssl settings. */
247
281
      SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca,
248
282
                                  server_group,
249
283
                                  SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA,
250
 
                                  TRUE));
 
284
                                  session->trust_default_ca));
251
285
      svn_config_get(config, &session->ssl_authorities, server_group,
252
 
                     SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL);
 
286
                     SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES,
 
287
                     session->ssl_authorities);
 
288
 
 
289
      /* Load the group bulk updates flag. */
 
290
      SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates,
 
291
                                      server_group,
 
292
                                      SVN_CONFIG_OPTION_HTTP_BULK_UPDATES,
 
293
                                      "auto",
 
294
                                      session->bulk_updates));
 
295
 
 
296
      /* Load the maximum number of parallel session connections,
 
297
         overriding global values. */
 
298
      SVN_ERR(svn_config_get_int64(config, &session->max_connections,
 
299
                                   server_group,
 
300
                                   SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
 
301
                                   session->max_connections));
 
302
 
 
303
      /* Should we use chunked transfer encoding. */ 
 
304
      SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
 
305
                                      server_group,
 
306
                                      OPTION_HTTP_CHUNKED_REQUESTS,
 
307
                                      "auto", chunked_requests));
253
308
    }
254
309
 
 
310
  /* Don't allow the http-max-connections value to be larger than our
 
311
     compiled-in limit, or to be too small to operate.  Broken
 
312
     functionality and angry administrators are equally undesirable. */
 
313
  if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT)
 
314
    session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT;
 
315
  if (session->max_connections < 2)
 
316
    session->max_connections = 2;
 
317
 
255
318
  /* Parse the connection timeout value, if any. */
 
319
  session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT);
256
320
  if (timeout_str)
257
321
    {
258
322
      char *endstr;
267
331
                                _("Invalid config: negative timeout value"));
268
332
      session->timeout = apr_time_from_sec(timeout);
269
333
    }
270
 
  else
271
 
    session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT);
 
334
  SVN_ERR_ASSERT(session->timeout >= 0);
272
335
 
273
336
  /* Convert the proxy port value, if any. */
274
337
  if (port_str)
290
353
      proxy_port = (apr_port_t) port;
291
354
    }
292
355
  else
293
 
    proxy_port = 80;
 
356
    {
 
357
      proxy_port = 80;
 
358
    }
294
359
 
295
360
  if (proxy_host)
296
361
    {
302
367
                                     session->pool);
303
368
      if (status)
304
369
        {
305
 
          return svn_error_wrap_apr(status,
306
 
                                    _("Could not resolve proxy server '%s'"),
307
 
                                    proxy_host);
 
370
          return svn_ra_serf__wrap_err(
 
371
                   status, _("Could not resolve proxy server '%s'"),
 
372
                   proxy_host);
308
373
        }
309
374
      session->using_proxy = TRUE;
310
375
      serf_config_proxy(session->context, proxy_addr);
311
376
    }
312
377
  else
313
 
    session->using_proxy = FALSE;
 
378
    {
 
379
      session->using_proxy = FALSE;
 
380
    }
 
381
 
 
382
  /* Setup detect_chunking and using_chunked_requests based on
 
383
   * the chunked_requests tristate */
 
384
  if (chunked_requests == svn_tristate_unknown)
 
385
    {
 
386
      session->detect_chunking = TRUE;
 
387
      session->using_chunked_requests = TRUE;
 
388
    }
 
389
  else if (chunked_requests == svn_tristate_true)
 
390
    {
 
391
      session->detect_chunking = FALSE;
 
392
      session->using_chunked_requests = TRUE;
 
393
    }
 
394
  else /* chunked_requests == svn_tristate_false */
 
395
    {
 
396
      session->detect_chunking = FALSE;
 
397
      session->using_chunked_requests = FALSE;
 
398
    }
314
399
 
315
400
  /* Setup authentication. */
316
401
  SVN_ERR(load_http_auth_types(pool, config, server_group,
335
420
    }
336
421
}
337
422
 
 
423
/** Our User-Agent string. */
 
424
static const char *
 
425
get_user_agent_string(apr_pool_t *pool)
 
426
{
 
427
  int major, minor, patch;
 
428
  serf_lib_version(&major, &minor, &patch);
 
429
 
 
430
  return apr_psprintf(pool, "SVN/%s (%s) serf/%d.%d.%d",
 
431
                      SVN_VER_NUMBER, SVN_BUILD_TARGET,
 
432
                      major, minor, patch);
 
433
}
 
434
 
 
435
/* Implements svn_ra__vtable_t.open_session(). */
338
436
static svn_error_t *
339
437
svn_ra_serf__open(svn_ra_session_t *session,
340
438
                  const char **corrected_url,
348
446
  svn_ra_serf__session_t *serf_sess;
349
447
  apr_uri_t url;
350
448
  const char *client_string = NULL;
 
449
  svn_error_t *err;
351
450
 
352
451
  if (corrected_url)
353
452
    *corrected_url = NULL;
393
492
 
394
493
  serf_sess->capabilities = apr_hash_make(serf_sess->pool);
395
494
 
 
495
  /* We have to assume that the server only supports HTTP/1.0. Once it's clear
 
496
     HTTP/1.1 is supported, we can upgrade. */
 
497
  serf_sess->http10 = TRUE;
 
498
 
 
499
  /* If we switch to HTTP/1.1, then we will use chunked requests. We may disable
 
500
     this, if we find an intervening proxy does not support chunked requests.  */
 
501
  serf_sess->using_chunked_requests = TRUE;
 
502
 
396
503
  SVN_ERR(load_config(serf_sess, config, serf_sess->pool));
397
504
 
398
 
 
399
 
  serf_sess->conns = apr_palloc(serf_sess->pool, sizeof(*serf_sess->conns) * 4);
400
 
 
401
505
  serf_sess->conns[0] = apr_pcalloc(serf_sess->pool,
402
506
                                    sizeof(*serf_sess->conns[0]));
403
507
  serf_sess->conns[0]->bkt_alloc =
405
509
  serf_sess->conns[0]->session = serf_sess;
406
510
  serf_sess->conns[0]->last_status_code = -1;
407
511
 
408
 
  serf_sess->conns[0]->using_ssl = serf_sess->using_ssl;
409
 
  serf_sess->conns[0]->server_cert_failures = 0;
410
 
  serf_sess->conns[0]->using_compression = serf_sess->using_compression;
411
 
  serf_sess->conns[0]->hostname = url.hostname;
412
 
  serf_sess->conns[0]->useragent = NULL;
413
 
 
414
512
  /* create the user agent string */
415
513
  if (callbacks->get_client_string)
416
 
    callbacks->get_client_string(callback_baton, &client_string, pool);
 
514
    SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool));
417
515
 
418
516
  if (client_string)
419
 
    serf_sess->conns[0]->useragent = apr_pstrcat(pool, USER_AGENT, "/",
420
 
                                                 client_string, (char *)NULL);
 
517
    serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ",
 
518
                                       client_string, (char *)NULL);
421
519
  else
422
 
    serf_sess->conns[0]->useragent = USER_AGENT;
 
520
    serf_sess->useragent = get_user_agent_string(pool);
423
521
 
424
522
  /* go ahead and tell serf about the connection. */
425
523
  status =
430
528
                            svn_ra_serf__conn_closed, serf_sess->conns[0],
431
529
                            serf_sess->pool);
432
530
  if (status)
433
 
    return svn_error_wrap_apr(status, NULL);
 
531
    return svn_ra_serf__wrap_err(status, NULL);
434
532
 
435
533
  /* Set the progress callback. */
436
534
  serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress,
440
538
 
441
539
  session->priv = serf_sess;
442
540
 
443
 
  return svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool);
 
541
  err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool);
 
542
 
 
543
  /* serf should produce a usable error code instead of APR_EGENERAL */
 
544
  if (err && err->apr_err == APR_EGENERAL)
 
545
    err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, err,
 
546
                            _("Connection to '%s' failed"), session_URL);
 
547
  SVN_ERR(err);
 
548
 
 
549
  /* We have set up a useful connection (that doesn't indication a redirect).
 
550
     If we've been told there is possibly a worrisome proxy in our path to the
 
551
     server AND we switched to HTTP/1.1 (chunked requests), then probe for
 
552
     problems in any proxy.  */
 
553
  if ((corrected_url == NULL || *corrected_url == NULL)
 
554
      && serf_sess->detect_chunking && !serf_sess->http10)
 
555
    SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool));
 
556
 
 
557
  return SVN_NO_ERROR;
444
558
}
445
559
 
 
560
/* Implements svn_ra__vtable_t.reparent(). */
446
561
static svn_error_t *
447
562
svn_ra_serf__reparent(svn_ra_session_t *ra_session,
448
563
                      const char *url,
496
611
  return SVN_NO_ERROR;
497
612
}
498
613
 
 
614
/* Implements svn_ra__vtable_t.get_session_url(). */
499
615
static svn_error_t *
500
616
svn_ra_serf__get_session_url(svn_ra_session_t *ra_session,
501
617
                             const char **url,
506
622
  return SVN_NO_ERROR;
507
623
}
508
624
 
 
625
/* Implements svn_ra__vtable_t.get_latest_revnum(). */
509
626
static svn_error_t *
510
627
svn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session,
511
628
                               svn_revnum_t *latest_revnum,
512
629
                               apr_pool_t *pool)
513
630
{
514
 
  const char *relative_url, *basecoll_url;
515
631
  svn_ra_serf__session_t *session = ra_session->priv;
516
632
 
517
 
  return svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, session,
518
 
                                        NULL, session->session_url.path,
519
 
                                        SVN_INVALID_REVNUM, latest_revnum,
520
 
                                        pool);
 
633
  return svn_error_trace(svn_ra_serf__get_youngest_revnum(
 
634
                           latest_revnum, session, pool));
521
635
}
522
636
 
 
637
/* Implements svn_ra__vtable_t.rev_proplist(). */
523
638
static svn_error_t *
524
639
svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session,
525
640
                          svn_revnum_t rev,
546
661
      SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool));
547
662
    }
548
663
 
 
664
  /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash.  */
549
665
  SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
550
666
                                      propfind_path, rev, "0", all_props,
551
667
                                      pool, pool));
556
672
  return SVN_NO_ERROR;
557
673
}
558
674
 
 
675
/* Implements svn_ra__vtable_t.rev_prop(). */
559
676
static svn_error_t *
560
677
svn_ra_serf__rev_prop(svn_ra_session_t *session,
561
678
                      svn_revnum_t rev,
567
684
 
568
685
  SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool));
569
686
 
570
 
  *value = apr_hash_get(props, name, APR_HASH_KEY_STRING);
 
687
  *value = svn_hash_gets(props, name);
571
688
 
572
689
  return SVN_NO_ERROR;
573
690
}
574
691
 
575
692
static svn_error_t *
576
 
fetch_path_props(svn_ra_serf__propfind_context_t **ret_prop_ctx,
577
 
                 apr_hash_t **ret_props,
578
 
                 const char **ret_path,
579
 
                 svn_revnum_t *ret_revision,
 
693
fetch_path_props(apr_hash_t **props,
580
694
                 svn_ra_serf__session_t *session,
581
 
                 const char *rel_path,
 
695
                 const char *session_relpath,
582
696
                 svn_revnum_t revision,
583
697
                 const svn_ra_serf__dav_props_t *desired_props,
584
 
                 apr_pool_t *pool)
 
698
                 apr_pool_t *result_pool,
 
699
                 apr_pool_t *scratch_pool)
585
700
{
586
 
  svn_ra_serf__propfind_context_t *prop_ctx;
587
 
  apr_hash_t *props;
588
 
  const char *path;
 
701
  const char *url;
589
702
 
590
 
  path = session->session_url.path;
 
703
  url = session->session_url.path;
591
704
 
592
705
  /* If we have a relative path, append it. */
593
 
  if (rel_path)
594
 
    {
595
 
      path = svn_path_url_add_component2(path, rel_path, pool);
596
 
    }
597
 
 
598
 
  props = apr_hash_make(pool);
599
 
 
600
 
  /* If we were given a specific revision, we have to fetch the VCC and
601
 
   * do a PROPFIND off of that.
602
 
   */
603
 
  if (!SVN_IS_VALID_REVNUM(revision))
604
 
    {
605
 
      SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session,
606
 
                                         session->conns[0], path, revision,
607
 
                                         "0", desired_props, NULL,
608
 
                                         pool));
609
 
    }
610
 
  else
611
 
    {
612
 
      const char *relative_url, *basecoll_url;
613
 
 
614
 
      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
615
 
                                             session, NULL, path,
616
 
                                             revision, NULL, pool));
617
 
 
618
 
      /* We will try again with our new path; however, we're now
619
 
       * technically an unversioned resource because we are accessing
620
 
       * the revision's baseline-collection.
621
 
       */
622
 
      path = svn_path_url_add_component2(basecoll_url, relative_url, pool);
623
 
      revision = SVN_INVALID_REVNUM;
624
 
      SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session,
625
 
                                         session->conns[0], path, revision,
626
 
                                         "0", desired_props, NULL,
627
 
                                         pool));
628
 
    }
629
 
 
630
 
  /* ### switch to svn_ra_serf__retrieve_props?  */
631
 
  SVN_ERR(svn_ra_serf__wait_for_props(prop_ctx, session, pool));
632
 
 
633
 
  *ret_path = path;
634
 
  *ret_prop_ctx = prop_ctx;
635
 
  *ret_props = props;
636
 
  *ret_revision = revision;
 
706
  if (session_relpath)
 
707
    url = svn_path_url_add_component2(url, session_relpath, scratch_pool);
 
708
 
 
709
  /* If we were given a specific revision, get a URL that refers to that
 
710
     specific revision (rather than floating with HEAD).  */
 
711
  if (SVN_IS_VALID_REVNUM(revision))
 
712
    {
 
713
      SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */,
 
714
                                          session, NULL /* conn */,
 
715
                                          url, revision,
 
716
                                          scratch_pool, scratch_pool));
 
717
    }
 
718
 
 
719
  /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant.
 
720
     Or we started with SVN_INVALID_REVNUM and URL may be floating.  */
 
721
  SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0],
 
722
                                        url, SVN_INVALID_REVNUM,
 
723
                                        desired_props,
 
724
                                        result_pool, scratch_pool));
637
725
 
638
726
  return SVN_NO_ERROR;
639
727
}
640
728
 
 
729
/* Implements svn_ra__vtable_t.check_path(). */
641
730
static svn_error_t *
642
731
svn_ra_serf__check_path(svn_ra_session_t *ra_session,
643
732
                        const char *rel_path,
647
736
{
648
737
  svn_ra_serf__session_t *session = ra_session->priv;
649
738
  apr_hash_t *props;
650
 
  svn_ra_serf__propfind_context_t *prop_ctx;
651
 
  const char *path;
652
 
  svn_revnum_t fetched_rev;
653
739
 
654
 
  svn_error_t *err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
655
 
                                      session, rel_path,
656
 
                                      revision, check_path_props, pool);
 
740
  svn_error_t *err = fetch_path_props(&props, session, rel_path,
 
741
                                      revision, check_path_props,
 
742
                                      pool, pool);
657
743
 
658
744
  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
659
745
    {
664
750
    {
665
751
      /* Any other error, raise to caller. */
666
752
      if (err)
667
 
        return err;
 
753
        return svn_error_trace(err);
668
754
 
669
 
      SVN_ERR(svn_ra_serf__get_resource_type(kind, props, path, fetched_rev));
 
755
      SVN_ERR(svn_ra_serf__get_resource_type(kind, props));
670
756
    }
671
757
 
672
758
  return SVN_NO_ERROR;
788
874
    {
789
875
      const char *base_name;
790
876
 
791
 
      entry = apr_pcalloc(pool, sizeof(*entry));
 
877
      entry = svn_dirent_create(pool);
792
878
 
793
879
      apr_hash_set(dirents->full_paths, path, path_len, entry);
794
880
 
795
881
      base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool),
796
882
                                      pool);
797
883
 
798
 
      apr_hash_set(dirents->base_paths, base_name, APR_HASH_KEY_STRING, entry);
 
884
      svn_hash_sets(dirents->base_paths, base_name, entry);
799
885
    }
800
886
 
801
887
  dwb.entry = entry;
876
962
  return (svn_ra_serf__dav_props_t *) props->elts;
877
963
}
878
964
 
 
965
/* Implements svn_ra__vtable_t.stat(). */
879
966
static svn_error_t *
880
967
svn_ra_serf__stat(svn_ra_session_t *ra_session,
881
968
                  const char *rel_path,
885
972
{
886
973
  svn_ra_serf__session_t *session = ra_session->priv;
887
974
  apr_hash_t *props;
888
 
  svn_ra_serf__propfind_context_t *prop_ctx;
889
 
  const char *path;
890
 
  svn_revnum_t fetched_rev;
891
975
  svn_error_t *err;
892
976
  struct dirent_walker_baton_t dwb;
893
977
  svn_tristate_t deadprop_count = svn_tristate_unknown;
894
978
 
895
 
  err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
 
979
  err = fetch_path_props(&props,
896
980
                         session, rel_path, revision,
897
981
                         get_dirent_props(SVN_DIRENT_ALL, session, pool),
898
 
                         pool);
 
982
                         pool, pool);
899
983
  if (err)
900
984
    {
901
985
      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
908
992
        return svn_error_trace(err);
909
993
    }
910
994
 
911
 
  dwb.entry = apr_pcalloc(pool, sizeof(*dwb.entry));
 
995
  dwb.entry = svn_dirent_create(pool);
912
996
  dwb.supports_deadprop_count = &deadprop_count;
913
997
  dwb.result_pool = pool;
914
 
  SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev,
915
 
                                      dirent_walker, &dwb,
916
 
                                      pool));
 
998
  SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
917
999
 
918
1000
  if (deadprop_count == svn_tristate_false
919
1001
      && session->supports_deadprop_count == svn_tristate_unknown
923
1005
         information */
924
1006
      session->supports_deadprop_count = svn_tristate_false;
925
1007
 
926
 
      SVN_ERR(fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
927
 
                               session, rel_path, fetched_rev,
 
1008
      SVN_ERR(fetch_path_props(&props,
 
1009
                               session, rel_path, SVN_INVALID_REVNUM,
928
1010
                               get_dirent_props(SVN_DIRENT_ALL, session, pool),
929
 
                               pool));
 
1011
                               pool, pool));
930
1012
 
931
 
      SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev,
932
 
                                      dirent_walker, &dwb,
933
 
                                      pool));
 
1013
      SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
934
1014
    }
935
1015
 
936
1016
  if (deadprop_count != svn_tristate_unknown)
946
1026
 * SVN_ERR_FS_NOT_DIRECTORY if not.
947
1027
 */
948
1028
static svn_error_t *
949
 
resource_is_directory(apr_hash_t *props,
950
 
                      const char *path,
951
 
                      svn_revnum_t revision)
 
1029
resource_is_directory(apr_hash_t *props)
952
1030
{
953
1031
  svn_node_kind_t kind;
954
1032
 
955
 
  SVN_ERR(svn_ra_serf__get_resource_type(&kind, props, path, revision));
 
1033
  SVN_ERR(svn_ra_serf__get_resource_type(&kind, props));
956
1034
 
957
1035
  if (kind != svn_node_dir)
958
1036
    {
963
1041
  return SVN_NO_ERROR;
964
1042
}
965
1043
 
 
1044
/* Implements svn_ra__vtable_t.get_dir(). */
966
1045
static svn_error_t *
967
1046
svn_ra_serf__get_dir(svn_ra_session_t *ra_session,
968
1047
                     apr_hash_t **dirents,
989
1068
     public url. */
990
1069
  if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
991
1070
    {
992
 
      const char *relative_url, *basecoll_url;
993
 
 
994
 
      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
995
 
                                             session, NULL, path, revision,
996
 
                                             fetched_rev, pool));
997
 
 
998
 
      path = svn_path_url_add_component2(basecoll_url, relative_url, pool);
 
1071
      SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev,
 
1072
                                          session, NULL /* conn */,
 
1073
                                          path, revision,
 
1074
                                          pool, pool));
999
1075
      revision = SVN_INVALID_REVNUM;
1000
1076
    }
 
1077
  /* REVISION is always SVN_INVALID_REVNUM  */
 
1078
  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
1001
1079
 
1002
1080
  /* If we're asked for children, fetch them now. */
1003
1081
  if (dirents)
1004
1082
    {
1005
1083
      struct path_dirent_visitor_t dirent_walk;
1006
1084
      apr_hash_t *props;
 
1085
      const char *rtype;
1007
1086
 
1008
1087
      /* Always request node kind to check that path is really a
1009
1088
       * directory.
1010
1089
       */
1011
1090
      dirent_fields |= SVN_DIRENT_KIND;
1012
1091
      SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
1013
 
                                          path, revision, "1",
 
1092
                                          path, SVN_INVALID_REVNUM, "1",
1014
1093
                                          get_dirent_props(dirent_fields,
1015
1094
                                                           session, pool),
1016
1095
                                          pool, pool));
1017
1096
 
1018
1097
      /* Check if the path is really a directory. */
1019
 
      SVN_ERR(resource_is_directory(props, path, revision));
 
1098
      rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype");
 
1099
      if (rtype == NULL || strcmp(rtype, "collection") != 0)
 
1100
        return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
 
1101
                                _("Can't get entries of non-directory"));
1020
1102
 
1021
1103
      /* We're going to create two hashes to help the walker along.
1022
1104
       * We're going to return the 2nd one back to the caller as it
1028
1110
      dirent_walk.supports_deadprop_count = svn_tristate_unknown;
1029
1111
      dirent_walk.result_pool = pool;
1030
1112
 
1031
 
      SVN_ERR(svn_ra_serf__walk_all_paths(props, revision, path_dirent_walker,
1032
 
                                          &dirent_walk, pool));
 
1113
      SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
 
1114
                                          path_dirent_walker, &dirent_walk,
 
1115
                                          pool));
1033
1116
 
1034
1117
      if (dirent_walk.supports_deadprop_count == svn_tristate_false
1035
1118
          && session->supports_deadprop_count == svn_tristate_unknown
1040
1123
          session->supports_deadprop_count = svn_tristate_false;
1041
1124
          SVN_ERR(svn_ra_serf__retrieve_props(&props, session,
1042
1125
                                              session->conns[0],
1043
 
                                              path, revision, "1",
 
1126
                                              path, SVN_INVALID_REVNUM, "1",
1044
1127
                                              get_dirent_props(dirent_fields,
1045
1128
                                                               session, pool),
1046
1129
                                              pool, pool));
1047
1130
 
1048
 
          SVN_ERR(svn_hash__clear(dirent_walk.full_paths, pool));
1049
 
          SVN_ERR(svn_hash__clear(dirent_walk.base_paths, pool));
 
1131
          apr_hash_clear(dirent_walk.full_paths);
 
1132
          apr_hash_clear(dirent_walk.base_paths);
1050
1133
 
1051
 
          SVN_ERR(svn_ra_serf__walk_all_paths(props, revision,
 
1134
          SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
1052
1135
                                              path_dirent_walker,
1053
1136
                                              &dirent_walk, pool));
1054
1137
        }
1064
1147
    {
1065
1148
      apr_hash_t *props;
1066
1149
 
1067
 
      SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
1068
 
                                          path, revision, "0", all_props,
1069
 
                                          pool, pool));
 
1150
      SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0],
 
1151
                                            path, SVN_INVALID_REVNUM,
 
1152
                                            all_props,
 
1153
                                            pool, pool));
 
1154
 
1070
1155
      /* Check if the path is really a directory. */
1071
 
      SVN_ERR(resource_is_directory(props, path, revision));
 
1156
      SVN_ERR(resource_is_directory(props));
1072
1157
 
1073
 
      SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, path, revision,
1074
 
                                         pool, pool));
 
1158
      /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
 
1159
         ### put them into POOL, so we're okay.  */
 
1160
      SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool));
1075
1161
    }
1076
1162
 
1077
1163
  return SVN_NO_ERROR;
1078
1164
}
1079
1165
 
1080
 
static svn_error_t *
 
1166
svn_error_t *
1081
1167
svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
1082
1168
                            const char **url,
1083
1169
                            apr_pool_t *pool)
1102
1188
   case where the root of the repository is not readable.
1103
1189
   However, it does not handle the case where we're fetching path not existing
1104
1190
   in HEAD of a repository with unreadable root directory.
 
1191
 
 
1192
   Implements svn_ra__vtable_t.get_uuid().
1105
1193
 */
1106
1194
static svn_error_t *
1107
1195
svn_ra_serf__get_uuid(svn_ra_session_t *ra_session,
1171
1259
  svn_ra_serf__replay,
1172
1260
  svn_ra_serf__has_capability,
1173
1261
  svn_ra_serf__replay_range,
1174
 
  svn_ra_serf__get_deleted_rev
 
1262
  svn_ra_serf__get_deleted_rev,
 
1263
  svn_ra_serf__register_editor_shim_callbacks,
 
1264
  svn_ra_serf__get_inherited_props
1175
1265
};
1176
1266
 
1177
1267
svn_error_t *
1189
1279
  int serf_minor;
1190
1280
  int serf_patch;
1191
1281
 
1192
 
  SVN_ERR(svn_ver_check_list(ra_serf_version(), checklist));
 
1282
  SVN_ERR(svn_ver_check_list2(ra_serf_version(), checklist, svn_ver_equal));
1193
1283
 
1194
1284
  /* Simplified version check to make sure we can safely use the
1195
1285
     VTABLE parameter. The RA loader does a more exhaustive check. */
1209
1299
      || serf_minor < SERF_MINOR_VERSION)
1210
1300
    {
1211
1301
      return svn_error_createf(
 
1302
         /* ### should return a unique error  */
1212
1303
         SVN_ERR_VERSION_MISMATCH, NULL,
1213
1304
         _("ra_serf was compiled for serf %d.%d.%d but loaded "
1214
1305
           "an incompatible %d.%d.%d library"),