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
;
828 /* This capability doesn't rely on anything server side. */
829 if (strcmp(capability
, SVN_RA_CAPABILITY_COMMIT_REVPROPS
) == 0)
835 cap_result
= apr_hash_get(ras
->capabilities
,
837 APR_HASH_KEY_STRING
);
839 /* If any capability is unknown, they're all unknown, so ask. */
840 if (cap_result
== NULL
)
841 SVN_ERR(exchange_capabilities(ras
, pool
));
844 /* Try again, now that we've fetched the capabilities. */
845 cap_result
= apr_hash_get(ras
->capabilities
,
846 capability
, APR_HASH_KEY_STRING
);
848 /* Some capabilities depend on the repository as well as the server.
849 NOTE: ../libsvn_ra_serf/serf.c:svn_ra_serf__has_capability()
850 has a very similar code block. If you change something here,
851 check there as well. */
852 if (cap_result
== capability_server_yes
)
854 if (strcmp(capability
, SVN_RA_CAPABILITY_MERGEINFO
) == 0)
856 /* Handle mergeinfo specially. Mergeinfo depends on the
857 repository as well as the server, but the server routine
858 that answered our exchange_capabilities() call above
859 didn't even know which repository we were interested in
860 -- it just told us whether the server supports mergeinfo.
861 If the answer was 'no', there's no point checking the
862 particular repository; but if it was 'yes, we still must
863 change it to 'no' iff the repository itself doesn't
864 support mergeinfo. */
865 svn_mergeinfo_catalog_t ignored
;
867 apr_array_header_t
*paths
= apr_array_make(pool
, 1,
869 APR_ARRAY_PUSH(paths
, const char *) = "";
871 err
= svn_ra_neon__get_mergeinfo(session
, &ignored
, paths
, 0,
876 if (err
->apr_err
== SVN_ERR_UNSUPPORTED_FEATURE
)
878 svn_error_clear(err
);
879 cap_result
= capability_no
;
881 else if (err
->apr_err
== SVN_ERR_FS_NOT_FOUND
882 || err
->apr_err
== SVN_ERR_RA_DAV_PATH_NOT_FOUND
)
884 /* Mergeinfo requests use relative paths, and
885 anyway we're in r0, so this is a likely error,
886 but it means the repository supports mergeinfo! */
887 svn_error_clear(err
);
888 cap_result
= capability_yes
;
895 cap_result
= capability_yes
;
897 apr_hash_set(ras
->capabilities
,
898 SVN_RA_CAPABILITY_MERGEINFO
, APR_HASH_KEY_STRING
,
903 return svn_error_createf
904 (SVN_ERR_UNKNOWN_CAPABILITY
, NULL
,
905 _("Don't know how to handle '%s' for capability '%s'"),
906 capability_server_yes
, capability
);
910 if (cap_result
== capability_yes
)
914 else if (cap_result
== capability_no
)
918 else if (cap_result
== NULL
)
920 return svn_error_createf
921 (SVN_ERR_UNKNOWN_CAPABILITY
, NULL
,
922 _("Don't know anything about capability '%s'"), capability
);
924 else /* "can't happen" */
926 /* Well, let's hope it's a string. */
927 return svn_error_createf
928 (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED
, NULL
,
929 _("Attempt to fetch capability '%s' resulted in '%s'"),
930 capability
, cap_result
);
938 /* ### need an ne_session_dup to avoid the second gethostbyname
939 * call and make this halfway sane. */
942 /* Parse URL into *URI, doing some sanity checking and initializing the port
943 to a default value if it wasn't specified in URL. */
945 parse_url(ne_uri
*uri
, const char *url
)
947 if (ne_uri_parse(url
, uri
)
948 || uri
->host
== NULL
|| uri
->path
== NULL
|| uri
->scheme
== NULL
)
951 return svn_error_create(SVN_ERR_RA_ILLEGAL_URL
, NULL
,
952 _("Malformed URL for repository"));
955 uri
->port
= ne_uri_defaultport(uri
->scheme
);
961 svn_ra_neon__open(svn_ra_session_t
*session
,
962 const char *repos_URL
,
963 const svn_ra_callbacks2_t
*callbacks
,
964 void *callback_baton
,
969 ne_session
*sess
, *sess2
;
970 ne_uri
*uri
= apr_pcalloc(pool
, sizeof(*uri
));
971 svn_ra_neon__session_t
*ras
;
973 svn_boolean_t compression
;
975 const char *server_group
;
977 unsigned int neon_auth_types
= 0;
978 const char *pkcs11_provider
;
979 neonprogress_baton_t
*neonprogress_baton
=
980 apr_pcalloc(pool
, sizeof(*neonprogress_baton
));
981 const char *useragent
= NULL
;
982 const char *client_string
= NULL
;
984 if (callbacks
->get_client_string
)
985 callbacks
->get_client_string(callback_baton
, &client_string
, pool
);
988 useragent
= apr_pstrcat(pool
, "SVN/" SVN_VERSION
"/", client_string
, NULL
);
990 useragent
= "SVN/" SVN_VERSION
;
992 /* Sanity check the URI */
993 SVN_ERR(parse_url(uri
, repos_URL
));
995 /* make sure we eventually destroy the uri */
996 apr_pool_cleanup_register(pool
, uri
, cleanup_uri
, apr_pool_cleanup_null
);
999 /* Can we initialize network? */
1000 if (ne_sock_init() != 0)
1001 return svn_error_create(SVN_ERR_RA_DAV_SOCK_INIT
, NULL
,
1002 _("Network socket initialization failed"));
1004 /* we want to know if the repository is actually somewhere else */
1005 /* ### not yet: http_redirect_register(sess, ... ); */
1007 /* HACK! Neon uses strcmp when checking for https, but RFC 2396 says
1008 * we should be using case-insensitive comparisons when checking for
1009 * URI schemes. To allow our users to use WeIrd CasE HttPS we force
1010 * the scheme to lower case before we pass it on to Neon, otherwise we
1011 * would crash later on when we assume Neon has set up its https stuff
1012 * but it really didn't. */
1013 for (itr
= uri
->scheme
; *itr
; ++itr
)
1014 *itr
= tolower(*itr
);
1016 is_ssl_session
= (svn_cstring_casecmp(uri
->scheme
, "https") == 0);
1019 if (ne_has_support(NE_FEATURE_SSL
) == 0)
1020 return svn_error_create(SVN_ERR_RA_DAV_SOCK_INIT
, NULL
,
1021 _("SSL is not supported"));
1023 /* Create two neon session objects, and set their properties... */
1024 sess
= ne_session_create(uri
->scheme
, uri
->host
, uri
->port
);
1025 sess2
= ne_session_create(uri
->scheme
, uri
->host
, uri
->port
);
1026 /* make sure we will eventually destroy the session */
1027 apr_pool_cleanup_register(pool
, sess
, cleanup_session
, apr_pool_cleanup_null
);
1028 apr_pool_cleanup_register(pool
, sess2
, cleanup_session
,
1029 apr_pool_cleanup_null
);
1031 cfg
= config
? apr_hash_get(config
,
1032 SVN_CONFIG_CATEGORY_SERVERS
,
1033 APR_HASH_KEY_STRING
) : NULL
;
1035 server_group
= svn_config_find_group(cfg
, uri
->host
,
1036 SVN_CONFIG_SECTION_GROUPS
, pool
);
1038 server_group
= NULL
;
1040 /* If there's a timeout or proxy for this URL, use it. */
1042 const char *proxy_host
;
1043 unsigned int proxy_port
;
1044 const char *proxy_username
;
1045 const char *proxy_password
;
1049 SVN_ERR(get_server_settings(&proxy_host
,
1062 #ifdef SVN_NEON_0_26
1063 if (neon_auth_types
== 0)
1065 /* If there were no auth types specified in the configuration
1066 file, provide the appropriate defaults. */
1067 neon_auth_types
= NE_AUTH_BASIC
| NE_AUTH_DIGEST
;
1069 neon_auth_types
|= NE_AUTH_NEGOTIATE
;
1074 ne_debug_init(stderr
, debug
);
1078 ne_session_proxy(sess
, proxy_host
, proxy_port
);
1079 ne_session_proxy(sess2
, proxy_host
, proxy_port
);
1083 /* Allocate the baton in pool, not on stack, so it will
1084 last till whenever Neon needs it. */
1085 struct proxy_auth_baton
*pab
= apr_palloc(pool
, sizeof(*pab
));
1087 pab
->username
= proxy_username
;
1088 pab
->password
= proxy_password
? proxy_password
: "";
1090 ne_set_proxy_auth(sess
, proxy_auth
, pab
);
1091 ne_set_proxy_auth(sess2
, proxy_auth
, pab
);
1093 #ifdef SVN_NEON_0_26
1096 /* Enable (only) the Negotiate scheme for proxy
1097 authentication, if no username/password is
1099 ne_add_proxy_auth(sess
, NE_AUTH_NEGOTIATE
, NULL
, NULL
);
1100 ne_add_proxy_auth(sess2
, NE_AUTH_NEGOTIATE
, NULL
, NULL
);
1106 timeout
= DEFAULT_HTTP_TIMEOUT
;
1107 ne_set_read_timeout(sess
, timeout
);
1108 ne_set_read_timeout(sess2
, timeout
);
1110 #ifdef SVN_NEON_0_27
1111 ne_set_connect_timeout(sess
, timeout
);
1112 ne_set_connect_timeout(sess2
, timeout
);
1118 ne_set_useragent(sess
, useragent
);
1119 ne_set_useragent(sess2
, useragent
);
1123 ne_set_useragent(sess
, "SVN/" SVN_VERSION
);
1124 ne_set_useragent(sess2
, "SVN/" SVN_VERSION
);
1127 /* clean up trailing slashes from the URL */
1128 len
= strlen(uri
->path
);
1129 if (len
> 1 && (uri
->path
)[len
- 1] == '/')
1130 (uri
->path
)[len
- 1] = '\0';
1132 /* Create and fill a session_baton. */
1133 ras
= apr_pcalloc(pool
, sizeof(*ras
));
1135 ras
->url
= svn_stringbuf_create(repos_URL
, pool
);
1136 /* copies uri pointer members, they get free'd in __close. */
1138 ras
->ne_sess
= sess
;
1139 ras
->ne_sess2
= sess2
;
1140 ras
->callbacks
= callbacks
;
1141 ras
->callback_baton
= callback_baton
;
1142 ras
->compression
= compression
;
1143 ras
->progress_baton
= callbacks
->progress_baton
;
1144 ras
->progress_func
= callbacks
->progress_func
;
1145 ras
->capabilities
= apr_hash_make(ras
->pool
);
1148 /* save config and server group in the auth parameter hash */
1149 svn_auth_set_parameter(ras
->callbacks
->auth_baton
,
1150 SVN_AUTH_PARAM_CONFIG
, cfg
);
1151 svn_auth_set_parameter(ras
->callbacks
->auth_baton
,
1152 SVN_AUTH_PARAM_SERVER_GROUP
, server_group
);
1154 /* note that ras->username and ras->password are still NULL at this
1157 /* Register an authentication 'pull' callback with the neon sessions */
1158 #ifdef SVN_NEON_0_26
1159 ne_add_server_auth(sess
, neon_auth_types
, request_auth
, ras
);
1160 ne_add_server_auth(sess2
, neon_auth_types
, request_auth
, ras
);
1162 ne_set_server_auth(sess
, request_auth
, ras
);
1163 ne_set_server_auth(sess2
, request_auth
, ras
);
1168 const char *authorities
, *trust_default_ca
;
1169 authorities
= svn_config_get_server_setting(
1171 SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES
,
1174 if (authorities
!= NULL
)
1176 char *files
, *file
, *last
;
1177 files
= apr_pstrdup(pool
, authorities
);
1179 while ((file
= apr_strtok(files
, ";", &last
)) != NULL
)
1181 ne_ssl_certificate
*ca_cert
;
1183 ca_cert
= ne_ssl_cert_read(file
);
1184 if (ca_cert
== NULL
)
1186 return svn_error_createf
1187 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE
, NULL
,
1188 _("Invalid config: unable to load certificate file '%s'"),
1189 svn_path_local_style(file
, pool
));
1191 ne_ssl_trust_cert(sess
, ca_cert
);
1192 ne_ssl_trust_cert(sess2
, ca_cert
);
1196 /* When the CA certificate or server certificate has
1197 verification problems, neon will call our verify function before
1198 outright rejection of the connection.*/
1199 ne_ssl_set_verify(sess
, server_ssl_callback
, ras
);
1200 ne_ssl_set_verify(sess2
, server_ssl_callback
, ras
);
1201 /* For client connections, we register a callback for if the server
1202 wants to authenticate the client via client certificate. */
1204 #ifdef SVN_NEON_0_28
1205 if (pkcs11_provider
)
1207 ne_ssl_pkcs11_provider
*provider
;
1210 /* Initialize the PKCS#11 provider. */
1211 rv
= ne_ssl_pkcs11_provider_init(&provider
, pkcs11_provider
);
1212 if (rv
!= NE_PK11_OK
)
1214 return svn_error_createf
1215 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE
, NULL
,
1216 _("Invalid config: unable to load PKCS#11 provider '%s'"),
1220 /* Share the provider between the two sessions. */
1221 ne_ssl_set_pkcs11_provider(sess
, provider
);
1222 ne_ssl_set_pkcs11_provider(sess2
, provider
);
1224 ne_ssl_pkcs11_provider_pin(provider
, client_ssl_pkcs11_pin_entry
,
1227 apr_pool_cleanup_register(pool
, provider
, cleanup_p11provider
,
1228 apr_pool_cleanup_null
);
1230 /* Note the "else"; if a PKCS#11 provider is set up, a client
1231 cert callback is already configured, so don't displace it
1232 with the normal one here. */
1236 ne_ssl_provide_clicert(sess
, client_ssl_callback
, ras
);
1237 ne_ssl_provide_clicert(sess2
, client_ssl_callback
, ras
);
1240 /* See if the user wants us to trust "default" openssl CAs. */
1241 trust_default_ca
= svn_config_get_server_setting(
1243 SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA
,
1246 if (svn_cstring_casecmp(trust_default_ca
, "true") == 0)
1248 ne_ssl_trust_default_ca(sess
);
1249 ne_ssl_trust_default_ca(sess2
);
1252 neonprogress_baton
->pool
= pool
;
1253 neonprogress_baton
->progress_baton
= callbacks
->progress_baton
;
1254 neonprogress_baton
->progress_func
= callbacks
->progress_func
;
1255 ne_set_progress(sess
, ra_neon_neonprogress
, neonprogress_baton
);
1256 ne_set_progress(sess2
, ra_neon_neonprogress
, neonprogress_baton
);
1257 session
->priv
= ras
;
1259 SVN_ERR(exchange_capabilities(ras
, pool
));
1261 return SVN_NO_ERROR
;
1265 static svn_error_t
*svn_ra_neon__reparent(svn_ra_session_t
*session
,
1269 svn_ra_neon__session_t
*ras
= session
->priv
;
1270 ne_uri
*uri
= apr_pcalloc(session
->pool
, sizeof(*uri
));
1272 SVN_ERR(parse_url(uri
, url
));
1273 apr_pool_cleanup_register(session
->pool
, uri
, cleanup_uri
,
1274 apr_pool_cleanup_null
);
1277 svn_stringbuf_set(ras
->url
, url
);
1278 return SVN_NO_ERROR
;
1281 static svn_error_t
*svn_ra_neon__get_session_url(svn_ra_session_t
*session
,
1285 svn_ra_neon__session_t
*ras
= session
->priv
;
1286 *url
= apr_pstrmemdup(pool
, ras
->url
->data
, ras
->url
->len
);
1287 return SVN_NO_ERROR
;
1290 static svn_error_t
*svn_ra_neon__get_repos_root(svn_ra_session_t
*session
,
1294 svn_ra_neon__session_t
*ras
= session
->priv
;
1296 if (! ras
->repos_root
)
1298 svn_string_t bc_relative
;
1299 svn_stringbuf_t
*url_buf
;
1301 SVN_ERR(svn_ra_neon__get_baseline_info(NULL
, NULL
, &bc_relative
,
1302 NULL
, ras
, ras
->url
->data
,
1303 SVN_INVALID_REVNUM
, pool
));
1305 /* Remove as many path components from the URL as there are components
1307 url_buf
= svn_stringbuf_dup(ras
->url
, pool
);
1308 svn_path_remove_components
1309 (url_buf
, svn_path_component_count(bc_relative
.data
));
1310 ras
->repos_root
= apr_pstrdup(ras
->pool
, url_buf
->data
);
1313 *url
= ras
->repos_root
;
1314 return SVN_NO_ERROR
;
1318 static svn_error_t
*svn_ra_neon__do_get_uuid(svn_ra_session_t
*session
,
1322 svn_ra_neon__session_t
*ras
= session
->priv
;
1326 svn_ra_neon__resource_t
*rsrc
;
1327 const char *lopped_path
;
1329 SVN_ERR(svn_ra_neon__search_for_starting_props(&rsrc
, &lopped_path
,
1330 ras
, ras
->url
->data
,
1332 SVN_ERR(svn_ra_neon__maybe_store_auth_info(ras
, pool
));
1336 /* ### better error reporting... */
1337 return svn_error_create(APR_EGENERAL
, NULL
,
1338 _("The UUID property was not found on the "
1339 "resource or any of its parents"));
1343 /* search_for_starting_props() filled it. */
1346 return SVN_NO_ERROR
;
1350 static const svn_version_t
*
1351 ra_neon_version(void)
1356 static const svn_ra__vtable_t neon_vtable
= {
1358 ra_neon_get_description
,
1359 ra_neon_get_schemes
,
1361 svn_ra_neon__reparent
,
1362 svn_ra_neon__get_session_url
,
1363 svn_ra_neon__get_latest_revnum
,
1364 svn_ra_neon__get_dated_revision
,
1365 svn_ra_neon__change_rev_prop
,
1366 svn_ra_neon__rev_proplist
,
1367 svn_ra_neon__rev_prop
,
1368 svn_ra_neon__get_commit_editor
,
1369 svn_ra_neon__get_file
,
1370 svn_ra_neon__get_dir
,
1371 svn_ra_neon__get_mergeinfo
,
1372 svn_ra_neon__do_update
,
1373 svn_ra_neon__do_switch
,
1374 svn_ra_neon__do_status
,
1375 svn_ra_neon__do_diff
,
1376 svn_ra_neon__get_log
,
1377 svn_ra_neon__do_check_path
,
1378 svn_ra_neon__do_stat
,
1379 svn_ra_neon__do_get_uuid
,
1380 svn_ra_neon__get_repos_root
,
1381 svn_ra_neon__get_locations
,
1382 svn_ra_neon__get_location_segments
,
1383 svn_ra_neon__get_file_revs
,
1385 svn_ra_neon__unlock
,
1386 svn_ra_neon__get_lock
,
1387 svn_ra_neon__get_locks
,
1388 svn_ra_neon__replay
,
1389 svn_ra_neon__has_capability
,
1390 svn_ra_neon__replay_range
1394 svn_ra_neon__init(const svn_version_t
*loader_version
,
1395 const svn_ra__vtable_t
**vtable
,
1398 static const svn_version_checklist_t checklist
[] =
1400 { "svn_subr", svn_subr_version
},
1401 { "svn_delta", svn_delta_version
},
1405 SVN_ERR(svn_ver_check_list(ra_neon_version(), checklist
));
1407 /* Simplified version check to make sure we can safely use the
1408 VTABLE parameter. The RA loader does a more exhaustive check. */
1409 if (loader_version
->major
!= SVN_VER_MAJOR
)
1411 return svn_error_createf
1412 (SVN_ERR_VERSION_MISMATCH
, NULL
,
1413 _("Unsupported RA loader version (%d) for ra_neon"),
1414 loader_version
->major
);
1417 *vtable
= &neon_vtable
;
1419 return SVN_NO_ERROR
;
1422 /* Compatibility wrapper for the 1.1 and before API. */
1423 #define NAME "ra_neon"
1424 #define DESCRIPTION RA_NEON_DESCRIPTION
1425 #define VTBL neon_vtable
1426 #define INITFUNC svn_ra_neon__init
1427 #define COMPAT_INITFUNC svn_ra_dav_init
1428 #include "../libsvn_ra/wrapper_template.h"