diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_subr/cmdline.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_subr/cmdline.c')
-rw-r--r-- | subversion/libsvn_subr/cmdline.c | 435 |
1 files changed, 356 insertions, 79 deletions
diff --git a/subversion/libsvn_subr/cmdline.c b/subversion/libsvn_subr/cmdline.c index 89d1ff3..9e97bf7 100644 --- a/subversion/libsvn_subr/cmdline.c +++ b/subversion/libsvn_subr/cmdline.c @@ -34,6 +34,7 @@ #else #include <crtdbg.h> #include <io.h> +#include <conio.h> #endif #include <apr.h> /* for STDIN_FILENO */ @@ -62,17 +63,32 @@ #include "private/svn_cmdline_private.h" #include "private/svn_utf_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_string_private.h" #include "svn_private_config.h" #include "win32_crashrpt.h" +#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400) +/* Before Visual Studio 2005, the C runtime didn't handle encodings for the + for the stdio output handling. */ +#define CMDLINE_USE_CUSTOM_ENCODING + /* The stdin encoding. If null, it's the same as the native encoding. */ static const char *input_encoding = NULL; /* The stdout encoding. If null, it's the same as the native encoding. */ static const char *output_encoding = NULL; +#elif defined(WIN32) && defined(_MSC_VER) +/* For now limit this code to Visual C++, as the result is highly dependent + on the CRT implementation */ +#define USE_WIN32_CONSOLE_SHORTCUT + +/* When TRUE, stdout/stderr is directly connected to a console */ +static svn_boolean_t shortcut_stdout_to_console = FALSE; +static svn_boolean_t shortcut_stderr_to_console = FALSE; +#endif int @@ -114,7 +130,7 @@ svn_cmdline_init(const char *progname, FILE *error_stream) #endif #ifdef WIN32 -#if _MSC_VER < 1400 +#ifdef CMDLINE_USE_CUSTOM_ENCODING /* Initialize the input and output encodings. */ { static char input_encoding_buffer[16]; @@ -128,33 +144,35 @@ svn_cmdline_init(const char *progname, FILE *error_stream) "CP%u", (unsigned) GetConsoleOutputCP()); output_encoding = output_encoding_buffer; } -#endif /* _MSC_VER < 1400 */ +#endif /* CMDLINE_USE_CUSTOM_ENCODING */ #ifdef SVN_USE_WIN32_CRASHHANDLER - /* Attach (but don't load) the crash handler */ - SetUnhandledExceptionFilter(svn__unhandled_exception_filter); + if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER")) + { + /* Attach (but don't load) the crash handler */ + SetUnhandledExceptionFilter(svn__unhandled_exception_filter); #if _MSC_VER >= 1400 - /* ### This should work for VC++ 2002 (=1300) and later */ - /* Show the abort message on STDERR instead of a dialog to allow - scripts (e.g. our testsuite) to continue after an abort without - user intervention. Allow overriding for easier debugging. */ - if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) - { - /* In release mode: Redirect abort() errors to stderr */ - _set_error_mode(_OUT_TO_STDERR); - - /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. - (Ignored in release builds) */ - _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - } + /* ### This should work for VC++ 2002 (=1300) and later */ + /* Show the abort message on STDERR instead of a dialog to allow + scripts (e.g. our testsuite) to continue after an abort without + user intervention. Allow overriding for easier debugging. */ + if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) + { + /* In release mode: Redirect abort() errors to stderr */ + _set_error_mode(_OUT_TO_STDERR); + + /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. + (Ignored in release builds) */ + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + } #endif /* _MSC_VER >= 1400 */ - + } #endif /* SVN_USE_WIN32_CRASHHANDLER */ #endif /* WIN32 */ @@ -247,6 +265,31 @@ svn_cmdline_init(const char *progname, FILE *error_stream) return EXIT_FAILURE; } +#ifdef USE_WIN32_CONSOLE_SHORTCUT + if (_isatty(STDOUT_FILENO)) + { + DWORD ignored; + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + /* stdout is a char device handle, but is it the console? */ + if (GetConsoleMode(stdout_handle, &ignored)) + shortcut_stdout_to_console = TRUE; + + /* Don't close stdout_handle */ + } + if (_isatty(STDERR_FILENO)) + { + DWORD ignored; + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + /* stderr is a char device handle, but is it the console? */ + if (GetConsoleMode(stderr_handle, &ignored)) + shortcut_stderr_to_console = TRUE; + + /* Don't close stderr_handle */ + } +#endif + return EXIT_SUCCESS; } @@ -256,10 +299,12 @@ svn_cmdline_cstring_from_utf8(const char **dest, const char *src, apr_pool_t *pool) { - if (output_encoding == NULL) - return svn_utf_cstring_from_utf8(dest, src, pool); - else +#ifdef CMDLINE_USE_CUSTOM_ENCODING + if (output_encoding != NULL) return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool); +#endif + + return svn_utf_cstring_from_utf8(dest, src, pool); } @@ -277,10 +322,12 @@ svn_cmdline_cstring_to_utf8(const char **dest, const char *src, apr_pool_t *pool) { - if (input_encoding == NULL) - return svn_utf_cstring_to_utf8(dest, src, pool); - else +#ifdef CMDLINE_USE_CUSTOM_ENCODING + if (input_encoding != NULL) return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool); +#endif + + return svn_utf_cstring_to_utf8(dest, src, pool); } @@ -336,6 +383,45 @@ svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool) svn_error_t *err; const char *out; +#ifdef USE_WIN32_CONSOLE_SHORTCUT + /* For legacy reasons the Visual C++ runtime converts output to the console + from the native 'ansi' encoding, to unicode, then back to 'ansi' and then + onwards to the console which is implemented as unicode. + + For operations like 'svn status -v' this may cause about 70% of the total + processing time, with absolutely no gain. + + For this specific scenario this shortcut exists. It has the nice side + effect of allowing full unicode output to the console. + + Note that this shortcut is not used when the output is redirected, as in + that case the data is put on the pipe/file after the first conversion to + ansi. In this case the most expensive conversion is already avoided. + */ + if ((stream == stdout && shortcut_stdout_to_console) + || (stream == stderr && shortcut_stderr_to_console)) + { + WCHAR *result; + + if (string[0] == '\0') + return SVN_NO_ERROR; + + SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */ + + SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool)); + + if (_cputws(result)) + { + if (apr_get_os_error()) + { + return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); + } + } + + return SVN_NO_ERROR; + } +#endif + err = svn_cmdline_cstring_from_utf8(&out, string, pool); if (err) @@ -393,10 +479,12 @@ svn_cmdline_fflush(FILE *stream) const char *svn_cmdline_output_encoding(apr_pool_t *pool) { +#ifdef CMDLINE_USE_CUSTOM_ENCODING if (output_encoding) return apr_pstrdup(pool, output_encoding); - else - return SVN_APR_LOCALE_CHARSET; +#endif + + return SVN_APR_LOCALE_CHARSET; } int @@ -419,31 +507,51 @@ svn_cmdline_handle_exit_error(svn_error_t *err, return EXIT_FAILURE; } +struct trust_server_cert_non_interactive_baton { + svn_boolean_t trust_server_cert_unknown_ca; + svn_boolean_t trust_server_cert_cn_mismatch; + svn_boolean_t trust_server_cert_expired; + svn_boolean_t trust_server_cert_not_yet_valid; + svn_boolean_t trust_server_cert_other_failure; +}; + /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. Don't actually prompt. Instead, set *CRED_P to valid credentials - iff FAILURES is empty or is exactly SVN_AUTH_SSL_UNKNOWNCA. If - there are any other failure bits, then set *CRED_P to null (that - is, reject the cert). + iff FAILURES is empty or may be accepted according to the flags + in BATON. If there are any other failure bits, then set *CRED_P + to null (that is, reject the cert). Ignore MAY_SAVE; we don't save certs we never prompted for. - Ignore BATON, REALM, and CERT_INFO, + Ignore REALM and CERT_INFO, Ignore any further films by George Lucas. */ static svn_error_t * -ssl_trust_unknown_server_cert - (svn_auth_cred_ssl_server_trust_t **cred_p, - void *baton, - const char *realm, - apr_uint32_t failures, - const svn_auth_ssl_server_cert_info_t *cert_info, - svn_boolean_t may_save, - apr_pool_t *pool) +trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p, + void *baton, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t + *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) { + struct trust_server_cert_non_interactive_baton *b = baton; + apr_uint32_t non_ignored_failures; *cred_p = NULL; - if (failures == 0 || failures == SVN_AUTH_SSL_UNKNOWNCA) + /* Mask away bits we are instructed to ignore. */ + non_ignored_failures = failures & ~( + (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0) + | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0) + | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0) + | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0) + | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0) + ); + + /* If no failures remain, accept the certificate. */ + if (non_ignored_failures == 0) { *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); (*cred_p)->may_save = FALSE; @@ -454,17 +562,22 @@ ssl_trust_unknown_server_cert } svn_error_t * -svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, - svn_boolean_t non_interactive, - const char *auth_username, - const char *auth_password, - const char *config_dir, - svn_boolean_t no_auth_cache, - svn_boolean_t trust_server_cert, - svn_config_t *cfg, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *pool) +svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, + svn_boolean_t non_interactive, + const char *auth_username, + const char *auth_password, + const char *config_dir, + svn_boolean_t no_auth_cache, + svn_boolean_t trust_server_cert_unknown_ca, + svn_boolean_t trust_server_cert_cn_mismatch, + svn_boolean_t trust_server_cert_expired, + svn_boolean_t trust_server_cert_not_yet_valid, + svn_boolean_t trust_server_cert_other_failure, + svn_config_t *cfg, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) + { svn_boolean_t store_password_val = TRUE; svn_boolean_t store_auth_creds_val = TRUE; @@ -505,24 +618,6 @@ svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, svn_auth_get_username_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - /* The windows ssl server certificate CRYPTOAPI provider. */ - SVN_ERR(svn_auth_get_platform_specific_provider(&provider, - "windows", - "ssl_server_trust", - pool)); - - if (provider) - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* The windows ssl authority certificate CRYPTOAPI provider. */ - SVN_ERR(svn_auth_get_platform_specific_provider(&provider, - "windows", - "ssl_server_authority", - pool)); - - if (provider) - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_ssl_server_trust_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_file_provider(&provider, pool); @@ -583,11 +678,22 @@ svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } } - else if (trust_server_cert) + else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch || + trust_server_cert_expired || trust_server_cert_not_yet_valid || + trust_server_cert_other_failure) { + struct trust_server_cert_non_interactive_baton *b; + + b = apr_palloc(pool, sizeof(*b)); + b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca; + b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch; + b->trust_server_cert_expired = trust_server_cert_expired; + b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid; + b->trust_server_cert_other_failure = trust_server_cert_other_failure; + /* Remember, only register this provider if non_interactive. */ svn_auth_get_ssl_server_trust_prompt_provider - (&provider, ssl_trust_unknown_server_cert, NULL, pool); + (&provider, trust_server_cert_non_interactive, b, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } @@ -689,12 +795,12 @@ svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, outstr, pool, svn_xml_protect_pcdata, inherited_prop ? "inherited_property" : "property", "name", propname, - "encoding", encoding, NULL); + "encoding", encoding, SVN_VA_NULL); else svn_xml_make_open_tag( outstr, pool, svn_xml_protect_pcdata, inherited_prop ? "inherited_property" : "property", - "name", propname, NULL); + "name", propname, SVN_VA_NULL); svn_stringbuf_appendcstr(*outstr, xml_safe); @@ -705,9 +811,124 @@ svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, return; } +/* Return the most similar string to NEEDLE in HAYSTACK, which contains + * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar. + */ +/* See svn_cl__similarity_check() for a more general solution. */ +static const char * +most_similar(const char *needle_cstr, + const char **haystack, + apr_size_t haystack_len, + apr_pool_t *scratch_pool) +{ + const char *max_similar; + apr_size_t max_score = 0; + apr_size_t i; + svn_membuf_t membuf; + svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool); + + svn_membuf__create(&membuf, 64, scratch_pool); + + for (i = 0; i < haystack_len; i++) + { + apr_size_t score; + svn_string_t *hay = svn_string_create(haystack[i], scratch_pool); + + score = svn_string__similarity(needle_str, hay, &membuf, NULL); + + /* If you update this factor, consider updating + * svn_cl__similarity_check(). */ + if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3 + && score > max_score) + { + max_score = score; + max_similar = haystack[i]; + } + } + + if (max_score) + return max_similar; + else + return NULL; +} + +/* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */ +static svn_error_t * +string_in_array(const char *needle, + const char **haystack, + apr_size_t haystack_len, + apr_pool_t *scratch_pool) +{ + const char *next_of_kin; + apr_size_t i; + for (i = 0; i < haystack_len; i++) + { + if (!strcmp(needle, haystack[i])) + return SVN_NO_ERROR; + } + + /* Error. */ + next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool); + if (next_of_kin) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Ignoring unknown value '%s'; " + "did you mean '%s'?"), + needle, next_of_kin); + else + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Ignoring unknown value '%s'"), + needle); +} + +#include "config_keys.inc" + +/* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are + * known. Warn to stderr if not. (An unknown value may be either a typo + * or added in a newer minor version of Subversion.) */ +static svn_error_t * +validate_config_option(svn_cmdline__config_argument_t *config_option, + apr_pool_t *scratch_pool) +{ + svn_boolean_t arbitrary_keys = FALSE; + + /* TODO: some day, we could also verify that OPTION is valid for SECTION; + i.e., forbid invalid combinations such as config:auth:diff-extensions. */ + +#define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) ) + + SVN_ERR(string_in_array(config_option->file, svn__valid_config_files, + ARRAYLEN(svn__valid_config_files), + scratch_pool)); + SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections, + ARRAYLEN(svn__valid_config_sections), + scratch_pool)); + + /* Don't validate option names for sections such as servers[group], + * config[tunnels], and config[auto-props] that permit arbitrary options. */ + { + int i; + + for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++) + { + if (!strcmp(config_option->section, svn__empty_config_sections[i])) + arbitrary_keys = TRUE; + } + } + + if (! arbitrary_keys) + SVN_ERR(string_in_array(config_option->option, svn__valid_config_options, + ARRAYLEN(svn__valid_config_options), + scratch_pool)); + +#undef ARRAYLEN + + return SVN_NO_ERROR; +} + svn_error_t * svn_cmdline__parse_config_option(apr_array_header_t *config_options, const char *opt_arg, + const char *prefix, apr_pool_t *pool) { svn_cmdline__config_argument_t *config_option; @@ -721,6 +942,8 @@ svn_cmdline__parse_config_option(apr_array_header_t *config_options, if ((equals_sign = strchr(second_colon + 1, '=')) && (equals_sign != second_colon + 1)) { + svn_error_t *warning; + config_option = apr_pcalloc(pool, sizeof(*config_option)); config_option->file = apr_pstrndup(pool, opt_arg, first_colon - opt_arg); @@ -729,6 +952,13 @@ svn_cmdline__parse_config_option(apr_array_header_t *config_options, config_option->option = apr_pstrndup(pool, second_colon + 1, equals_sign - second_colon - 1); + warning = validate_config_option(config_option, pool); + if (warning) + { + svn_handle_warning2(stderr, warning, prefix); + svn_error_clear(warning); + } + if (! (strchr(config_option->option, ':'))) { config_option->value = apr_pstrndup(pool, equals_sign + 1, @@ -920,7 +1150,7 @@ svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr, svn_xml_make_open_tag( outstr, pool, svn_xml_self_closing, inherited_props ? "inherited_property" : "property", - "name", pname, NULL); + "name", pname, SVN_VA_NULL); } else { @@ -1275,7 +1505,7 @@ svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, /* Translate back to UTF8/LF if desired. */ if (as_text) { - err = svn_subst_translate_string2(edited_contents, FALSE, FALSE, + err = svn_subst_translate_string2(edited_contents, NULL, NULL, *edited_contents, encoding, FALSE, pool, pool); if (err) @@ -1319,3 +1549,50 @@ svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, return svn_error_trace(err); } + +svn_error_t * +svn_cmdline__parse_trust_options( + svn_boolean_t *trust_server_cert_unknown_ca, + svn_boolean_t *trust_server_cert_cn_mismatch, + svn_boolean_t *trust_server_cert_expired, + svn_boolean_t *trust_server_cert_not_yet_valid, + svn_boolean_t *trust_server_cert_other_failure, + const char *opt_arg, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *failures; + int i; + + *trust_server_cert_unknown_ca = FALSE; + *trust_server_cert_cn_mismatch = FALSE; + *trust_server_cert_expired = FALSE; + *trust_server_cert_not_yet_valid = FALSE; + *trust_server_cert_other_failure = FALSE; + + failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool); + + for (i = 0; i < failures->nelts; i++) + { + const char *value = APR_ARRAY_IDX(failures, i, const char *); + if (!strcmp(value, "unknown-ca")) + *trust_server_cert_unknown_ca = TRUE; + else if (!strcmp(value, "cn-mismatch")) + *trust_server_cert_cn_mismatch = TRUE; + else if (!strcmp(value, "expired")) + *trust_server_cert_expired = TRUE; + else if (!strcmp(value, "not-yet-valid")) + *trust_server_cert_not_yet_valid = TRUE; + else if (!strcmp(value, "other")) + *trust_server_cert_other_failure = TRUE; + else + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Unknown value '%s' for %s.\n" + "Supported values: %s"), + value, + "--trust-server-cert-failures", + "unknown-ca, cn-mismatch, expired, " + "not-yet-valid, other"); + } + + return SVN_NO_ERROR; +} |