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 * ====================================================================
24 #define APR_WANT_STRFUNC
26 #include <apr_general.h>
31 #include "svn_error.h"
32 #include "svn_pools.h"
34 #include "../libsvn_ra/ra_loader.h"
35 #include "svn_config.h"
36 #include "svn_delta.h"
37 #include "svn_version.h"
41 #include "svn_private_config.h"
44 #include <ne_pkcs11.h>
49 #define DEFAULT_HTTP_TIMEOUT 3600
52 /* a cleanup routine attached to the pool that contains the RA session
54 static apr_status_t
cleanup_session(void *sess
)
56 ne_session_destroy(sess
);
60 /* a cleanup routine attached to the pool that contains the RA session
62 static apr_status_t
cleanup_uri(void *uri
)
69 /* a cleanup routine attached to the pool that contains the PKCS#11
71 static apr_status_t
cleanup_p11provider(void *provider
)
73 ne_ssl_pkcs11_provider
*prov
= provider
;
74 ne_ssl_pkcs11_provider_destroy(prov
);
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
)
86 svn_ra_neon__session_t
*ras
= userdata
;
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
)
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
,
114 ras
->callbacks
->auth_baton
,
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
,
126 svn_error_clear(err
);
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
);
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. */
152 convert_neon_failures(int neon_failures
)
154 apr_uint32_t svn_failures
= 0;
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. */
169 svn_failures
|= SVN_AUTH_SSL_OTHER
;
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. */
179 server_ssl_callback(void *userdata
,
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
;
186 svn_auth_iterstate_t
*state
;
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
,
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
,
223 apr_pool_create(&pool
, ras
->pool
);
224 error
= svn_auth_first_credentials(&creds
, &state
,
225 SVN_AUTH_CRED_SSL_SERVER_TRUST
,
227 ras
->callbacks
->auth_baton
,
229 if (error
|| ! creds
)
231 svn_error_clear(error
);
235 server_creds
= creds
;
236 error
= svn_auth_save_credentials(state
, pool
);
239 /* It would be nice to show the error to the user somehow... */
240 svn_error_clear(error
);
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
;
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
;
261 svn_boolean_t ok
= FALSE
;
265 apr_pool_create(&pool
, ras
->pool
);
266 for (try = 0; TRUE
; ++try)
270 error
= svn_auth_first_credentials(&creds
, &state
,
271 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW
,
273 ras
->callbacks
->auth_baton
,
278 error
= svn_auth_next_credentials(&creds
, state
, pool
);
281 if (error
|| ! creds
)
283 /* Failure or too many attempts */
284 svn_error_clear(error
);
289 svn_auth_cred_ssl_client_cert_pw_t
*pw_creds
= creds
;
291 if (ne_ssl_clicert_decrypt(clicert
, pw_creds
->password
) == 0)
299 svn_pool_destroy(pool
);
305 /* Callback invoked to enter PKCS#11 PIN code. */
307 client_ssl_pkcs11_pin_entry(void *userdata
,
309 const char *slot_descr
,
310 const char *token_label
,
314 svn_ra_neon__session_t
*ras
= userdata
;
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
, "");
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
,
335 ras
->callbacks
->auth_baton
,
340 err
= svn_auth_next_credentials(&creds
, ras
->auth_iterstate
, ras
->pool
);
345 svn_error_clear(err
);
351 apr_cpystrn(pin
, pw_creds
->password
, NE_SSL_P11PINLEN
);
358 client_ssl_callback(void *userdata
, ne_session
*sess
,
359 const ne_ssl_dname
*const *dnames
,
362 svn_ra_neon__session_t
*ras
= userdata
;
363 ne_ssl_client_cert
*clicert
= NULL
;
365 svn_auth_iterstate_t
*state
;
366 const char *realmstring
;
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)
380 error
= svn_auth_first_credentials(&creds
, &state
,
381 SVN_AUTH_CRED_SSL_CLIENT_CERT
,
383 ras
->callbacks
->auth_baton
,
388 error
= svn_auth_next_credentials(&creds
, state
, pool
);
391 if (error
|| ! creds
)
393 /* Failure or too many attempts */
394 svn_error_clear(error
);
399 svn_auth_cred_ssl_client_cert_t
*client_creds
= creds
;
401 clicert
= ne_ssl_clicert_read(client_creds
->cert_file
);
404 if (! ne_ssl_clicert_encrypted(clicert
) ||
405 client_ssl_decrypt_cert(ras
, client_creds
->cert_file
,
408 ne_ssl_set_clicert(sess
, clicert
);
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
,
434 svn_boolean_t
*compression
,
435 unsigned int *neon_auth_types
,
436 const char **pk11_provider
,
438 const char *requested_host
,
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. */
446 *proxy_port
= (unsigned int) -1;
447 *proxy_username
= NULL
;
448 *proxy_password
= 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
);
461 apr_array_header_t
*l
= svn_cstring_split(exceptions
, ",", TRUE
, pool
);
462 is_exception
= svn_cstring_match_glob_list(requested_host
, l
);
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
);
484 svn_config_get(cfg
, &http_auth_types
, SVN_CONFIG_SECTION_GLOBAL
,
485 SVN_CONFIG_OPTION_HTTP_AUTH_TYPES
, NULL
);
487 svn_config_get(cfg
, pk11_provider
, SVN_CONFIG_SECTION_GLOBAL
,
488 SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER
, NULL
);
491 server_group
= svn_config_find_group(cfg
, requested_host
,
492 SVN_CONFIG_SECTION_GROUPS
, pool
);
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
,
511 svn_config_get(cfg
, &debug_str
, server_group
,
512 SVN_CONFIG_OPTION_NEON_DEBUG_MASK
, debug_str
);
514 svn_config_get(cfg
, &http_auth_types
, server_group
,
515 SVN_CONFIG_OPTION_HTTP_AUTH_TYPES
, http_auth_types
);
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. */
525 const long int port
= strtol(port_str
, &endstr
, 10);
528 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL
, NULL
,
529 _("Invalid URL: illegal character in proxy "
532 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL
, NULL
,
533 _("Invalid URL: negative proxy port number"));
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"));
546 const long int timeout
= strtol(timeout_str
, &endstr
, 10);
549 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE
, NULL
,
550 _("Invalid config: illegal character in "
553 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE
, NULL
,
554 _("Invalid config: negative timeout value"));
555 *timeout_seconds
= timeout
;
558 *timeout_seconds
= 0;
563 const long int debug
= strtol(debug_str
, &endstr
, 10);
566 return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE
, NULL
,
567 _("Invalid config: illegal character in "
568 "debug mask value"));
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
;
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 */
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.
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
,
639 struct proxy_auth_baton
*pab
= userdata
;
646 apr_cpystrn(username
, pab
->username
, NE_ABUFSIZ
);
647 apr_cpystrn(password
, pab
->password
, NE_ABUFSIZ
);
652 #define RA_NEON_DESCRIPTION \
653 N_("Module for accessing a repository via WebDAV protocol using Neon.")
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
;
675 } neonprogress_baton_t
;
679 ra_neon_neonprogress(void *baton
, ne_off_t progress
, ne_off_t total
)
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. */
708 parse_capabilities(ne_request
*req
,
709 svn_ra_neon__session_t
*ras
,
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");
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:
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
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
,
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. */
781 exchange_capabilities(svn_ra_neon__session_t
*ras
, apr_pool_t
*pool
)
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
);
798 if (http_ret_code
== 200)
800 parse_capabilities(rar
->ne_req
, ras
, pool
);
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"),
813 svn_ra_neon__request_destroy(rar
);
820 svn_ra_neon__has_capability(svn_ra_session_t
*session
,
822 const char *capability
,
825 svn_ra_neon__session_t
*ras
= session
->priv
;
826 const char *cap_result
= apr_hash_get(ras
->capabilities
,
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
;
858 apr_array_header_t
*paths
= apr_array_make(pool
, 1,
860 APR_ARRAY_PUSH(paths
, const char *) = "";
862 err
= svn_ra_neon__get_mergeinfo(session
, &ignored
, paths
, 0,
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
;
886 cap_result
= capability_yes
;
888 apr_hash_set(ras
->capabilities
,
889 SVN_RA_CAPABILITY_MERGEINFO
, APR_HASH_KEY_STRING
,
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
)
905 else if (cap_result
== capability_no
)
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
);
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. */
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
)
942 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL
, NULL
,
943 _("Malformed URL for repository"));
946 uri
->port
= ne_uri_defaultport(uri
->scheme
);
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
,
960 ne_session
*sess
, *sess2
;
961 ne_uri
*uri
= apr_pcalloc(pool
, sizeof(*uri
));
962 svn_ra_neon__session_t
*ras
;
964 svn_boolean_t compression
;
966 const char *server_group
;
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
);
979 useragent
= apr_pstrcat(pool
, "SVN/" SVN_VERSION
"/", client_string
, NULL
);
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);
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
;
1026 server_group
= svn_config_find_group(cfg
, uri
->host
,
1027 SVN_CONFIG_SECTION_GROUPS
, pool
);
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
;
1040 SVN_ERR(get_server_settings(&proxy_host
,
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
;
1060 neon_auth_types
|= NE_AUTH_NEGOTIATE
;
1065 ne_debug_init(stderr
, debug
);
1069 ne_session_proxy(sess
, proxy_host
, proxy_port
);
1070 ne_session_proxy(sess2
, proxy_host
, proxy_port
);
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
);
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
);
1099 ne_set_useragent(sess
, useragent
);
1100 ne_set_useragent(sess2
, useragent
);
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
));
1116 ras
->url
= svn_stringbuf_create(repos_URL
, pool
);
1117 /* copies uri pointer members, they get free'd in __close. */
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
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
);
1141 ne_set_server_auth(sess
, request_auth
, ras
);
1142 ne_set_server_auth(sess2
, request_auth
, ras
);
1147 const char *authorities
, *trust_default_ca
;
1148 authorities
= svn_config_get_server_setting(
1150 SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES
,
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
;
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
;
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'"),
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
,
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. */
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(
1222 SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA
,
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
,
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
);
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
,
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
,
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
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
,
1301 svn_ra_neon__session_t
*ras
= session
->priv
;
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
,
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 */
1326 return svn_error_create
1327 (SVN_ERR_RA_NO_REPOS_UUID
, NULL
,
1328 _("Please upgrade the server to 0.19 or later"));
1332 return SVN_NO_ERROR
;
1336 static const svn_version_t
*
1337 ra_neon_version(void)
1342 static const svn_ra__vtable_t neon_vtable
= {
1344 ra_neon_get_description
,
1345 ra_neon_get_schemes
,
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
,
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
1380 svn_ra_neon__init(const svn_version_t
*loader_version
,
1381 const svn_ra__vtable_t
**vtable
,
1384 static const svn_version_checklist_t checklist
[] =
1386 { "svn_subr", svn_subr_version
},
1387 { "svn_delta", svn_delta_version
},
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"