Followup to r29625: fix getopt tests.
[svn.git] / subversion / libsvn_ra_neon / session.c
blobb12e216dc0016096a3c96453cf58631cf409552f
1 /*
2 * session.c : routines for maintaining sessions state (to the DAV server)
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include <assert.h>
22 #include <ctype.h>
24 #define APR_WANT_STRFUNC
25 #include <apr_want.h>
26 #include <apr_general.h>
27 #include <apr_xml.h>
29 #include <ne_auth.h>
31 #include "svn_error.h"
32 #include "svn_pools.h"
33 #include "svn_ra.h"
34 #include "../libsvn_ra/ra_loader.h"
35 #include "svn_config.h"
36 #include "svn_delta.h"
37 #include "svn_version.h"
38 #include "svn_path.h"
39 #include "svn_time.h"
40 #include "svn_xml.h"
41 #include "svn_private_config.h"
43 #ifdef SVN_NEON_0_28
44 #include <ne_pkcs11.h>
45 #endif
47 #include "ra_neon.h"
49 #define DEFAULT_HTTP_TIMEOUT 3600
52 /* a cleanup routine attached to the pool that contains the RA session
53 baton. */
54 static apr_status_t cleanup_session(void *sess)
56 ne_session_destroy(sess);
57 return APR_SUCCESS;
60 /* a cleanup routine attached to the pool that contains the RA session
61 root URI. */
62 static apr_status_t cleanup_uri(void *uri)
64 ne_uri_free(uri);
65 return APR_SUCCESS;
68 #ifdef SVN_NEON_0_28
69 /* a cleanup routine attached to the pool that contains the PKCS#11
70 provider object. */
71 static apr_status_t cleanup_p11provider(void *provider)
73 ne_ssl_pkcs11_provider *prov = provider;
74 ne_ssl_pkcs11_provider_destroy(prov);
75 return APR_SUCCESS;
77 #endif
79 /* A neon-session callback to 'pull' authentication data when
80 challenged. In turn, this routine 'pulls' the data from the client
81 callbacks if needed. */
82 static int request_auth(void *userdata, const char *realm, int attempt,
83 char *username, char *password)
85 svn_error_t *err;
86 svn_ra_neon__session_t *ras = userdata;
87 void *creds;
88 svn_auth_cred_simple_t *simple_creds;
90 /* Start by clearing the cache of any previously-fetched username. */
91 ras->auth_username = NULL;
93 /* No auth_baton? Give up. */
94 if (! ras->callbacks->auth_baton)
95 return -1;
97 /* Neon automatically tries some auth protocols and bumps the attempt
98 count without using Subversion's callbacks, so we can't depend
99 on attempt == 0 the first time we are called -- we need to check
100 if the auth state has been initted as well. */
101 if (attempt == 0 || ras->auth_iterstate == NULL)
103 const char *realmstring;
105 /* <https://svn.collab.net:80> Subversion repository */
106 realmstring = apr_psprintf(ras->pool, "<%s://%s:%d> %s",
107 ras->root.scheme, ras->root.host,
108 ras->root.port, realm);
110 err = svn_auth_first_credentials(&creds,
111 &(ras->auth_iterstate),
112 SVN_AUTH_CRED_SIMPLE,
113 realmstring,
114 ras->callbacks->auth_baton,
115 ras->pool);
118 else /* attempt > 0 */
119 /* ### TODO: if the http realm changed this time around, we
120 should be calling first_creds(), not next_creds(). */
121 err = svn_auth_next_credentials(&creds,
122 ras->auth_iterstate,
123 ras->pool);
124 if (err || ! creds)
126 svn_error_clear(err);
127 return -1;
129 simple_creds = creds;
131 /* ### silently truncates username/password to 256 chars. */
132 apr_cpystrn(username, simple_creds->username, NE_ABUFSIZ);
133 apr_cpystrn(password, simple_creds->password, NE_ABUFSIZ);
135 /* Cache the fetched username in ra_session. */
136 ras->auth_username = apr_pstrdup(ras->pool, simple_creds->username);
138 return 0;
142 static const apr_uint32_t neon_failure_map[][2] =
144 { NE_SSL_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID },
145 { NE_SSL_EXPIRED, SVN_AUTH_SSL_EXPIRED },
146 { NE_SSL_IDMISMATCH, SVN_AUTH_SSL_CNMISMATCH },
147 { NE_SSL_UNTRUSTED, SVN_AUTH_SSL_UNKNOWNCA }
150 /* Convert neon's SSL failure mask to our own failure mask. */
151 static apr_uint32_t
152 convert_neon_failures(int neon_failures)
154 apr_uint32_t svn_failures = 0;
155 apr_size_t i;
157 for (i = 0; i < sizeof(neon_failure_map) / (2 * sizeof(int)); ++i)
159 if (neon_failures & neon_failure_map[i][0])
161 svn_failures |= neon_failure_map[i][1];
162 neon_failures &= ~neon_failure_map[i][0];
166 /* Map any remaining neon failure bits to our OTHER bit. */
167 if (neon_failures)
169 svn_failures |= SVN_AUTH_SSL_OTHER;
172 return svn_failures;
175 /* A neon-session callback to validate the SSL certificate when the CA
176 is unknown (e.g. a self-signed cert), or there are other SSL
177 certificate problems. */
178 static int
179 server_ssl_callback(void *userdata,
180 int failures,
181 const ne_ssl_certificate *cert)
183 svn_ra_neon__session_t *ras = userdata;
184 svn_auth_cred_ssl_server_trust_t *server_creds = NULL;
185 void *creds;
186 svn_auth_iterstate_t *state;
187 apr_pool_t *pool;
188 svn_error_t *error;
189 char *ascii_cert = ne_ssl_cert_export(cert);
190 char *issuer_dname = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
191 svn_auth_ssl_server_cert_info_t cert_info;
192 char fingerprint[NE_SSL_DIGESTLEN];
193 char valid_from[NE_SSL_VDATELEN], valid_until[NE_SSL_VDATELEN];
194 const char *realmstring;
195 apr_uint32_t *svn_failures = apr_palloc(ras->pool, sizeof(*svn_failures));
197 /* Construct the realmstring, e.g. https://svn.collab.net:80 */
198 realmstring = apr_psprintf(ras->pool, "%s://%s:%d", ras->root.scheme,
199 ras->root.host, ras->root.port);
201 *svn_failures = convert_neon_failures(failures);
202 svn_auth_set_parameter(ras->callbacks->auth_baton,
203 SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
204 svn_failures);
206 /* Extract the info from the certificate */
207 cert_info.hostname = ne_ssl_cert_identity(cert);
208 if (ne_ssl_cert_digest(cert, fingerprint) != 0)
210 strcpy(fingerprint, "<unknown>");
212 cert_info.fingerprint = fingerprint;
213 ne_ssl_cert_validity(cert, valid_from, valid_until);
214 cert_info.valid_from = valid_from;
215 cert_info.valid_until = valid_until;
216 cert_info.issuer_dname = issuer_dname;
217 cert_info.ascii_cert = ascii_cert;
219 svn_auth_set_parameter(ras->callbacks->auth_baton,
220 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
221 &cert_info);
223 apr_pool_create(&pool, ras->pool);
224 error = svn_auth_first_credentials(&creds, &state,
225 SVN_AUTH_CRED_SSL_SERVER_TRUST,
226 realmstring,
227 ras->callbacks->auth_baton,
228 pool);
229 if (error || ! creds)
231 svn_error_clear(error);
233 else
235 server_creds = creds;
236 error = svn_auth_save_credentials(state, pool);
237 if (error)
239 /* It would be nice to show the error to the user somehow... */
240 svn_error_clear(error);
244 free(issuer_dname);
245 free(ascii_cert);
246 svn_auth_set_parameter(ras->callbacks->auth_baton,
247 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
249 svn_pool_destroy(pool);
250 return ! server_creds;
253 static svn_boolean_t
254 client_ssl_decrypt_cert(svn_ra_neon__session_t *ras,
255 const char *cert_file,
256 ne_ssl_client_cert *clicert)
258 svn_auth_iterstate_t *state;
259 svn_error_t *error;
260 apr_pool_t *pool;
261 svn_boolean_t ok = FALSE;
262 void *creds;
263 int try;
265 apr_pool_create(&pool, ras->pool);
266 for (try = 0; TRUE; ++try)
268 if (try == 0)
270 error = svn_auth_first_credentials(&creds, &state,
271 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
272 cert_file,
273 ras->callbacks->auth_baton,
274 pool);
276 else
278 error = svn_auth_next_credentials(&creds, state, pool);
281 if (error || ! creds)
283 /* Failure or too many attempts */
284 svn_error_clear(error);
285 break;
287 else
289 svn_auth_cred_ssl_client_cert_pw_t *pw_creds = creds;
291 if (ne_ssl_clicert_decrypt(clicert, pw_creds->password) == 0)
293 /* Success */
294 ok = TRUE;
295 break;
299 svn_pool_destroy(pool);
301 return ok;
304 #ifdef SVN_NEON_0_28
305 /* Callback invoked to enter PKCS#11 PIN code. */
306 static int
307 client_ssl_pkcs11_pin_entry(void *userdata,
308 int attempt,
309 const char *slot_descr,
310 const char *token_label,
311 unsigned int flags,
312 char *pin)
314 svn_ra_neon__session_t *ras = userdata;
315 svn_error_t *err;
316 void *creds;
317 svn_auth_cred_ssl_client_cert_pw_t *pw_creds;
319 /* Always prevent PIN caching. */
320 svn_auth_set_parameter(ras->callbacks->auth_baton,
321 SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
323 if (attempt == 0)
325 const char *realmstring;
327 realmstring = apr_psprintf(ras->pool,
328 _("PIN for token \"%s\" in slot \"%s\""),
329 token_label, slot_descr);
331 err = svn_auth_first_credentials(&creds,
332 &(ras->auth_iterstate),
333 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
334 realmstring,
335 ras->callbacks->auth_baton,
336 ras->pool);
338 else
340 err = svn_auth_next_credentials(&creds, ras->auth_iterstate, ras->pool);
343 if (err || ! creds)
345 svn_error_clear(err);
346 return -1;
349 pw_creds = creds;
351 apr_cpystrn(pin, pw_creds->password, NE_SSL_P11PINLEN);
353 return 0;
355 #endif
357 static void
358 client_ssl_callback(void *userdata, ne_session *sess,
359 const ne_ssl_dname *const *dnames,
360 int dncount)
362 svn_ra_neon__session_t *ras = userdata;
363 ne_ssl_client_cert *clicert = NULL;
364 void *creds;
365 svn_auth_iterstate_t *state;
366 const char *realmstring;
367 apr_pool_t *pool;
368 svn_error_t *error;
369 int try;
371 apr_pool_create(&pool, ras->pool);
373 realmstring = apr_psprintf(pool, "%s://%s:%d", ras->root.scheme,
374 ras->root.host, ras->root.port);
376 for (try = 0; TRUE; ++try)
378 if (try == 0)
380 error = svn_auth_first_credentials(&creds, &state,
381 SVN_AUTH_CRED_SSL_CLIENT_CERT,
382 realmstring,
383 ras->callbacks->auth_baton,
384 pool);
386 else
388 error = svn_auth_next_credentials(&creds, state, pool);
391 if (error || ! creds)
393 /* Failure or too many attempts */
394 svn_error_clear(error);
395 break;
397 else
399 svn_auth_cred_ssl_client_cert_t *client_creds = creds;
401 clicert = ne_ssl_clicert_read(client_creds->cert_file);
402 if (clicert)
404 if (! ne_ssl_clicert_encrypted(clicert) ||
405 client_ssl_decrypt_cert(ras, client_creds->cert_file,
406 clicert))
408 ne_ssl_set_clicert(sess, clicert);
410 break;
415 svn_pool_destroy(pool);
418 /* Set *PROXY_HOST, *PROXY_PORT, *PROXY_USERNAME, *PROXY_PASSWORD,
419 * *TIMEOUT_SECONDS, *NEON_DEBUG, *COMPRESSION, *NEON_AUTH_TYPES, and
420 * *PK11_PROVIDER to the information for REQUESTED_HOST, allocated in
421 * POOL, if there is any applicable information. If there is no
422 * applicable information or if there is an error, then set
423 * *PROXY_PORT to (unsigned int) -1, *TIMEOUT_SECONDS and *NEON_DEBUG
424 * to zero, *COMPRESSION to TRUE, *NEON_AUTH_TYPES is left untouched,
425 * and the rest are set to NULL. This function can return an error,
426 * so before examining any values, check the error return value.
428 static svn_error_t *get_server_settings(const char **proxy_host,
429 unsigned int *proxy_port,
430 const char **proxy_username,
431 const char **proxy_password,
432 int *timeout_seconds,
433 int *neon_debug,
434 svn_boolean_t *compression,
435 unsigned int *neon_auth_types,
436 const char **pk11_provider,
437 svn_config_t *cfg,
438 const char *requested_host,
439 apr_pool_t *pool)
441 const char *exceptions, *port_str, *timeout_str, *server_group;
442 const char *debug_str, *http_auth_types;
443 svn_boolean_t is_exception = FALSE;
444 /* If we find nothing, default to nulls. */
445 *proxy_host = NULL;
446 *proxy_port = (unsigned int) -1;
447 *proxy_username = NULL;
448 *proxy_password = NULL;
449 port_str = NULL;
450 timeout_str = NULL;
451 debug_str = NULL;
452 http_auth_types = NULL;
453 *pk11_provider = NULL;
455 /* Use the default proxy-specific settings if and only if
456 "http-proxy-exceptions" is not set to exclude this host. */
457 svn_config_get(cfg, &exceptions, SVN_CONFIG_SECTION_GLOBAL,
458 SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, NULL);
459 if (exceptions)
461 apr_array_header_t *l = svn_cstring_split(exceptions, ",", TRUE, pool);
462 is_exception = svn_cstring_match_glob_list(requested_host, l);
464 if (! is_exception)
466 svn_config_get(cfg, proxy_host, SVN_CONFIG_SECTION_GLOBAL,
467 SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL);
468 svn_config_get(cfg, &port_str, SVN_CONFIG_SECTION_GLOBAL,
469 SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL);
470 svn_config_get(cfg, proxy_username, SVN_CONFIG_SECTION_GLOBAL,
471 SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL);
472 svn_config_get(cfg, proxy_password, SVN_CONFIG_SECTION_GLOBAL,
473 SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL);
476 /* Apply non-proxy-specific settings regardless of exceptions: */
477 svn_config_get(cfg, &timeout_str, SVN_CONFIG_SECTION_GLOBAL,
478 SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL);
479 SVN_ERR(svn_config_get_bool(cfg, compression, SVN_CONFIG_SECTION_GLOBAL,
480 SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE));
481 svn_config_get(cfg, &debug_str, SVN_CONFIG_SECTION_GLOBAL,
482 SVN_CONFIG_OPTION_NEON_DEBUG_MASK, NULL);
483 #ifdef SVN_NEON_0_26
484 svn_config_get(cfg, &http_auth_types, SVN_CONFIG_SECTION_GLOBAL,
485 SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, NULL);
486 #endif
487 svn_config_get(cfg, pk11_provider, SVN_CONFIG_SECTION_GLOBAL,
488 SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER, NULL);
490 if (cfg)
491 server_group = svn_config_find_group(cfg, requested_host,
492 SVN_CONFIG_SECTION_GROUPS, pool);
493 else
494 server_group = NULL;
496 if (server_group)
498 svn_config_get(cfg, proxy_host, server_group,
499 SVN_CONFIG_OPTION_HTTP_PROXY_HOST, *proxy_host);
500 svn_config_get(cfg, &port_str, server_group,
501 SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str);
502 svn_config_get(cfg, proxy_username, server_group,
503 SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, *proxy_username);
504 svn_config_get(cfg, proxy_password, server_group,
505 SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, *proxy_password);
506 svn_config_get(cfg, &timeout_str, server_group,
507 SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str);
508 SVN_ERR(svn_config_get_bool(cfg, compression, server_group,
509 SVN_CONFIG_OPTION_HTTP_COMPRESSION,
510 *compression));
511 svn_config_get(cfg, &debug_str, server_group,
512 SVN_CONFIG_OPTION_NEON_DEBUG_MASK, debug_str);
513 #ifdef SVN_NEON_0_26
514 svn_config_get(cfg, &http_auth_types, server_group,
515 SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, http_auth_types);
516 #endif
517 svn_config_get(cfg, pk11_provider, server_group,
518 SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER, *pk11_provider);
521 /* Special case: convert the port value, if any. */
522 if (port_str)
524 char *endstr;
525 const long int port = strtol(port_str, &endstr, 10);
527 if (*endstr)
528 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
529 _("Invalid URL: illegal character in proxy "
530 "port number"));
531 if (port < 0)
532 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
533 _("Invalid URL: negative proxy port number"));
534 if (port > 65535)
535 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
536 _("Invalid URL: proxy port number greater "
537 "than maximum TCP port number 65535"));
538 *proxy_port = port;
540 else
541 *proxy_port = 80;
543 if (timeout_str)
545 char *endstr;
546 const long int timeout = strtol(timeout_str, &endstr, 10);
548 if (*endstr)
549 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
550 _("Invalid config: illegal character in "
551 "timeout value"));
552 if (timeout < 0)
553 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
554 _("Invalid config: negative timeout value"));
555 *timeout_seconds = timeout;
557 else
558 *timeout_seconds = 0;
560 if (debug_str)
562 char *endstr;
563 const long int debug = strtol(debug_str, &endstr, 10);
565 if (*endstr)
566 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
567 _("Invalid config: illegal character in "
568 "debug mask value"));
570 *neon_debug = debug;
572 else
573 *neon_debug = 0;
575 #ifdef SVN_NEON_0_26
576 if (http_auth_types)
578 char *token, *last;
579 char *auth_types_list = apr_palloc(pool, strlen(http_auth_types) + 1);
580 apr_collapse_spaces(auth_types_list, http_auth_types);
581 while ((token = apr_strtok(auth_types_list, ";", &last)) != NULL)
583 auth_types_list = NULL;
584 if (svn_cstring_casecmp("basic", token) == 0)
585 *neon_auth_types |= NE_AUTH_BASIC;
586 else if (svn_cstring_casecmp("digest", token) == 0)
587 *neon_auth_types |= NE_AUTH_DIGEST;
588 else if (svn_cstring_casecmp("negotiate", token) == 0)
589 *neon_auth_types |= NE_AUTH_NEGOTIATE;
590 else
591 return svn_error_createf(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
592 _("Invalid config: unknown http auth"
593 "type '%s'"), token);
596 #endif /* SVN_NEON_0_26 */
598 return SVN_NO_ERROR;
602 /* Userdata for the `proxy_auth' function. */
603 struct proxy_auth_baton
605 const char *username; /* Cannot be NULL, but "" is okay. */
606 const char *password; /* Cannot be NULL, but "" is okay. */
610 /* An `ne_request_auth' callback, see ne_auth.h. USERDATA is a
611 * `struct proxy_auth_baton *'.
613 * If ATTEMPT < 10, copy USERDATA->username and USERDATA->password
614 * into USERNAME and PASSWORD respectively (but do not copy more than
615 * NE_ABUFSIZ bytes of either), and return zero to indicate to Neon
616 * that authentication should be attempted.
618 * If ATTEMPT >= 10, copy nothing into USERNAME and PASSWORD and
619 * return 1, to cancel further authentication attempts.
621 * Ignore REALM.
623 * ### Note: There is no particularly good reason for the 10-attempt
624 * limit. Perhaps there should only be one attempt, and if it fails,
625 * we just cancel any further attempts. I used 10 just in case the
626 * proxy tries various times with various realms, since we ignore
627 * REALM. And why do we ignore REALM? Because we currently don't
628 * have any way to specify different auth information for different
629 * realms. (I'm assuming that REALM would be a realm on the proxy
630 * server, not on the Subversion repository server that is the real
631 * destination.) Do we have any need to support proxy realms?
633 static int proxy_auth(void *userdata,
634 const char *realm,
635 int attempt,
636 char *username,
637 char *password)
639 struct proxy_auth_baton *pab = userdata;
641 if (attempt >= 10)
642 return 1;
644 /* Else. */
646 apr_cpystrn(username, pab->username, NE_ABUFSIZ);
647 apr_cpystrn(password, pab->password, NE_ABUFSIZ);
649 return 0;
652 #define RA_NEON_DESCRIPTION \
653 N_("Module for accessing a repository via WebDAV protocol using Neon.")
655 static const char *
656 ra_neon_get_description(void)
658 return _(RA_NEON_DESCRIPTION);
661 static const char * const *
662 ra_neon_get_schemes(apr_pool_t *pool)
664 static const char *schemes_no_ssl[] = { "http", NULL };
665 static const char *schemes_ssl[] = { "http", "https", NULL };
667 return ne_has_support(NE_FEATURE_SSL) ? schemes_ssl : schemes_no_ssl;
670 typedef struct neonprogress_baton_t
672 svn_ra_progress_notify_func_t progress_func;
673 void *progress_baton;
674 apr_pool_t *pool;
675 } neonprogress_baton_t;
677 static void
678 #ifdef SVN_NEON_0_27
679 ra_neon_neonprogress(void *baton, ne_off_t progress, ne_off_t total)
680 #else
681 ra_neon_neonprogress(void *baton, off_t progress, off_t total)
682 #endif /* SVN_NEON_0_27 */
684 const neonprogress_baton_t *neonprogress_baton = baton;
685 if (neonprogress_baton->progress_func)
687 neonprogress_baton->progress_func(progress, total,
688 neonprogress_baton->progress_baton,
689 neonprogress_baton->pool);
695 /** Capabilities exchange. */
697 /* Both server and repository support the capability. */
698 static const char *capability_yes = "yes";
699 /* Either server or repository does not support the capability. */
700 static const char *capability_no = "no";
701 /* Server supports the capability, but don't yet know if repository does. */
702 static const char *capability_server_yes = "server-yes";
705 /* Store in RAS the capabilities discovered from REQ's headers.
706 Use POOL for temporary allocation only. */
707 static void
708 parse_capabilities(ne_request *req,
709 svn_ra_neon__session_t *ras,
710 apr_pool_t *pool)
712 const char *header_value;
714 /* Start out assuming all capabilities are unsupported. */
715 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
716 APR_HASH_KEY_STRING, capability_no);
717 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
718 APR_HASH_KEY_STRING, capability_no);
719 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
720 APR_HASH_KEY_STRING, capability_no);
722 /* Then find out which ones are supported. */
723 header_value = ne_get_response_header(req, "dav");
724 if (header_value)
726 /* Multiple headers of the same name will have been merged
727 together by the time we see them (either by an intermediary,
728 as is permitted in HTTP, or by neon) -- merged in the sense
729 that if a header "foo" appears multiple times, all the values
730 will be concatenated together, with spaces at the splice
731 points. For example, if the server sent:
733 DAV: 1,2
734 DAV: version-control,checkout,working-resource
735 DAV: merge,baseline,activity,version-controlled-collection
736 DAV: http://subversion.tigris.org/xmlns/dav/svn/depth
738 Here we might see:
740 header_value == "1,2, version-control,checkout,working-resource, merge,baseline,activity,version-controlled-collection, http://subversion.tigris.org/xmlns/dav/svn/depth, <http://apache.org/dav/propset/fs/1>"
742 (Deliberately not line-wrapping that, so you can see what
743 we're about to parse.)
746 apr_array_header_t *vals =
747 svn_cstring_split(header_value, ",", TRUE, pool);
749 /* Right now we only have a few capabilities to detect, so
750 just seek for them directly. This could be written
751 slightly more efficiently, but that wouldn't be worth it
752 until we have many more capabilities. */
754 if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals))
755 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
756 APR_HASH_KEY_STRING, capability_yes);
758 if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals))
759 /* The server doesn't know what repository we're referring
760 to, so it can't just say capability_yes. */
761 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
762 APR_HASH_KEY_STRING, capability_server_yes);
764 if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals))
765 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
766 APR_HASH_KEY_STRING, capability_yes);
768 if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY,
769 vals))
770 apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
771 APR_HASH_KEY_STRING, capability_yes);
776 /* Exchange capabilities with the server, by sending an OPTIONS
777 request announcing the client's capabilities, and by filling
778 RAS->capabilities with the server's capabilities as read from the
779 response headers. Use POOL only for temporary allocation. */
780 static svn_error_t *
781 exchange_capabilities(svn_ra_neon__session_t *ras, apr_pool_t *pool)
783 int http_ret_code;
784 svn_ra_neon__request_t *rar;
785 svn_error_t *err = SVN_NO_ERROR;
787 rar = svn_ra_neon__request_create(ras, "OPTIONS", ras->url->data, pool);
789 ne_add_request_header(rar->ne_req, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
790 ne_add_request_header(rar->ne_req, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
791 ne_add_request_header(rar->ne_req, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
793 err = svn_ra_neon__request_dispatch(&http_ret_code, rar,
794 NULL, NULL, 200, 0, pool);
795 if (err)
796 goto cleanup;
798 if (http_ret_code == 200)
800 parse_capabilities(rar->ne_req, ras, pool);
802 else
804 /* "can't happen", because svn_ra_neon__request_dispatch()
805 itself should have returned error if response code != 200. */
806 return svn_error_createf
807 (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
808 _("OPTIONS request (for capabilities) got HTTP response code %d"),
809 http_ret_code);
812 cleanup:
813 svn_ra_neon__request_destroy(rar);
815 return err;
819 svn_error_t *
820 svn_ra_neon__has_capability(svn_ra_session_t *session,
821 svn_boolean_t *has,
822 const char *capability,
823 apr_pool_t *pool)
825 svn_ra_neon__session_t *ras = session->priv;
826 const char *cap_result = apr_hash_get(ras->capabilities,
827 capability,
828 APR_HASH_KEY_STRING);
830 /* If any capability is unknown, they're all unknown, so ask. */
831 if (cap_result == NULL)
832 SVN_ERR(exchange_capabilities(ras, pool));
835 /* Try again, now that we've fetched the capabilities. */
836 cap_result = apr_hash_get(ras->capabilities,
837 capability, APR_HASH_KEY_STRING);
839 /* Some capabilities depend on the repository as well as the server.
840 NOTE: ../libsvn_ra_serf/serf.c:svn_ra_serf__has_capability()
841 has a very similar code block. If you change something here,
842 check there as well. */
843 if (cap_result == capability_server_yes)
845 if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
847 /* Handle mergeinfo specially. Mergeinfo depends on the
848 repository as well as the server, but the server routine
849 that answered our exchange_capabilities() call above
850 didn't even know which repository we were interested in
851 -- it just told us whether the server supports mergeinfo.
852 If the answer was 'no', there's no point checking the
853 particular repository; but if it was 'yes, we still must
854 change it to 'no' iff the repository itself doesn't
855 support mergeinfo. */
856 svn_mergeinfo_catalog_t ignored;
857 svn_error_t *err;
858 apr_array_header_t *paths = apr_array_make(pool, 1,
859 sizeof(char *));
860 APR_ARRAY_PUSH(paths, const char *) = "";
862 err = svn_ra_neon__get_mergeinfo(session, &ignored, paths, 0,
863 FALSE, FALSE, pool);
865 if (err)
867 if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
869 svn_error_clear(err);
870 cap_result = capability_no;
872 else if (err->apr_err == SVN_ERR_FS_NOT_FOUND
873 || err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND)
875 /* Mergeinfo requests use relative paths, and
876 anyway we're in r0, so this is a likely error,
877 but it means the repository supports mergeinfo! */
878 svn_error_clear(err);
879 cap_result = capability_yes;
881 else
882 return err;
885 else
886 cap_result = capability_yes;
888 apr_hash_set(ras->capabilities,
889 SVN_RA_CAPABILITY_MERGEINFO, APR_HASH_KEY_STRING,
890 cap_result);
892 else
894 return svn_error_createf
895 (SVN_ERR_UNKNOWN_CAPABILITY, NULL,
896 _("Don't know how to handle '%s' for capability '%s'"),
897 capability_server_yes, capability);
901 if (cap_result == capability_yes)
903 *has = TRUE;
905 else if (cap_result == capability_no)
907 *has = FALSE;
909 else if (cap_result == NULL)
911 return svn_error_createf
912 (SVN_ERR_UNKNOWN_CAPABILITY, NULL,
913 _("Don't know anything about capability '%s'"), capability);
915 else /* "can't happen" */
917 /* Well, let's hope it's a string. */
918 return svn_error_createf
919 (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
920 _("Attempt to fetch capability '%s' resulted in '%s'"),
921 capability, cap_result);
924 return SVN_NO_ERROR;
929 /* ### need an ne_session_dup to avoid the second gethostbyname
930 * call and make this halfway sane. */
933 /* Parse URL into *URI, doing some sanity checking and initializing the port
934 to a default value if it wasn't specified in URL. */
935 static svn_error_t *
936 parse_url(ne_uri *uri, const char *url)
938 if (ne_uri_parse(url, uri)
939 || uri->host == NULL || uri->path == NULL || uri->scheme == NULL)
941 ne_uri_free(uri);
942 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
943 _("Malformed URL for repository"));
945 if (uri->port == 0)
946 uri->port = ne_uri_defaultport(uri->scheme);
948 return SVN_NO_ERROR;
951 static svn_error_t *
952 svn_ra_neon__open(svn_ra_session_t *session,
953 const char *repos_URL,
954 const svn_ra_callbacks2_t *callbacks,
955 void *callback_baton,
956 apr_hash_t *config,
957 apr_pool_t *pool)
959 apr_size_t len;
960 ne_session *sess, *sess2;
961 ne_uri *uri = apr_pcalloc(pool, sizeof(*uri));
962 svn_ra_neon__session_t *ras;
963 int is_ssl_session;
964 svn_boolean_t compression;
965 svn_config_t *cfg;
966 const char *server_group;
967 char *itr;
968 unsigned int neon_auth_types = 0;
969 const char *pkcs11_provider;
970 neonprogress_baton_t *neonprogress_baton =
971 apr_pcalloc(pool, sizeof(*neonprogress_baton));
972 const char *useragent = NULL;
973 const char *client_string = NULL;
975 if (callbacks->get_client_string)
976 callbacks->get_client_string(callback_baton, &client_string, pool);
978 if (client_string)
979 useragent = apr_pstrcat(pool, "SVN/" SVN_VERSION "/", client_string, NULL);
980 else
981 useragent = "SVN/" SVN_VERSION;
983 /* Sanity check the URI */
984 SVN_ERR(parse_url(uri, repos_URL));
986 /* make sure we eventually destroy the uri */
987 apr_pool_cleanup_register(pool, uri, cleanup_uri, apr_pool_cleanup_null);
990 /* Can we initialize network? */
991 if (ne_sock_init() != 0)
992 return svn_error_create(SVN_ERR_RA_DAV_SOCK_INIT, NULL,
993 _("Network socket initialization failed"));
995 /* we want to know if the repository is actually somewhere else */
996 /* ### not yet: http_redirect_register(sess, ... ); */
998 /* HACK! Neon uses strcmp when checking for https, but RFC 2396 says
999 * we should be using case-insensitive comparisons when checking for
1000 * URI schemes. To allow our users to use WeIrd CasE HttPS we force
1001 * the scheme to lower case before we pass it on to Neon, otherwise we
1002 * would crash later on when we assume Neon has set up its https stuff
1003 * but it really didn't. */
1004 for (itr = uri->scheme; *itr; ++itr)
1005 *itr = tolower(*itr);
1007 is_ssl_session = (svn_cstring_casecmp(uri->scheme, "https") == 0);
1008 if (is_ssl_session)
1010 if (ne_has_support(NE_FEATURE_SSL) == 0)
1011 return svn_error_create(SVN_ERR_RA_DAV_SOCK_INIT, NULL,
1012 _("SSL is not supported"));
1014 /* Create two neon session objects, and set their properties... */
1015 sess = ne_session_create(uri->scheme, uri->host, uri->port);
1016 sess2 = ne_session_create(uri->scheme, uri->host, uri->port);
1017 /* make sure we will eventually destroy the session */
1018 apr_pool_cleanup_register(pool, sess, cleanup_session, apr_pool_cleanup_null);
1019 apr_pool_cleanup_register(pool, sess2, cleanup_session,
1020 apr_pool_cleanup_null);
1022 cfg = config ? apr_hash_get(config,
1023 SVN_CONFIG_CATEGORY_SERVERS,
1024 APR_HASH_KEY_STRING) : NULL;
1025 if (cfg)
1026 server_group = svn_config_find_group(cfg, uri->host,
1027 SVN_CONFIG_SECTION_GROUPS, pool);
1028 else
1029 server_group = NULL;
1031 /* If there's a timeout or proxy for this URL, use it. */
1033 const char *proxy_host;
1034 unsigned int proxy_port;
1035 const char *proxy_username;
1036 const char *proxy_password;
1037 int timeout;
1038 int debug;
1040 SVN_ERR(get_server_settings(&proxy_host,
1041 &proxy_port,
1042 &proxy_username,
1043 &proxy_password,
1044 &timeout,
1045 &debug,
1046 &compression,
1047 &neon_auth_types,
1048 &pkcs11_provider,
1049 cfg,
1050 uri->host,
1051 pool));
1053 #ifdef SVN_NEON_0_26
1054 if (neon_auth_types == 0)
1056 /* If there were no auth types specified in the configuration
1057 file, provide the appropriate defaults. */
1058 neon_auth_types = NE_AUTH_BASIC | NE_AUTH_DIGEST;
1059 if (is_ssl_session)
1060 neon_auth_types |= NE_AUTH_NEGOTIATE;
1062 #endif
1064 if (debug)
1065 ne_debug_init(stderr, debug);
1067 if (proxy_host)
1069 ne_session_proxy(sess, proxy_host, proxy_port);
1070 ne_session_proxy(sess2, proxy_host, proxy_port);
1072 if (proxy_username)
1074 /* Allocate the baton in pool, not on stack, so it will
1075 last till whenever Neon needs it. */
1076 struct proxy_auth_baton *pab = apr_palloc(pool, sizeof(*pab));
1078 pab->username = proxy_username;
1079 pab->password = proxy_password ? proxy_password : "";
1081 ne_set_proxy_auth(sess, proxy_auth, pab);
1082 ne_set_proxy_auth(sess2, proxy_auth, pab);
1086 if (!timeout)
1087 timeout = DEFAULT_HTTP_TIMEOUT;
1088 ne_set_read_timeout(sess, timeout);
1089 ne_set_read_timeout(sess2, timeout);
1091 #ifdef SVN_NEON_0_27
1092 ne_set_connect_timeout(sess, timeout);
1093 ne_set_connect_timeout(sess2, timeout);
1094 #endif
1097 if (useragent)
1099 ne_set_useragent(sess, useragent);
1100 ne_set_useragent(sess2, useragent);
1102 else
1104 ne_set_useragent(sess, "SVN/" SVN_VERSION);
1105 ne_set_useragent(sess2, "SVN/" SVN_VERSION);
1108 /* clean up trailing slashes from the URL */
1109 len = strlen(uri->path);
1110 if (len > 1 && (uri->path)[len - 1] == '/')
1111 (uri->path)[len - 1] = '\0';
1113 /* Create and fill a session_baton. */
1114 ras = apr_pcalloc(pool, sizeof(*ras));
1115 ras->pool = pool;
1116 ras->url = svn_stringbuf_create(repos_URL, pool);
1117 /* copies uri pointer members, they get free'd in __close. */
1118 ras->root = *uri;
1119 ras->ne_sess = sess;
1120 ras->ne_sess2 = sess2;
1121 ras->callbacks = callbacks;
1122 ras->callback_baton = callback_baton;
1123 ras->compression = compression;
1124 ras->progress_baton = callbacks->progress_baton;
1125 ras->progress_func = callbacks->progress_func;
1126 ras->capabilities = apr_hash_make(ras->pool);
1127 /* save config and server group in the auth parameter hash */
1128 svn_auth_set_parameter(ras->callbacks->auth_baton,
1129 SVN_AUTH_PARAM_CONFIG, cfg);
1130 svn_auth_set_parameter(ras->callbacks->auth_baton,
1131 SVN_AUTH_PARAM_SERVER_GROUP, server_group);
1133 /* note that ras->username and ras->password are still NULL at this
1134 point. */
1136 /* Register an authentication 'pull' callback with the neon sessions */
1137 #ifdef SVN_NEON_0_26
1138 ne_add_server_auth(sess, neon_auth_types, request_auth, ras);
1139 ne_add_server_auth(sess2, neon_auth_types, request_auth, ras);
1140 #else
1141 ne_set_server_auth(sess, request_auth, ras);
1142 ne_set_server_auth(sess2, request_auth, ras);
1143 #endif
1145 if (is_ssl_session)
1147 const char *authorities, *trust_default_ca;
1148 authorities = svn_config_get_server_setting(
1149 cfg, server_group,
1150 SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES,
1151 NULL);
1153 if (authorities != NULL)
1155 char *files, *file, *last;
1156 files = apr_pstrdup(pool, authorities);
1158 while ((file = apr_strtok(files, ";", &last)) != NULL)
1160 ne_ssl_certificate *ca_cert;
1161 files = NULL;
1162 ca_cert = ne_ssl_cert_read(file);
1163 if (ca_cert == NULL)
1165 return svn_error_createf
1166 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
1167 _("Invalid config: unable to load certificate file '%s'"),
1168 svn_path_local_style(file, pool));
1170 ne_ssl_trust_cert(sess, ca_cert);
1171 ne_ssl_trust_cert(sess2, ca_cert);
1175 /* When the CA certificate or server certificate has
1176 verification problems, neon will call our verify function before
1177 outright rejection of the connection.*/
1178 ne_ssl_set_verify(sess, server_ssl_callback, ras);
1179 ne_ssl_set_verify(sess2, server_ssl_callback, ras);
1180 /* For client connections, we register a callback for if the server
1181 wants to authenticate the client via client certificate. */
1183 #ifdef SVN_NEON_0_28
1184 if (pkcs11_provider)
1186 ne_ssl_pkcs11_provider *provider;
1187 int rv;
1189 /* Initialize the PKCS#11 provider. */
1190 rv = ne_ssl_pkcs11_provider_init(&provider, pkcs11_provider);
1191 if (rv != NE_PK11_OK)
1193 return svn_error_createf
1194 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
1195 _("Invalid config: unable to load PKCS#11 provider '%s'"),
1196 pkcs11_provider);
1199 /* Share the provider between the two sessions. */
1200 ne_ssl_set_pkcs11_provider(sess, provider);
1201 ne_ssl_set_pkcs11_provider(sess2, provider);
1203 ne_ssl_pkcs11_provider_pin(provider, client_ssl_pkcs11_pin_entry,
1204 ras);
1206 apr_pool_cleanup_register(pool, provider, cleanup_p11provider,
1207 apr_pool_cleanup_null);
1209 /* Note the "else"; if a PKCS#11 provider is set up, a client
1210 cert callback is already configured, so don't displace it
1211 with the normal one here. */
1212 else
1213 #endif
1215 ne_ssl_provide_clicert(sess, client_ssl_callback, ras);
1216 ne_ssl_provide_clicert(sess2, client_ssl_callback, ras);
1219 /* See if the user wants us to trust "default" openssl CAs. */
1220 trust_default_ca = svn_config_get_server_setting(
1221 cfg, server_group,
1222 SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA,
1223 "true");
1225 if (svn_cstring_casecmp(trust_default_ca, "true") == 0)
1227 ne_ssl_trust_default_ca(sess);
1228 ne_ssl_trust_default_ca(sess2);
1231 neonprogress_baton->pool = pool;
1232 neonprogress_baton->progress_baton = callbacks->progress_baton;
1233 neonprogress_baton->progress_func = callbacks->progress_func;
1234 ne_set_progress(sess, ra_neon_neonprogress, neonprogress_baton);
1235 ne_set_progress(sess2, ra_neon_neonprogress, neonprogress_baton);
1236 session->priv = ras;
1238 SVN_ERR(exchange_capabilities(ras, pool));
1240 return SVN_NO_ERROR;
1244 static svn_error_t *svn_ra_neon__reparent(svn_ra_session_t *session,
1245 const char *url,
1246 apr_pool_t *pool)
1248 svn_ra_neon__session_t *ras = session->priv;
1249 ne_uri *uri = apr_pcalloc(session->pool, sizeof(*uri));
1251 SVN_ERR(parse_url(uri, url));
1252 apr_pool_cleanup_register(session->pool, uri, cleanup_uri,
1253 apr_pool_cleanup_null);
1255 ras->root = *uri;
1256 svn_stringbuf_set(ras->url, url);
1257 return SVN_NO_ERROR;
1260 static svn_error_t *svn_ra_neon__get_session_url(svn_ra_session_t *session,
1261 const char **url,
1262 apr_pool_t *pool)
1264 svn_ra_neon__session_t *ras = session->priv;
1265 *url = apr_pstrmemdup(pool, ras->url->data, ras->url->len);
1266 return SVN_NO_ERROR;
1269 static svn_error_t *svn_ra_neon__get_repos_root(svn_ra_session_t *session,
1270 const char **url,
1271 apr_pool_t *pool)
1273 svn_ra_neon__session_t *ras = session->priv;
1275 if (! ras->repos_root)
1277 svn_string_t bc_relative;
1278 svn_stringbuf_t *url_buf;
1280 SVN_ERR(svn_ra_neon__get_baseline_info(NULL, NULL, &bc_relative,
1281 NULL, ras, ras->url->data,
1282 SVN_INVALID_REVNUM, pool));
1284 /* Remove as many path components from the URL as there are components
1285 in bc_relative. */
1286 url_buf = svn_stringbuf_dup(ras->url, pool);
1287 svn_path_remove_components
1288 (url_buf, svn_path_component_count(bc_relative.data));
1289 ras->repos_root = apr_pstrdup(ras->pool, url_buf->data);
1292 *url = ras->repos_root;
1293 return SVN_NO_ERROR;
1297 static svn_error_t *svn_ra_neon__do_get_uuid(svn_ra_session_t *session,
1298 const char **uuid,
1299 apr_pool_t *pool)
1301 svn_ra_neon__session_t *ras = session->priv;
1303 if (! ras->uuid)
1305 svn_ra_neon__resource_t *rsrc;
1306 const char *lopped_path;
1307 const svn_string_t *uuid_propval;
1309 SVN_ERR(svn_ra_neon__search_for_starting_props(&rsrc, &lopped_path,
1310 ras, ras->url->data,
1311 pool));
1312 SVN_ERR(svn_ra_neon__maybe_store_auth_info(ras, pool));
1314 uuid_propval = apr_hash_get(rsrc->propset,
1315 SVN_RA_NEON__PROP_REPOSITORY_UUID,
1316 APR_HASH_KEY_STRING);
1317 if (uuid_propval == NULL)
1318 /* ### better error reporting... */
1319 return svn_error_create(APR_EGENERAL, NULL,
1320 _("The UUID property was not found on the "
1321 "resource or any of its parents"));
1323 if (uuid_propval && (uuid_propval->len > 0))
1324 ras->uuid = apr_pstrdup(ras->pool, uuid_propval->data); /* cache */
1325 else
1326 return svn_error_create
1327 (SVN_ERR_RA_NO_REPOS_UUID, NULL,
1328 _("Please upgrade the server to 0.19 or later"));
1331 *uuid = ras->uuid;
1332 return SVN_NO_ERROR;
1336 static const svn_version_t *
1337 ra_neon_version(void)
1339 SVN_VERSION_BODY;
1342 static const svn_ra__vtable_t neon_vtable = {
1343 ra_neon_version,
1344 ra_neon_get_description,
1345 ra_neon_get_schemes,
1346 svn_ra_neon__open,
1347 svn_ra_neon__reparent,
1348 svn_ra_neon__get_session_url,
1349 svn_ra_neon__get_latest_revnum,
1350 svn_ra_neon__get_dated_revision,
1351 svn_ra_neon__change_rev_prop,
1352 svn_ra_neon__rev_proplist,
1353 svn_ra_neon__rev_prop,
1354 svn_ra_neon__get_commit_editor,
1355 svn_ra_neon__get_file,
1356 svn_ra_neon__get_dir,
1357 svn_ra_neon__get_mergeinfo,
1358 svn_ra_neon__do_update,
1359 svn_ra_neon__do_switch,
1360 svn_ra_neon__do_status,
1361 svn_ra_neon__do_diff,
1362 svn_ra_neon__get_log,
1363 svn_ra_neon__do_check_path,
1364 svn_ra_neon__do_stat,
1365 svn_ra_neon__do_get_uuid,
1366 svn_ra_neon__get_repos_root,
1367 svn_ra_neon__get_locations,
1368 svn_ra_neon__get_location_segments,
1369 svn_ra_neon__get_file_revs,
1370 svn_ra_neon__lock,
1371 svn_ra_neon__unlock,
1372 svn_ra_neon__get_lock,
1373 svn_ra_neon__get_locks,
1374 svn_ra_neon__replay,
1375 svn_ra_neon__has_capability,
1376 svn_ra_neon__replay_range
1379 svn_error_t *
1380 svn_ra_neon__init(const svn_version_t *loader_version,
1381 const svn_ra__vtable_t **vtable,
1382 apr_pool_t *pool)
1384 static const svn_version_checklist_t checklist[] =
1386 { "svn_subr", svn_subr_version },
1387 { "svn_delta", svn_delta_version },
1388 { NULL, NULL }
1391 SVN_ERR(svn_ver_check_list(ra_neon_version(), checklist));
1393 /* Simplified version check to make sure we can safely use the
1394 VTABLE parameter. The RA loader does a more exhaustive check. */
1395 if (loader_version->major != SVN_VER_MAJOR)
1397 return svn_error_createf
1398 (SVN_ERR_VERSION_MISMATCH, NULL,
1399 _("Unsupported RA loader version (%d) for ra_neon"),
1400 loader_version->major);
1403 *vtable = &neon_vtable;
1405 return SVN_NO_ERROR;
1408 /* Compatibility wrapper for the 1.1 and before API. */
1409 #define NAME "ra_neon"
1410 #define DESCRIPTION RA_NEON_DESCRIPTION
1411 #define VTBL neon_vtable
1412 #define INITFUNC svn_ra_neon__init
1413 #define COMPAT_INITFUNC svn_ra_dav_init
1414 #include "../libsvn_ra/wrapper_template.h"