cmdline.c revision 299742
1/* 2 * cmdline.c : Helpers for command-line programs. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25#include <stdlib.h> /* for atexit() */ 26#include <stdio.h> /* for setvbuf() */ 27#include <locale.h> /* for setlocale() */ 28 29#ifndef WIN32 30#include <sys/types.h> 31#include <sys/stat.h> 32#include <fcntl.h> 33#include <unistd.h> 34#else 35#include <crtdbg.h> 36#include <io.h> 37#include <conio.h> 38#endif 39 40#include <apr.h> /* for STDIN_FILENO */ 41#include <apr_errno.h> /* for apr_strerror */ 42#include <apr_general.h> /* for apr_initialize/apr_terminate */ 43#include <apr_strings.h> /* for apr_snprintf */ 44#include <apr_pools.h> 45 46#include "svn_cmdline.h" 47#include "svn_ctype.h" 48#include "svn_dso.h" 49#include "svn_dirent_uri.h" 50#include "svn_hash.h" 51#include "svn_path.h" 52#include "svn_pools.h" 53#include "svn_error.h" 54#include "svn_nls.h" 55#include "svn_utf.h" 56#include "svn_auth.h" 57#include "svn_xml.h" 58#include "svn_base64.h" 59#include "svn_config.h" 60#include "svn_sorts.h" 61#include "svn_props.h" 62#include "svn_subst.h" 63 64#include "private/svn_cmdline_private.h" 65#include "private/svn_utf_private.h" 66#include "private/svn_sorts_private.h" 67#include "private/svn_string_private.h" 68 69#include "svn_private_config.h" 70 71#include "win32_crashrpt.h" 72 73#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400) 74/* Before Visual Studio 2005, the C runtime didn't handle encodings for the 75 for the stdio output handling. */ 76#define CMDLINE_USE_CUSTOM_ENCODING 77 78/* The stdin encoding. If null, it's the same as the native encoding. */ 79static const char *input_encoding = NULL; 80 81/* The stdout encoding. If null, it's the same as the native encoding. */ 82static const char *output_encoding = NULL; 83#elif defined(WIN32) && defined(_MSC_VER) 84/* For now limit this code to Visual C++, as the result is highly dependent 85 on the CRT implementation */ 86#define USE_WIN32_CONSOLE_SHORTCUT 87 88/* When TRUE, stdout/stderr is directly connected to a console */ 89static svn_boolean_t shortcut_stdout_to_console = FALSE; 90static svn_boolean_t shortcut_stderr_to_console = FALSE; 91#endif 92 93 94int 95svn_cmdline_init(const char *progname, FILE *error_stream) 96{ 97 apr_status_t status; 98 apr_pool_t *pool; 99 svn_error_t *err; 100 char prefix_buf[64]; /* 64 is probably bigger than most program names */ 101 102#ifndef WIN32 103 { 104 struct stat st; 105 106 /* The following makes sure that file descriptors 0 (stdin), 1 107 (stdout) and 2 (stderr) will not be "reused", because if 108 e.g. file descriptor 2 would be reused when opening a file, a 109 write to stderr would write to that file and most likely 110 corrupt it. */ 111 if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) || 112 (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) || 113 (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1)) 114 { 115 if (error_stream) 116 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n", 117 progname); 118 return EXIT_FAILURE; 119 } 120 } 121#endif 122 123 /* Ignore any errors encountered while attempting to change stream 124 buffering, as the streams should retain their default buffering 125 modes. */ 126 if (error_stream) 127 setvbuf(error_stream, NULL, _IONBF, 0); 128#ifndef WIN32 129 setvbuf(stdout, NULL, _IOLBF, 0); 130#endif 131 132#ifdef WIN32 133#ifdef CMDLINE_USE_CUSTOM_ENCODING 134 /* Initialize the input and output encodings. */ 135 { 136 static char input_encoding_buffer[16]; 137 static char output_encoding_buffer[16]; 138 139 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer, 140 "CP%u", (unsigned) GetConsoleCP()); 141 input_encoding = input_encoding_buffer; 142 143 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer, 144 "CP%u", (unsigned) GetConsoleOutputCP()); 145 output_encoding = output_encoding_buffer; 146 } 147#endif /* CMDLINE_USE_CUSTOM_ENCODING */ 148 149#ifdef SVN_USE_WIN32_CRASHHANDLER 150 if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER")) 151 { 152 /* Attach (but don't load) the crash handler */ 153 SetUnhandledExceptionFilter(svn__unhandled_exception_filter); 154 155#if _MSC_VER >= 1400 156 /* ### This should work for VC++ 2002 (=1300) and later */ 157 /* Show the abort message on STDERR instead of a dialog to allow 158 scripts (e.g. our testsuite) to continue after an abort without 159 user intervention. Allow overriding for easier debugging. */ 160 if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) 161 { 162 /* In release mode: Redirect abort() errors to stderr */ 163 _set_error_mode(_OUT_TO_STDERR); 164 165 /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. 166 (Ignored in release builds) */ 167 _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); 168 _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); 169 _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); 170 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 171 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 172 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 173 } 174#endif /* _MSC_VER >= 1400 */ 175 } 176#endif /* SVN_USE_WIN32_CRASHHANDLER */ 177 178#endif /* WIN32 */ 179 180 /* C programs default to the "C" locale. But because svn is supposed 181 to be i18n-aware, it should inherit the default locale of its 182 environment. */ 183 if (!setlocale(LC_ALL, "") 184 && !setlocale(LC_CTYPE, "")) 185 { 186 if (error_stream) 187 { 188 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL }; 189 const char **env_var = &env_vars[0], *env_val = NULL; 190 while (*env_var) 191 { 192 env_val = getenv(*env_var); 193 if (env_val && env_val[0]) 194 break; 195 ++env_var; 196 } 197 198 if (!*env_var) 199 { 200 /* Unlikely. Can setlocale fail if no env vars are set? */ 201 --env_var; 202 env_val = "not set"; 203 } 204 205 fprintf(error_stream, 206 "%s: warning: cannot set LC_CTYPE locale\n" 207 "%s: warning: environment variable %s is %s\n" 208 "%s: warning: please check that your locale name is correct\n", 209 progname, progname, *env_var, env_val, progname); 210 } 211 } 212 213 /* Initialize the APR subsystem, and register an atexit() function 214 to Uninitialize that subsystem at program exit. */ 215 status = apr_initialize(); 216 if (status) 217 { 218 if (error_stream) 219 { 220 char buf[1024]; 221 apr_strerror(status, buf, sizeof(buf) - 1); 222 fprintf(error_stream, 223 "%s: error: cannot initialize APR: %s\n", 224 progname, buf); 225 } 226 return EXIT_FAILURE; 227 } 228 229 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3); 230 prefix_buf[sizeof(prefix_buf) - 3] = '\0'; 231 strcat(prefix_buf, ": "); 232 233 /* DSO pool must be created before any other pools used by the 234 application so that pool cleanup doesn't unload DSOs too 235 early. See docstring of svn_dso_initialize2(). */ 236 if ((err = svn_dso_initialize2())) 237 { 238 if (error_stream) 239 svn_handle_error2(err, error_stream, TRUE, prefix_buf); 240 241 svn_error_clear(err); 242 return EXIT_FAILURE; 243 } 244 245 if (0 > atexit(apr_terminate)) 246 { 247 if (error_stream) 248 fprintf(error_stream, 249 "%s: error: atexit registration failed\n", 250 progname); 251 return EXIT_FAILURE; 252 } 253 254 /* Create a pool for use by the UTF-8 routines. It will be cleaned 255 up by APR at exit time. */ 256 pool = svn_pool_create(NULL); 257 svn_utf_initialize2(FALSE, pool); 258 259 if ((err = svn_nls_init())) 260 { 261 if (error_stream) 262 svn_handle_error2(err, error_stream, TRUE, prefix_buf); 263 264 svn_error_clear(err); 265 return EXIT_FAILURE; 266 } 267 268#ifdef USE_WIN32_CONSOLE_SHORTCUT 269 if (_isatty(STDOUT_FILENO)) 270 { 271 DWORD ignored; 272 HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); 273 274 /* stdout is a char device handle, but is it the console? */ 275 if (GetConsoleMode(stdout_handle, &ignored)) 276 shortcut_stdout_to_console = TRUE; 277 278 /* Don't close stdout_handle */ 279 } 280 if (_isatty(STDERR_FILENO)) 281 { 282 DWORD ignored; 283 HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); 284 285 /* stderr is a char device handle, but is it the console? */ 286 if (GetConsoleMode(stderr_handle, &ignored)) 287 shortcut_stderr_to_console = TRUE; 288 289 /* Don't close stderr_handle */ 290 } 291#endif 292 293 return EXIT_SUCCESS; 294} 295 296 297svn_error_t * 298svn_cmdline_cstring_from_utf8(const char **dest, 299 const char *src, 300 apr_pool_t *pool) 301{ 302#ifdef CMDLINE_USE_CUSTOM_ENCODING 303 if (output_encoding != NULL) 304 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool); 305#endif 306 307 return svn_utf_cstring_from_utf8(dest, src, pool); 308} 309 310 311const char * 312svn_cmdline_cstring_from_utf8_fuzzy(const char *src, 313 apr_pool_t *pool) 314{ 315 return svn_utf__cstring_from_utf8_fuzzy(src, pool, 316 svn_cmdline_cstring_from_utf8); 317} 318 319 320svn_error_t * 321svn_cmdline_cstring_to_utf8(const char **dest, 322 const char *src, 323 apr_pool_t *pool) 324{ 325#ifdef CMDLINE_USE_CUSTOM_ENCODING 326 if (input_encoding != NULL) 327 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool); 328#endif 329 330 return svn_utf_cstring_to_utf8(dest, src, pool); 331} 332 333 334svn_error_t * 335svn_cmdline_path_local_style_from_utf8(const char **dest, 336 const char *src, 337 apr_pool_t *pool) 338{ 339 return svn_cmdline_cstring_from_utf8(dest, 340 svn_dirent_local_style(src, pool), 341 pool); 342} 343 344svn_error_t * 345svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...) 346{ 347 const char *message; 348 va_list ap; 349 350 /* A note about encoding issues: 351 * APR uses the execution character set, but here we give it UTF-8 strings, 352 * both the fmt argument and any other string arguments. Since apr_pvsprintf 353 * only cares about and produces ASCII characters, this works under the 354 * assumption that all supported platforms use an execution character set 355 * with ASCII as a subset. 356 */ 357 358 va_start(ap, fmt); 359 message = apr_pvsprintf(pool, fmt, ap); 360 va_end(ap); 361 362 return svn_cmdline_fputs(message, stdout, pool); 363} 364 365svn_error_t * 366svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...) 367{ 368 const char *message; 369 va_list ap; 370 371 /* See svn_cmdline_printf () for a note about character encoding issues. */ 372 373 va_start(ap, fmt); 374 message = apr_pvsprintf(pool, fmt, ap); 375 va_end(ap); 376 377 return svn_cmdline_fputs(message, stream, pool); 378} 379 380svn_error_t * 381svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool) 382{ 383 svn_error_t *err; 384 const char *out; 385 386#ifdef USE_WIN32_CONSOLE_SHORTCUT 387 /* For legacy reasons the Visual C++ runtime converts output to the console 388 from the native 'ansi' encoding, to unicode, then back to 'ansi' and then 389 onwards to the console which is implemented as unicode. 390 391 For operations like 'svn status -v' this may cause about 70% of the total 392 processing time, with absolutely no gain. 393 394 For this specific scenario this shortcut exists. It has the nice side 395 effect of allowing full unicode output to the console. 396 397 Note that this shortcut is not used when the output is redirected, as in 398 that case the data is put on the pipe/file after the first conversion to 399 ansi. In this case the most expensive conversion is already avoided. 400 */ 401 if ((stream == stdout && shortcut_stdout_to_console) 402 || (stream == stderr && shortcut_stderr_to_console)) 403 { 404 WCHAR *result; 405 406 if (string[0] == '\0') 407 return SVN_NO_ERROR; 408 409 SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */ 410 411 SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool)); 412 413 if (_cputws(result)) 414 { 415 if (apr_get_os_error()) 416 { 417 return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); 418 } 419 } 420 421 return SVN_NO_ERROR; 422 } 423#endif 424 425 err = svn_cmdline_cstring_from_utf8(&out, string, pool); 426 427 if (err) 428 { 429 svn_error_clear(err); 430 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); 431 } 432 433 /* On POSIX systems, errno will be set on an error in fputs, but this might 434 not be the case on other platforms. We reset errno and only 435 use it if it was set by the below fputs call. Else, we just return 436 a generic error. */ 437 errno = 0; 438 439 if (fputs(out, stream) == EOF) 440 { 441 if (apr_get_os_error()) /* is errno on POSIX */ 442 { 443 /* ### Issue #3014: Return a specific error for broken pipes, 444 * ### with a single element in the error chain. */ 445 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error())) 446 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); 447 else 448 return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); 449 } 450 else 451 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); 452 } 453 454 return SVN_NO_ERROR; 455} 456 457svn_error_t * 458svn_cmdline_fflush(FILE *stream) 459{ 460 /* See comment in svn_cmdline_fputs about use of errno and stdio. */ 461 errno = 0; 462 if (fflush(stream) == EOF) 463 { 464 if (apr_get_os_error()) /* is errno on POSIX */ 465 { 466 /* ### Issue #3014: Return a specific error for broken pipes, 467 * ### with a single element in the error chain. */ 468 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error())) 469 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); 470 else 471 return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); 472 } 473 else 474 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); 475 } 476 477 return SVN_NO_ERROR; 478} 479 480const char *svn_cmdline_output_encoding(apr_pool_t *pool) 481{ 482#ifdef CMDLINE_USE_CUSTOM_ENCODING 483 if (output_encoding) 484 return apr_pstrdup(pool, output_encoding); 485#endif 486 487 return SVN_APR_LOCALE_CHARSET; 488} 489 490int 491svn_cmdline_handle_exit_error(svn_error_t *err, 492 apr_pool_t *pool, 493 const char *prefix) 494{ 495 /* Issue #3014: 496 * Don't print anything on broken pipes. The pipe was likely 497 * closed by the process at the other end. We expect that 498 * process to perform error reporting as necessary. 499 * 500 * ### This assumes that there is only one error in a chain for 501 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */ 502 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR) 503 svn_handle_error2(err, stderr, FALSE, prefix); 504 svn_error_clear(err); 505 if (pool) 506 svn_pool_destroy(pool); 507 return EXIT_FAILURE; 508} 509 510struct trust_server_cert_non_interactive_baton { 511 svn_boolean_t trust_server_cert_unknown_ca; 512 svn_boolean_t trust_server_cert_cn_mismatch; 513 svn_boolean_t trust_server_cert_expired; 514 svn_boolean_t trust_server_cert_not_yet_valid; 515 svn_boolean_t trust_server_cert_other_failure; 516}; 517 518/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. 519 520 Don't actually prompt. Instead, set *CRED_P to valid credentials 521 iff FAILURES is empty or may be accepted according to the flags 522 in BATON. If there are any other failure bits, then set *CRED_P 523 to null (that is, reject the cert). 524 525 Ignore MAY_SAVE; we don't save certs we never prompted for. 526 527 Ignore REALM and CERT_INFO, 528 529 Ignore any further films by George Lucas. */ 530static svn_error_t * 531trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p, 532 void *baton, 533 const char *realm, 534 apr_uint32_t failures, 535 const svn_auth_ssl_server_cert_info_t 536 *cert_info, 537 svn_boolean_t may_save, 538 apr_pool_t *pool) 539{ 540 struct trust_server_cert_non_interactive_baton *b = baton; 541 apr_uint32_t non_ignored_failures; 542 *cred_p = NULL; 543 544 /* Mask away bits we are instructed to ignore. */ 545 non_ignored_failures = failures & ~( 546 (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0) 547 | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0) 548 | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0) 549 | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0) 550 | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0) 551 ); 552 553 /* If no failures remain, accept the certificate. */ 554 if (non_ignored_failures == 0) 555 { 556 *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 557 (*cred_p)->may_save = FALSE; 558 (*cred_p)->accepted_failures = failures; 559 } 560 561 return SVN_NO_ERROR; 562} 563 564svn_error_t * 565svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, 566 svn_boolean_t non_interactive, 567 const char *auth_username, 568 const char *auth_password, 569 const char *config_dir, 570 svn_boolean_t no_auth_cache, 571 svn_boolean_t trust_server_cert_unknown_ca, 572 svn_boolean_t trust_server_cert_cn_mismatch, 573 svn_boolean_t trust_server_cert_expired, 574 svn_boolean_t trust_server_cert_not_yet_valid, 575 svn_boolean_t trust_server_cert_other_failure, 576 svn_config_t *cfg, 577 svn_cancel_func_t cancel_func, 578 void *cancel_baton, 579 apr_pool_t *pool) 580 581{ 582 svn_boolean_t store_password_val = TRUE; 583 svn_boolean_t store_auth_creds_val = TRUE; 584 svn_auth_provider_object_t *provider; 585 svn_cmdline_prompt_baton2_t *pb = NULL; 586 587 /* The whole list of registered providers */ 588 apr_array_header_t *providers; 589 590 /* Populate the registered providers with the platform-specific providers */ 591 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers, 592 cfg, pool)); 593 594 /* If we have a cancellation function, cram it and the stuff it 595 needs into the prompt baton. */ 596 if (cancel_func) 597 { 598 pb = apr_palloc(pool, sizeof(*pb)); 599 pb->cancel_func = cancel_func; 600 pb->cancel_baton = cancel_baton; 601 pb->config_dir = config_dir; 602 } 603 604 if (!non_interactive) 605 { 606 /* This provider doesn't prompt the user in order to get creds; 607 it prompts the user regarding the caching of creds. */ 608 svn_auth_get_simple_provider2(&provider, 609 svn_cmdline_auth_plaintext_prompt, 610 pb, pool); 611 } 612 else 613 { 614 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool); 615 } 616 617 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 618 svn_auth_get_username_provider(&provider, pool); 619 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 620 621 svn_auth_get_ssl_server_trust_file_provider(&provider, pool); 622 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 623 svn_auth_get_ssl_client_cert_file_provider(&provider, pool); 624 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 625 626 if (!non_interactive) 627 { 628 /* This provider doesn't prompt the user in order to get creds; 629 it prompts the user regarding the caching of creds. */ 630 svn_auth_get_ssl_client_cert_pw_file_provider2 631 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt, 632 pb, pool); 633 } 634 else 635 { 636 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL, 637 pool); 638 } 639 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 640 641 if (!non_interactive) 642 { 643 svn_boolean_t ssl_client_cert_file_prompt; 644 645 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt, 646 SVN_CONFIG_SECTION_AUTH, 647 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT, 648 FALSE)); 649 650 /* Two basic prompt providers: username/password, and just username. */ 651 svn_auth_get_simple_prompt_provider(&provider, 652 svn_cmdline_auth_simple_prompt, 653 pb, 654 2, /* retry limit */ 655 pool); 656 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 657 658 svn_auth_get_username_prompt_provider 659 (&provider, svn_cmdline_auth_username_prompt, pb, 660 2, /* retry limit */ pool); 661 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 662 663 /* SSL prompt providers: server-certs and client-cert-passphrases. */ 664 svn_auth_get_ssl_server_trust_prompt_provider 665 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool); 666 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 667 668 svn_auth_get_ssl_client_cert_pw_prompt_provider 669 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool); 670 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 671 672 /* If configuration allows, add a provider for client-cert path 673 prompting, too. */ 674 if (ssl_client_cert_file_prompt) 675 { 676 svn_auth_get_ssl_client_cert_prompt_provider 677 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool); 678 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 679 } 680 } 681 else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch || 682 trust_server_cert_expired || trust_server_cert_not_yet_valid || 683 trust_server_cert_other_failure) 684 { 685 struct trust_server_cert_non_interactive_baton *b; 686 687 b = apr_palloc(pool, sizeof(*b)); 688 b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca; 689 b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch; 690 b->trust_server_cert_expired = trust_server_cert_expired; 691 b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid; 692 b->trust_server_cert_other_failure = trust_server_cert_other_failure; 693 694 /* Remember, only register this provider if non_interactive. */ 695 svn_auth_get_ssl_server_trust_prompt_provider 696 (&provider, trust_server_cert_non_interactive, b, pool); 697 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; 698 } 699 700 /* Build an authentication baton to give to libsvn_client. */ 701 svn_auth_open(ab, providers, pool); 702 703 /* Place any default --username or --password credentials into the 704 auth_baton's run-time parameter hash. */ 705 if (auth_username) 706 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, 707 auth_username); 708 if (auth_password) 709 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, 710 auth_password); 711 712 /* Same with the --non-interactive option. */ 713 if (non_interactive) 714 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, ""); 715 716 if (config_dir) 717 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR, 718 config_dir); 719 720 /* Determine whether storing passwords in any form is allowed. 721 * This is the deprecated location for this option, the new 722 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may 723 * override the value we set here. */ 724 SVN_ERR(svn_config_get_bool(cfg, &store_password_val, 725 SVN_CONFIG_SECTION_AUTH, 726 SVN_CONFIG_OPTION_STORE_PASSWORDS, 727 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS)); 728 729 if (! store_password_val) 730 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); 731 732 /* Determine whether we are allowed to write to the auth/ area. 733 * This is the deprecated location for this option, the new 734 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may 735 * override the value we set here. */ 736 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val, 737 SVN_CONFIG_SECTION_AUTH, 738 SVN_CONFIG_OPTION_STORE_AUTH_CREDS, 739 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS)); 740 741 if (no_auth_cache || ! store_auth_creds_val) 742 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); 743 744#ifdef SVN_HAVE_GNOME_KEYRING 745 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC, 746 &svn_cmdline__auth_gnome_keyring_unlock_prompt); 747#endif /* SVN_HAVE_GNOME_KEYRING */ 748 749 return SVN_NO_ERROR; 750} 751 752svn_error_t * 753svn_cmdline__getopt_init(apr_getopt_t **os, 754 int argc, 755 const char *argv[], 756 apr_pool_t *pool) 757{ 758 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv); 759 if (apr_err) 760 return svn_error_wrap_apr(apr_err, 761 _("Error initializing command line arguments")); 762 return SVN_NO_ERROR; 763} 764 765 766void 767svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, 768 const char* propname, 769 svn_string_t *propval, 770 svn_boolean_t inherited_prop, 771 apr_pool_t *pool) 772{ 773 const char *xml_safe; 774 const char *encoding = NULL; 775 776 if (*outstr == NULL) 777 *outstr = svn_stringbuf_create_empty(pool); 778 779 if (svn_xml_is_xml_safe(propval->data, propval->len)) 780 { 781 svn_stringbuf_t *xml_esc = NULL; 782 svn_xml_escape_cdata_string(&xml_esc, propval, pool); 783 xml_safe = xml_esc->data; 784 } 785 else 786 { 787 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE, 788 pool); 789 encoding = "base64"; 790 xml_safe = base64ed->data; 791 } 792 793 if (encoding) 794 svn_xml_make_open_tag( 795 outstr, pool, svn_xml_protect_pcdata, 796 inherited_prop ? "inherited_property" : "property", 797 "name", propname, 798 "encoding", encoding, SVN_VA_NULL); 799 else 800 svn_xml_make_open_tag( 801 outstr, pool, svn_xml_protect_pcdata, 802 inherited_prop ? "inherited_property" : "property", 803 "name", propname, SVN_VA_NULL); 804 805 svn_stringbuf_appendcstr(*outstr, xml_safe); 806 807 svn_xml_make_close_tag( 808 outstr, pool, 809 inherited_prop ? "inherited_property" : "property"); 810 811 return; 812} 813 814/* Return the most similar string to NEEDLE in HAYSTACK, which contains 815 * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar. 816 */ 817/* See svn_cl__similarity_check() for a more general solution. */ 818static const char * 819most_similar(const char *needle_cstr, 820 const char **haystack, 821 apr_size_t haystack_len, 822 apr_pool_t *scratch_pool) 823{ 824 const char *max_similar; 825 apr_size_t max_score = 0; 826 apr_size_t i; 827 svn_membuf_t membuf; 828 svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool); 829 830 svn_membuf__create(&membuf, 64, scratch_pool); 831 832 for (i = 0; i < haystack_len; i++) 833 { 834 apr_size_t score; 835 svn_string_t *hay = svn_string_create(haystack[i], scratch_pool); 836 837 score = svn_string__similarity(needle_str, hay, &membuf, NULL); 838 839 /* If you update this factor, consider updating 840 * svn_cl__similarity_check(). */ 841 if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3 842 && score > max_score) 843 { 844 max_score = score; 845 max_similar = haystack[i]; 846 } 847 } 848 849 if (max_score) 850 return max_similar; 851 else 852 return NULL; 853} 854 855/* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */ 856static svn_error_t * 857string_in_array(const char *needle, 858 const char **haystack, 859 apr_size_t haystack_len, 860 apr_pool_t *scratch_pool) 861{ 862 const char *next_of_kin; 863 apr_size_t i; 864 for (i = 0; i < haystack_len; i++) 865 { 866 if (!strcmp(needle, haystack[i])) 867 return SVN_NO_ERROR; 868 } 869 870 /* Error. */ 871 next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool); 872 if (next_of_kin) 873 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 874 _("Ignoring unknown value '%s'; " 875 "did you mean '%s'?"), 876 needle, next_of_kin); 877 else 878 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 879 _("Ignoring unknown value '%s'"), 880 needle); 881} 882 883#include "config_keys.inc" 884 885/* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are 886 * known. Warn to stderr if not. (An unknown value may be either a typo 887 * or added in a newer minor version of Subversion.) */ 888static svn_error_t * 889validate_config_option(svn_cmdline__config_argument_t *config_option, 890 apr_pool_t *scratch_pool) 891{ 892 svn_boolean_t arbitrary_keys = FALSE; 893 894 /* TODO: some day, we could also verify that OPTION is valid for SECTION; 895 i.e., forbid invalid combinations such as config:auth:diff-extensions. */ 896 897#define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) ) 898 899 SVN_ERR(string_in_array(config_option->file, svn__valid_config_files, 900 ARRAYLEN(svn__valid_config_files), 901 scratch_pool)); 902 SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections, 903 ARRAYLEN(svn__valid_config_sections), 904 scratch_pool)); 905 906 /* Don't validate option names for sections such as servers[group], 907 * config[tunnels], and config[auto-props] that permit arbitrary options. */ 908 { 909 int i; 910 911 for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++) 912 { 913 if (!strcmp(config_option->section, svn__empty_config_sections[i])) 914 arbitrary_keys = TRUE; 915 } 916 } 917 918 if (! arbitrary_keys) 919 SVN_ERR(string_in_array(config_option->option, svn__valid_config_options, 920 ARRAYLEN(svn__valid_config_options), 921 scratch_pool)); 922 923#undef ARRAYLEN 924 925 return SVN_NO_ERROR; 926} 927 928svn_error_t * 929svn_cmdline__parse_config_option(apr_array_header_t *config_options, 930 const char *opt_arg, 931 const char *prefix, 932 apr_pool_t *pool) 933{ 934 svn_cmdline__config_argument_t *config_option; 935 const char *first_colon, *second_colon, *equals_sign; 936 apr_size_t len = strlen(opt_arg); 937 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg)) 938 { 939 if ((second_colon = strchr(first_colon + 1, ':')) && 940 (second_colon != first_colon + 1)) 941 { 942 if ((equals_sign = strchr(second_colon + 1, '=')) && 943 (equals_sign != second_colon + 1)) 944 { 945 svn_error_t *warning; 946 947 config_option = apr_pcalloc(pool, sizeof(*config_option)); 948 config_option->file = apr_pstrndup(pool, opt_arg, 949 first_colon - opt_arg); 950 config_option->section = apr_pstrndup(pool, first_colon + 1, 951 second_colon - first_colon - 1); 952 config_option->option = apr_pstrndup(pool, second_colon + 1, 953 equals_sign - second_colon - 1); 954 955 warning = validate_config_option(config_option, pool); 956 if (warning) 957 { 958 svn_handle_warning2(stderr, warning, prefix); 959 svn_error_clear(warning); 960 } 961 962 if (! (strchr(config_option->option, ':'))) 963 { 964 config_option->value = apr_pstrndup(pool, equals_sign + 1, 965 opt_arg + len - equals_sign - 1); 966 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *) 967 = config_option; 968 return SVN_NO_ERROR; 969 } 970 } 971 } 972 } 973 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 974 _("Invalid syntax of argument of --config-option")); 975} 976 977svn_error_t * 978svn_cmdline__apply_config_options(apr_hash_t *config, 979 const apr_array_header_t *config_options, 980 const char *prefix, 981 const char *argument_name) 982{ 983 int i; 984 985 for (i = 0; i < config_options->nelts; i++) 986 { 987 svn_config_t *cfg; 988 svn_cmdline__config_argument_t *arg = 989 APR_ARRAY_IDX(config_options, i, 990 svn_cmdline__config_argument_t *); 991 992 cfg = svn_hash_gets(config, arg->file); 993 994 if (cfg) 995 { 996 svn_config_set(cfg, arg->section, arg->option, arg->value); 997 } 998 else 999 { 1000 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1001 _("Unrecognized file in argument of %s"), argument_name); 1002 1003 svn_handle_warning2(stderr, err, prefix); 1004 svn_error_clear(err); 1005 } 1006 } 1007 1008 return SVN_NO_ERROR; 1009} 1010 1011/* Return a copy, allocated in POOL, of the next line of text from *STR 1012 * up to and including a CR and/or an LF. Change *STR to point to the 1013 * remainder of the string after the returned part. If there are no 1014 * characters to be returned, return NULL; never return an empty string. 1015 */ 1016static const char * 1017next_line(const char **str, apr_pool_t *pool) 1018{ 1019 const char *start = *str; 1020 const char *p = *str; 1021 1022 /* n.b. Throughout this fn, we never read any character after a '\0'. */ 1023 /* Skip over all non-EOL characters, if any. */ 1024 while (*p != '\r' && *p != '\n' && *p != '\0') 1025 p++; 1026 /* Skip over \r\n or \n\r or \r or \n, if any. */ 1027 if (*p == '\r' || *p == '\n') 1028 { 1029 char c = *p++; 1030 1031 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r')) 1032 p++; 1033 } 1034 1035 /* Now p points after at most one '\n' and/or '\r'. */ 1036 *str = p; 1037 1038 if (p == start) 1039 return NULL; 1040 1041 return svn_string_ncreate(start, p - start, pool)->data; 1042} 1043 1044const char * 1045svn_cmdline__indent_string(const char *str, 1046 const char *indent, 1047 apr_pool_t *pool) 1048{ 1049 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool); 1050 const char *line; 1051 1052 while ((line = next_line(&str, pool))) 1053 { 1054 svn_stringbuf_appendcstr(out, indent); 1055 svn_stringbuf_appendcstr(out, line); 1056 } 1057 return out->data; 1058} 1059 1060svn_error_t * 1061svn_cmdline__print_prop_hash(svn_stream_t *out, 1062 apr_hash_t *prop_hash, 1063 svn_boolean_t names_only, 1064 apr_pool_t *pool) 1065{ 1066 apr_array_header_t *sorted_props; 1067 int i; 1068 1069 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, 1070 pool); 1071 for (i = 0; i < sorted_props->nelts; i++) 1072 { 1073 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); 1074 const char *pname = item.key; 1075 svn_string_t *propval = item.value; 1076 const char *pname_stdout; 1077 1078 if (svn_prop_needs_translation(pname)) 1079 SVN_ERR(svn_subst_detranslate_string(&propval, propval, 1080 TRUE, pool)); 1081 1082 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool)); 1083 1084 if (out) 1085 { 1086 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout); 1087 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout, 1088 APR_EOL_STR, /* 'native' eol */ 1089 FALSE, /* no repair */ 1090 NULL, /* no keywords */ 1091 FALSE, /* no expansion */ 1092 pool)); 1093 1094 SVN_ERR(svn_stream_puts(out, pname_stdout)); 1095 } 1096 else 1097 { 1098 /* ### We leave these printfs for now, since if propval wasn't 1099 translated above, we don't know anything about its encoding. 1100 In fact, it might be binary data... */ 1101 printf(" %s\n", pname_stdout); 1102 } 1103 1104 if (!names_only) 1105 { 1106 /* Add an extra newline to the value before indenting, so that 1107 * every line of output has the indentation whether the value 1108 * already ended in a newline or not. */ 1109 const char *newval = apr_psprintf(pool, "%s\n", propval->data); 1110 const char *indented_newval = svn_cmdline__indent_string(newval, 1111 " ", 1112 pool); 1113 if (out) 1114 { 1115 SVN_ERR(svn_stream_puts(out, indented_newval)); 1116 } 1117 else 1118 { 1119 printf("%s", indented_newval); 1120 } 1121 } 1122 } 1123 1124 return SVN_NO_ERROR; 1125} 1126 1127svn_error_t * 1128svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr, 1129 apr_hash_t *prop_hash, 1130 svn_boolean_t names_only, 1131 svn_boolean_t inherited_props, 1132 apr_pool_t *pool) 1133{ 1134 apr_array_header_t *sorted_props; 1135 int i; 1136 1137 if (*outstr == NULL) 1138 *outstr = svn_stringbuf_create_empty(pool); 1139 1140 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, 1141 pool); 1142 for (i = 0; i < sorted_props->nelts; i++) 1143 { 1144 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); 1145 const char *pname = item.key; 1146 svn_string_t *propval = item.value; 1147 1148 if (names_only) 1149 { 1150 svn_xml_make_open_tag( 1151 outstr, pool, svn_xml_self_closing, 1152 inherited_props ? "inherited_property" : "property", 1153 "name", pname, SVN_VA_NULL); 1154 } 1155 else 1156 { 1157 const char *pname_out; 1158 1159 if (svn_prop_needs_translation(pname)) 1160 SVN_ERR(svn_subst_detranslate_string(&propval, propval, 1161 TRUE, pool)); 1162 1163 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool)); 1164 1165 svn_cmdline__print_xml_prop(outstr, pname_out, propval, 1166 inherited_props, pool); 1167 } 1168 } 1169 1170 return SVN_NO_ERROR; 1171} 1172 1173svn_boolean_t 1174svn_cmdline__be_interactive(svn_boolean_t non_interactive, 1175 svn_boolean_t force_interactive) 1176{ 1177 /* If neither --non-interactive nor --force-interactive was passed, 1178 * be interactive if stdin is a terminal. 1179 * If --force-interactive was passed, always be interactive. */ 1180 if (!force_interactive && !non_interactive) 1181 { 1182#ifdef WIN32 1183 return (_isatty(STDIN_FILENO) != 0); 1184#else 1185 return (isatty(STDIN_FILENO) != 0); 1186#endif 1187 } 1188 else if (force_interactive) 1189 return TRUE; 1190 1191 return !non_interactive; 1192} 1193 1194 1195/* Helper for the next two functions. Set *EDITOR to some path to an 1196 editor binary. Sources to search include: the EDITOR_CMD argument 1197 (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG 1198 is not NULL), $VISUAL, $EDITOR. Return 1199 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */ 1200static svn_error_t * 1201find_editor_binary(const char **editor, 1202 const char *editor_cmd, 1203 apr_hash_t *config) 1204{ 1205 const char *e; 1206 struct svn_config_t *cfg; 1207 1208 /* Use the editor specified on the command line via --editor-cmd, if any. */ 1209 e = editor_cmd; 1210 1211 /* Otherwise look for the Subversion-specific environment variable. */ 1212 if (! e) 1213 e = getenv("SVN_EDITOR"); 1214 1215 /* If not found then fall back on the config file. */ 1216 if (! e) 1217 { 1218 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; 1219 svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS, 1220 SVN_CONFIG_OPTION_EDITOR_CMD, NULL); 1221 } 1222 1223 /* If not found yet then try general purpose environment variables. */ 1224 if (! e) 1225 e = getenv("VISUAL"); 1226 if (! e) 1227 e = getenv("EDITOR"); 1228 1229#ifdef SVN_CLIENT_EDITOR 1230 /* If still not found then fall back on the hard-coded default. */ 1231 if (! e) 1232 e = SVN_CLIENT_EDITOR; 1233#endif 1234 1235 /* Error if there is no editor specified */ 1236 if (e) 1237 { 1238 const char *c; 1239 1240 for (c = e; *c; c++) 1241 if (!svn_ctype_isspace(*c)) 1242 break; 1243 1244 if (! *c) 1245 return svn_error_create 1246 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL, 1247 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or " 1248 "'editor-cmd' run-time configuration option is empty or " 1249 "consists solely of whitespace. Expected a shell command.")); 1250 } 1251 else 1252 return svn_error_create 1253 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL, 1254 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are " 1255 "set, and no 'editor-cmd' run-time configuration option was found")); 1256 1257 *editor = e; 1258 return SVN_NO_ERROR; 1259} 1260 1261 1262svn_error_t * 1263svn_cmdline__edit_file_externally(const char *path, 1264 const char *editor_cmd, 1265 apr_hash_t *config, 1266 apr_pool_t *pool) 1267{ 1268 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr; 1269 char *old_cwd; 1270 int sys_err; 1271 apr_status_t apr_err; 1272 1273 svn_dirent_split(&base_dir, &file_name, path, pool); 1274 1275 SVN_ERR(find_editor_binary(&editor, editor_cmd, config)); 1276 1277 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool); 1278 if (apr_err) 1279 return svn_error_wrap_apr(apr_err, _("Can't get working directory")); 1280 1281 /* APR doesn't like "" directories */ 1282 if (base_dir[0] == '\0') 1283 base_dir_apr = "."; 1284 else 1285 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool)); 1286 1287 apr_err = apr_filepath_set(base_dir_apr, pool); 1288 if (apr_err) 1289 return svn_error_wrap_apr 1290 (apr_err, _("Can't change working directory to '%s'"), base_dir); 1291 1292 cmd = apr_psprintf(pool, "%s %s", editor, file_name); 1293 sys_err = system(cmd); 1294 1295 apr_err = apr_filepath_set(old_cwd, pool); 1296 if (apr_err) 1297 svn_handle_error2(svn_error_wrap_apr 1298 (apr_err, _("Can't restore working directory")), 1299 stderr, TRUE /* fatal */, "svn: "); 1300 1301 if (sys_err) 1302 /* Extracting any meaning from sys_err is platform specific, so just 1303 use the raw value. */ 1304 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 1305 _("system('%s') returned %d"), cmd, sys_err); 1306 1307 return SVN_NO_ERROR; 1308} 1309 1310 1311svn_error_t * 1312svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, 1313 const char **tmpfile_left /* UTF-8! */, 1314 const char *editor_cmd, 1315 const char *base_dir /* UTF-8! */, 1316 const svn_string_t *contents /* UTF-8! */, 1317 const char *filename, 1318 apr_hash_t *config, 1319 svn_boolean_t as_text, 1320 const char *encoding, 1321 apr_pool_t *pool) 1322{ 1323 const char *editor; 1324 const char *cmd; 1325 apr_file_t *tmp_file; 1326 const char *tmpfile_name; 1327 const char *tmpfile_native; 1328 const char *tmpfile_apr, *base_dir_apr; 1329 svn_string_t *translated_contents; 1330 apr_status_t apr_err, apr_err2; 1331 apr_size_t written; 1332 apr_finfo_t finfo_before, finfo_after; 1333 svn_error_t *err = SVN_NO_ERROR, *err2; 1334 char *old_cwd; 1335 int sys_err; 1336 svn_boolean_t remove_file = TRUE; 1337 1338 SVN_ERR(find_editor_binary(&editor, editor_cmd, config)); 1339 1340 /* Convert file contents from UTF-8/LF if desired. */ 1341 if (as_text) 1342 { 1343 const char *translated; 1344 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated, 1345 APR_EOL_STR, FALSE, 1346 NULL, FALSE, pool)); 1347 translated_contents = svn_string_create_empty(pool); 1348 if (encoding) 1349 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data, 1350 translated, encoding, pool)); 1351 else 1352 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data, 1353 translated, pool)); 1354 translated_contents->len = strlen(translated_contents->data); 1355 } 1356 else 1357 translated_contents = svn_string_dup(contents, pool); 1358 1359 /* Move to BASE_DIR to avoid getting characters that need quoting 1360 into tmpfile_name */ 1361 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool); 1362 if (apr_err) 1363 return svn_error_wrap_apr(apr_err, _("Can't get working directory")); 1364 1365 /* APR doesn't like "" directories */ 1366 if (base_dir[0] == '\0') 1367 base_dir_apr = "."; 1368 else 1369 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool)); 1370 apr_err = apr_filepath_set(base_dir_apr, pool); 1371 if (apr_err) 1372 { 1373 return svn_error_wrap_apr 1374 (apr_err, _("Can't change working directory to '%s'"), base_dir); 1375 } 1376 1377 /*** From here on, any problems that occur require us to cd back!! ***/ 1378 1379 /* Ask the working copy for a temporary file named FILENAME-something. */ 1380 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name, 1381 "" /* dirpath */, 1382 filename, 1383 ".tmp", 1384 svn_io_file_del_none, pool, pool); 1385 1386 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS)) 1387 { 1388 const char *temp_dir_apr; 1389 1390 svn_error_clear(err); 1391 1392 SVN_ERR(svn_io_temp_dir(&base_dir, pool)); 1393 1394 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool)); 1395 apr_err = apr_filepath_set(temp_dir_apr, pool); 1396 if (apr_err) 1397 { 1398 return svn_error_wrap_apr 1399 (apr_err, _("Can't change working directory to '%s'"), base_dir); 1400 } 1401 1402 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name, 1403 "" /* dirpath */, 1404 filename, 1405 ".tmp", 1406 svn_io_file_del_none, pool, pool); 1407 } 1408 1409 if (err) 1410 goto cleanup2; 1411 1412 /*** From here on, any problems that occur require us to cleanup 1413 the file we just created!! ***/ 1414 1415 /* Dump initial CONTENTS to TMP_FILE. */ 1416 apr_err = apr_file_write_full(tmp_file, translated_contents->data, 1417 translated_contents->len, &written); 1418 1419 apr_err2 = apr_file_close(tmp_file); 1420 if (! apr_err) 1421 apr_err = apr_err2; 1422 1423 /* Make sure the whole CONTENTS were written, else return an error. */ 1424 if (apr_err) 1425 { 1426 err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"), 1427 tmpfile_name); 1428 goto cleanup; 1429 } 1430 1431 err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool); 1432 if (err) 1433 goto cleanup; 1434 1435 /* Get information about the temporary file before the user has 1436 been allowed to edit its contents. */ 1437 apr_err = apr_stat(&finfo_before, tmpfile_apr, 1438 APR_FINFO_MTIME, pool); 1439 if (apr_err) 1440 { 1441 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); 1442 goto cleanup; 1443 } 1444 1445 /* Backdate the file a little bit in case the editor is very fast 1446 and doesn't change the size. (Use two seconds, since some 1447 filesystems have coarse granularity.) It's OK if this call 1448 fails, so we don't check its return value.*/ 1449 apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool); 1450 1451 /* Stat it again to get the mtime we actually set. */ 1452 apr_err = apr_stat(&finfo_before, tmpfile_apr, 1453 APR_FINFO_MTIME | APR_FINFO_SIZE, pool); 1454 if (apr_err) 1455 { 1456 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); 1457 goto cleanup; 1458 } 1459 1460 /* Prepare the editor command line. */ 1461 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool); 1462 if (err) 1463 goto cleanup; 1464 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native); 1465 1466 /* If the caller wants us to leave the file around, return the path 1467 of the file we'll use, and make a note not to destroy it. */ 1468 if (tmpfile_left) 1469 { 1470 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool); 1471 remove_file = FALSE; 1472 } 1473 1474 /* Now, run the editor command line. */ 1475 sys_err = system(cmd); 1476 if (sys_err != 0) 1477 { 1478 /* Extracting any meaning from sys_err is platform specific, so just 1479 use the raw value. */ 1480 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 1481 _("system('%s') returned %d"), cmd, sys_err); 1482 goto cleanup; 1483 } 1484 1485 /* Get information about the temporary file after the assumed editing. */ 1486 apr_err = apr_stat(&finfo_after, tmpfile_apr, 1487 APR_FINFO_MTIME | APR_FINFO_SIZE, pool); 1488 if (apr_err) 1489 { 1490 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name); 1491 goto cleanup; 1492 } 1493 1494 /* If the file looks changed... */ 1495 if ((finfo_before.mtime != finfo_after.mtime) || 1496 (finfo_before.size != finfo_after.size)) 1497 { 1498 svn_stringbuf_t *edited_contents_s; 1499 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool); 1500 if (err) 1501 goto cleanup; 1502 1503 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s); 1504 1505 /* Translate back to UTF8/LF if desired. */ 1506 if (as_text) 1507 { 1508 err = svn_subst_translate_string2(edited_contents, NULL, NULL, 1509 *edited_contents, encoding, FALSE, 1510 pool, pool); 1511 if (err) 1512 { 1513 err = svn_error_quick_wrap 1514 (err, 1515 _("Error normalizing edited contents to internal format")); 1516 goto cleanup; 1517 } 1518 } 1519 } 1520 else 1521 { 1522 /* No edits seem to have been made */ 1523 *edited_contents = NULL; 1524 } 1525 1526 cleanup: 1527 if (remove_file) 1528 { 1529 /* Remove the file from disk. */ 1530 err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool); 1531 1532 /* Only report remove error if there was no previous error. */ 1533 if (! err && err2) 1534 err = err2; 1535 else 1536 svn_error_clear(err2); 1537 } 1538 1539 cleanup2: 1540 /* If we against all probability can't cd back, all further relative 1541 file references would be screwed up, so we have to abort. */ 1542 apr_err = apr_filepath_set(old_cwd, pool); 1543 if (apr_err) 1544 { 1545 svn_handle_error2(svn_error_wrap_apr 1546 (apr_err, _("Can't restore working directory")), 1547 stderr, TRUE /* fatal */, "svn: "); 1548 } 1549 1550 return svn_error_trace(err); 1551} 1552 1553svn_error_t * 1554svn_cmdline__parse_trust_options( 1555 svn_boolean_t *trust_server_cert_unknown_ca, 1556 svn_boolean_t *trust_server_cert_cn_mismatch, 1557 svn_boolean_t *trust_server_cert_expired, 1558 svn_boolean_t *trust_server_cert_not_yet_valid, 1559 svn_boolean_t *trust_server_cert_other_failure, 1560 const char *opt_arg, 1561 apr_pool_t *scratch_pool) 1562{ 1563 apr_array_header_t *failures; 1564 int i; 1565 1566 *trust_server_cert_unknown_ca = FALSE; 1567 *trust_server_cert_cn_mismatch = FALSE; 1568 *trust_server_cert_expired = FALSE; 1569 *trust_server_cert_not_yet_valid = FALSE; 1570 *trust_server_cert_other_failure = FALSE; 1571 1572 failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool); 1573 1574 for (i = 0; i < failures->nelts; i++) 1575 { 1576 const char *value = APR_ARRAY_IDX(failures, i, const char *); 1577 if (!strcmp(value, "unknown-ca")) 1578 *trust_server_cert_unknown_ca = TRUE; 1579 else if (!strcmp(value, "cn-mismatch")) 1580 *trust_server_cert_cn_mismatch = TRUE; 1581 else if (!strcmp(value, "expired")) 1582 *trust_server_cert_expired = TRUE; 1583 else if (!strcmp(value, "not-yet-valid")) 1584 *trust_server_cert_not_yet_valid = TRUE; 1585 else if (!strcmp(value, "other")) 1586 *trust_server_cert_other_failure = TRUE; 1587 else 1588 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1589 _("Unknown value '%s' for %s.\n" 1590 "Supported values: %s"), 1591 value, 1592 "--trust-server-cert-failures", 1593 "unknown-ca, cn-mismatch, expired, " 1594 "not-yet-valid, other"); 1595 } 1596 1597 return SVN_NO_ERROR; 1598} 1599