prompt.c revision 269847
1/* 2 * prompt.c -- ask the user for authentication information. 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 26 27 28/*** Includes. ***/ 29 30#include <apr_lib.h> 31#include <apr_poll.h> 32#include <apr_portable.h> 33 34#include "svn_cmdline.h" 35#include "svn_ctype.h" 36#include "svn_string.h" 37#include "svn_auth.h" 38#include "svn_error.h" 39#include "svn_path.h" 40 41#include "private/svn_cmdline_private.h" 42#include "svn_private_config.h" 43 44#ifdef WIN32 45#include <conio.h> 46#elif defined(HAVE_TERMIOS_H) 47#include <signal.h> 48#include <termios.h> 49#endif 50 51 52 53/* Descriptor of an open terminal */ 54typedef struct terminal_handle_t terminal_handle_t; 55struct terminal_handle_t 56{ 57 apr_file_t *infd; /* input file handle */ 58 apr_file_t *outfd; /* output file handle */ 59 svn_boolean_t noecho; /* terminal echo was turned off */ 60 svn_boolean_t close_handles; /* close handles when closing the terminal */ 61 apr_pool_t *pool; /* pool associated with the file handles */ 62 63#ifdef HAVE_TERMIOS_H 64 svn_boolean_t restore_state; /* terminal state was changed */ 65 apr_os_file_t osinfd; /* OS-specific handle for infd */ 66 struct termios attr; /* saved terminal attributes */ 67#endif 68}; 69 70/* Initialize safe state of terminal_handle_t. */ 71static void 72terminal_handle_init(terminal_handle_t *terminal, 73 apr_file_t *infd, apr_file_t *outfd, 74 svn_boolean_t noecho, svn_boolean_t close_handles, 75 apr_pool_t *pool) 76{ 77 memset(terminal, 0, sizeof(*terminal)); 78 terminal->infd = infd; 79 terminal->outfd = outfd; 80 terminal->noecho = noecho; 81 terminal->close_handles = close_handles; 82 terminal->pool = pool; 83} 84 85/* 86 * Common pool cleanup handler for terminal_handle_t. Closes TERMINAL. 87 * If CLOSE_HANDLES is TRUE, close the terminal file handles. 88 * If RESTORE_STATE is TRUE, restores the TERMIOS flags of the terminal. 89 */ 90static apr_status_t 91terminal_cleanup_handler(terminal_handle_t *terminal, 92 svn_boolean_t close_handles, 93 svn_boolean_t restore_state) 94{ 95 apr_status_t status = APR_SUCCESS; 96 97#ifdef HAVE_TERMIOS_H 98 /* Restore terminal state flags. */ 99 if (restore_state && terminal->restore_state) 100 tcsetattr(terminal->osinfd, TCSANOW, &terminal->attr); 101#endif 102 103 /* Close terminal handles. */ 104 if (close_handles && terminal->close_handles) 105 { 106 apr_file_t *const infd = terminal->infd; 107 apr_file_t *const outfd = terminal->outfd; 108 109 if (infd) 110 { 111 terminal->infd = NULL; 112 status = apr_file_close(infd); 113 } 114 115 if (!status && outfd && outfd != infd) 116 { 117 terminal->outfd = NULL; 118 status = apr_file_close(terminal->outfd); 119 } 120 } 121 return status; 122} 123 124/* Normal pool cleanup for a terminal. */ 125static apr_status_t terminal_plain_cleanup(void *baton) 126{ 127 return terminal_cleanup_handler(baton, FALSE, TRUE); 128} 129 130/* Child pool cleanup for a terminal -- does not restore echo state. */ 131static apr_status_t terminal_child_cleanup(void *baton) 132{ 133 return terminal_cleanup_handler(baton, FALSE, FALSE); 134} 135 136/* Explicitly close the terminal, removing its cleanup handlers. */ 137static svn_error_t * 138terminal_close(terminal_handle_t *terminal) 139{ 140 apr_status_t status; 141 142 /* apr_pool_cleanup_kill() removes both normal and child cleanup */ 143 apr_pool_cleanup_kill(terminal->pool, terminal, terminal_plain_cleanup); 144 145 status = terminal_cleanup_handler(terminal, TRUE, TRUE); 146 if (status) 147 return svn_error_create(status, NULL, _("Can't close terminal")); 148 return SVN_NO_ERROR; 149} 150 151/* Allocate and open *TERMINAL. If NOECHO is TRUE, try to turn off 152 terminal echo. Use POOL for all allocations.*/ 153static svn_error_t * 154terminal_open(terminal_handle_t **terminal, svn_boolean_t noecho, 155 apr_pool_t *pool) 156{ 157 apr_status_t status; 158 159#ifdef WIN32 160 /* On Windows, we'll use the console API directly if the process has 161 a console attached; otherwise we'll just use stdin and stderr. */ 162 const HANDLE conin = CreateFileW(L"CONIN$", GENERIC_READ, 163 FILE_SHARE_READ | FILE_SHARE_WRITE, 164 NULL, OPEN_EXISTING, 165 FILE_ATTRIBUTE_NORMAL, NULL); 166 *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); 167 if (conin != INVALID_HANDLE_VALUE) 168 { 169 /* The process has a console. */ 170 CloseHandle(conin); 171 terminal_handle_init(*terminal, NULL, NULL, noecho, FALSE, NULL); 172 return SVN_NO_ERROR; 173 } 174#else /* !WIN32 */ 175 /* Without evidence to the contrary, we'll assume this is *nix and 176 try to open /dev/tty. If that fails, we'll use stdin for input 177 and stderr for prompting. */ 178 apr_file_t *tmpfd; 179 status = apr_file_open(&tmpfd, "/dev/tty", 180 APR_READ | APR_WRITE, 181 APR_OS_DEFAULT, pool); 182 *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); 183 if (!status) 184 { 185 /* We have a terminal handle that we can use for input and output. */ 186 terminal_handle_init(*terminal, tmpfd, tmpfd, FALSE, TRUE, pool); 187 } 188#endif /* !WIN32 */ 189 else 190 { 191 /* There is no terminal. Sigh. */ 192 apr_file_t *infd; 193 apr_file_t *outfd; 194 195 status = apr_file_open_stdin(&infd, pool); 196 if (status) 197 return svn_error_wrap_apr(status, _("Can't open stdin")); 198 status = apr_file_open_stderr(&outfd, pool); 199 if (status) 200 return svn_error_wrap_apr(status, _("Can't open stderr")); 201 terminal_handle_init(*terminal, infd, outfd, FALSE, FALSE, pool); 202 } 203 204#ifdef HAVE_TERMIOS_H 205 /* Set terminal state */ 206 if (0 == apr_os_file_get(&(*terminal)->osinfd, (*terminal)->infd)) 207 { 208 if (0 == tcgetattr((*terminal)->osinfd, &(*terminal)->attr)) 209 { 210 struct termios attr = (*terminal)->attr; 211 /* Turn off signal handling and canonical input mode */ 212 attr.c_lflag &= ~(ISIG | ICANON); 213 attr.c_cc[VMIN] = 1; /* Read one byte at a time */ 214 attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */ 215 attr.c_lflag &= ~(ECHO); /* Turn off echo */ 216 if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr)) 217 { 218 (*terminal)->noecho = noecho; 219 (*terminal)->restore_state = TRUE; 220 } 221 } 222 } 223#endif /* HAVE_TERMIOS_H */ 224 225 /* Register pool cleanup to close handles and restore echo state. */ 226 apr_pool_cleanup_register((*terminal)->pool, *terminal, 227 terminal_plain_cleanup, 228 terminal_child_cleanup); 229 return SVN_NO_ERROR; 230} 231 232/* Write a null-terminated STRING to TERMINAL. 233 Use POOL for allocations related to converting STRING from UTF-8. */ 234static svn_error_t * 235terminal_puts(const char *string, terminal_handle_t *terminal, 236 apr_pool_t *pool) 237{ 238 svn_error_t *err; 239 apr_status_t status; 240 const char *converted; 241 242 err = svn_cmdline_cstring_from_utf8(&converted, string, pool); 243 if (err) 244 { 245 svn_error_clear(err); 246 converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); 247 } 248 249#ifdef WIN32 250 if (!terminal->outfd) 251 { 252 /* See terminal_open; we're using Console I/O. */ 253 _cputs(converted); 254 return SVN_NO_ERROR; 255 } 256#endif 257 258 status = apr_file_write_full(terminal->outfd, converted, 259 strlen(converted), NULL); 260 if (!status) 261 status = apr_file_flush(terminal->outfd); 262 if (status) 263 return svn_error_wrap_apr(status, _("Can't write to terminal")); 264 return SVN_NO_ERROR; 265} 266 267/* These codes can be returned from terminal_getc instead of a character. */ 268#define TERMINAL_NONE 0x80000 /* no character read, retry */ 269#define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */ 270#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */ 271#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */ 272 273/* Helper for terminal_getc: writes CH to OUTFD as a control char. */ 274#ifndef WIN32 275static void 276echo_control_char(char ch, apr_file_t *outfd) 277{ 278 if (svn_ctype_iscntrl(ch)) 279 { 280 const char substitute = (ch < 32? '@' + ch : '?'); 281 apr_file_putc('^', outfd); 282 apr_file_putc(substitute, outfd); 283 } 284 else if (svn_ctype_isprint(ch)) 285 { 286 /* Pass printable characters unchanged. */ 287 apr_file_putc(ch, outfd); 288 } 289 else 290 { 291 /* Everything else is strange. */ 292 apr_file_putc('^', outfd); 293 apr_file_putc('!', outfd); 294 } 295} 296#endif /* WIN32 */ 297 298/* Read one character or control code from TERMINAL, returning it in CODE. 299 if CAN_ERASE and the input was a deletion, emit codes to erase the 300 last character displayed on the terminal. 301 Use POOL for all allocations. */ 302static svn_error_t * 303terminal_getc(int *code, terminal_handle_t *terminal, 304 svn_boolean_t can_erase, apr_pool_t *pool) 305{ 306 const svn_boolean_t echo = !terminal->noecho; 307 apr_status_t status = APR_SUCCESS; 308 char ch; 309 310#ifdef WIN32 311 if (!terminal->infd) 312 { 313 /* See terminal_open; we're using Console I/O. */ 314 315 /* The following was hoisted from APR's getpass for Windows. */ 316 int concode = _getch(); 317 switch (concode) 318 { 319 case '\r': /* end-of-line */ 320 *code = TERMINAL_EOL; 321 if (echo) 322 _cputs("\r\n"); 323 break; 324 325 case EOF: /* end-of-file */ 326 case 26: /* Ctrl+Z */ 327 *code = TERMINAL_EOF; 328 if (echo) 329 _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n")); 330 break; 331 332 case 3: /* Ctrl+C, Ctrl+Break */ 333 /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ 334 if (echo) 335 _cputs("^C\r\n"); 336 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 337 338 case 0: /* Function code prefix */ 339 case 0xE0: 340 concode = (concode << 4) | _getch(); 341 /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ 342 if (concode == 0xE53 || concode == 0xE4B 343 || concode == 0x053 || concode == 0x04B) 344 { 345 *code = TERMINAL_DEL; 346 if (can_erase) 347 _cputs("\b \b"); 348 } 349 else 350 { 351 *code = TERMINAL_NONE; 352 _putch('\a'); 353 } 354 break; 355 356 case '\b': /* BS */ 357 case 127: /* DEL */ 358 *code = TERMINAL_DEL; 359 if (can_erase) 360 _cputs("\b \b"); 361 break; 362 363 default: 364 if (!apr_iscntrl(concode)) 365 { 366 *code = (int)(unsigned char)concode; 367 _putch(echo ? concode : '*'); 368 } 369 else 370 { 371 *code = TERMINAL_NONE; 372 _putch('\a'); 373 } 374 } 375 return SVN_NO_ERROR; 376 } 377#elif defined(HAVE_TERMIOS_H) 378 if (terminal->restore_state) 379 { 380 /* We're using a bytewise-immediate termios input */ 381 const struct termios *const attr = &terminal->attr; 382 383 status = apr_file_getc(&ch, terminal->infd); 384 if (status) 385 return svn_error_wrap_apr(status, _("Can't read from terminal")); 386 387 if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT]) 388 { 389 /* Break */ 390 echo_control_char(ch, terminal->outfd); 391 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 392 } 393 else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL]) 394 { 395 /* Newline */ 396 *code = TERMINAL_EOL; 397 apr_file_putc('\n', terminal->outfd); 398 } 399 else if (ch == '\b' || ch == attr->c_cc[VERASE]) 400 { 401 /* Delete */ 402 *code = TERMINAL_DEL; 403 if (can_erase) 404 { 405 apr_file_putc('\b', terminal->outfd); 406 apr_file_putc(' ', terminal->outfd); 407 apr_file_putc('\b', terminal->outfd); 408 } 409 } 410 else if (ch == attr->c_cc[VEOF]) 411 { 412 /* End of input */ 413 *code = TERMINAL_EOF; 414 echo_control_char(ch, terminal->outfd); 415 } 416 else if (ch == attr->c_cc[VSUSP]) 417 { 418 /* Suspend */ 419 *code = TERMINAL_NONE; 420 kill(0, SIGTSTP); 421 } 422 else if (!apr_iscntrl(ch)) 423 { 424 /* Normal character */ 425 *code = (int)(unsigned char)ch; 426 apr_file_putc((echo ? ch : '*'), terminal->outfd); 427 } 428 else 429 { 430 /* Ignored character */ 431 *code = TERMINAL_NONE; 432 apr_file_putc('\a', terminal->outfd); 433 } 434 return SVN_NO_ERROR; 435 } 436#endif /* HAVE_TERMIOS_H */ 437 438 /* Fall back to plain stream-based I/O. */ 439#ifndef WIN32 440 /* Wait for input on termin. This code is based on 441 apr_wait_for_io_or_timeout(). 442 Note that this will return an EINTR on a signal. */ 443 { 444 apr_pollfd_t pollset; 445 int n; 446 447 pollset.desc_type = APR_POLL_FILE; 448 pollset.desc.f = terminal->infd; 449 pollset.p = pool; 450 pollset.reqevents = APR_POLLIN; 451 452 status = apr_poll(&pollset, 1, &n, -1); 453 454 if (n == 1 && pollset.rtnevents & APR_POLLIN) 455 status = APR_SUCCESS; 456 } 457#endif /* !WIN32 */ 458 459 if (!status) 460 status = apr_file_getc(&ch, terminal->infd); 461 if (APR_STATUS_IS_EINTR(status)) 462 { 463 *code = TERMINAL_NONE; 464 return SVN_NO_ERROR; 465 } 466 else if (APR_STATUS_IS_EOF(status)) 467 { 468 *code = TERMINAL_EOF; 469 return SVN_NO_ERROR; 470 } 471 else if (status) 472 return svn_error_wrap_apr(status, _("Can't read from terminal")); 473 474 *code = (int)(unsigned char)ch; 475 return SVN_NO_ERROR; 476} 477 478 479/* Set @a *result to the result of prompting the user with @a 480 * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton. 481 * Do not call the cancel_func if @a *pb is NULL. 482 * Allocate @a *result in @a pool. 483 * 484 * If @a hide is true, then try to avoid displaying the user's input. 485 */ 486static svn_error_t * 487prompt(const char **result, 488 const char *prompt_msg, 489 svn_boolean_t hide, 490 svn_cmdline_prompt_baton2_t *pb, 491 apr_pool_t *pool) 492{ 493 /* XXX: If this functions ever starts using members of *pb 494 * which were not included in svn_cmdline_prompt_baton_t, 495 * we need to update svn_cmdline_prompt_user2 and its callers. */ 496 497 svn_boolean_t saw_first_half_of_eol = FALSE; 498 svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool); 499 terminal_handle_t *terminal; 500 int code; 501 char c; 502 503 SVN_ERR(terminal_open(&terminal, hide, pool)); 504 SVN_ERR(terminal_puts(prompt_msg, terminal, pool)); 505 506 while (1) 507 { 508 SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool)); 509 510 /* Check for cancellation after a character has been read, some 511 input processing modes may eat ^C and we'll only notice a 512 cancellation signal after characters have been read -- 513 sometimes even after a newline. */ 514 if (pb) 515 SVN_ERR(pb->cancel_func(pb->cancel_baton)); 516 517 switch (code) 518 { 519 case TERMINAL_NONE: 520 /* Nothing useful happened; retry. */ 521 continue; 522 523 case TERMINAL_DEL: 524 /* Delete the last input character. terminal_getc takes care 525 of erasing the feedback from the terminal, if applicable. */ 526 svn_stringbuf_chop(strbuf, 1); 527 continue; 528 529 case TERMINAL_EOL: 530 /* End-of-line means end of input. Trick the EOL-detection code 531 below to stop reading. */ 532 saw_first_half_of_eol = TRUE; 533 c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */ 534 break; 535 536 case TERMINAL_EOF: 537 return svn_error_create( 538 APR_EOF, 539 terminal_close(terminal), 540 _("End of file while reading from terminal")); 541 542 default: 543 /* Convert the returned code back to the character. */ 544 c = (char)code; 545 } 546 547 if (saw_first_half_of_eol) 548 { 549 if (c == APR_EOL_STR[1]) 550 break; 551 else 552 saw_first_half_of_eol = FALSE; 553 } 554 else if (c == APR_EOL_STR[0]) 555 { 556 /* GCC might complain here: "warning: will never be executed" 557 * That's fine. This is a compile-time check for "\r\n\0" */ 558 if (sizeof(APR_EOL_STR) == 3) 559 { 560 saw_first_half_of_eol = TRUE; 561 continue; 562 } 563 else if (sizeof(APR_EOL_STR) == 2) 564 break; 565 else 566 /* ### APR_EOL_STR holds more than two chars? Who 567 ever heard of such a thing? */ 568 SVN_ERR_MALFUNCTION(); 569 } 570 571 svn_stringbuf_appendbyte(strbuf, c); 572 } 573 574 if (terminal->noecho) 575 { 576 /* If terminal echo was turned off, make sure future output 577 to the terminal starts on a new line, as expected. */ 578 SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool)); 579 } 580 SVN_ERR(terminal_close(terminal)); 581 582 return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool); 583} 584 585 586 587/** Prompt functions for auth providers. **/ 588 589/* Helper function for auth provider prompters: mention the 590 * authentication @a realm on stderr, in a manner appropriate for 591 * preceding a prompt; or if @a realm is null, then do nothing. 592 */ 593static svn_error_t * 594maybe_print_realm(const char *realm, apr_pool_t *pool) 595{ 596 if (realm) 597 { 598 terminal_handle_t *terminal; 599 SVN_ERR(terminal_open(&terminal, FALSE, pool)); 600 SVN_ERR(terminal_puts( 601 apr_psprintf(pool, 602 _("Authentication realm: %s\n"), realm), 603 terminal, pool)); 604 SVN_ERR(terminal_close(terminal)); 605 } 606 607 return SVN_NO_ERROR; 608} 609 610 611/* This implements 'svn_auth_simple_prompt_func_t'. */ 612svn_error_t * 613svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p, 614 void *baton, 615 const char *realm, 616 const char *username, 617 svn_boolean_t may_save, 618 apr_pool_t *pool) 619{ 620 svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret)); 621 const char *pass_prompt; 622 svn_cmdline_prompt_baton2_t *pb = baton; 623 624 SVN_ERR(maybe_print_realm(realm, pool)); 625 626 if (username) 627 ret->username = apr_pstrdup(pool, username); 628 else 629 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 630 631 pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username); 632 SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool)); 633 ret->may_save = may_save; 634 *cred_p = ret; 635 return SVN_NO_ERROR; 636} 637 638 639/* This implements 'svn_auth_username_prompt_func_t'. */ 640svn_error_t * 641svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p, 642 void *baton, 643 const char *realm, 644 svn_boolean_t may_save, 645 apr_pool_t *pool) 646{ 647 svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret)); 648 svn_cmdline_prompt_baton2_t *pb = baton; 649 650 SVN_ERR(maybe_print_realm(realm, pool)); 651 652 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 653 ret->may_save = may_save; 654 *cred_p = ret; 655 return SVN_NO_ERROR; 656} 657 658 659/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */ 660svn_error_t * 661svn_cmdline_auth_ssl_server_trust_prompt 662 (svn_auth_cred_ssl_server_trust_t **cred_p, 663 void *baton, 664 const char *realm, 665 apr_uint32_t failures, 666 const svn_auth_ssl_server_cert_info_t *cert_info, 667 svn_boolean_t may_save, 668 apr_pool_t *pool) 669{ 670 const char *choice; 671 svn_stringbuf_t *msg; 672 svn_cmdline_prompt_baton2_t *pb = baton; 673 svn_stringbuf_t *buf = svn_stringbuf_createf 674 (pool, _("Error validating server certificate for '%s':\n"), realm); 675 676 if (failures & SVN_AUTH_SSL_UNKNOWNCA) 677 { 678 svn_stringbuf_appendcstr 679 (buf, 680 _(" - The certificate is not issued by a trusted authority. Use the\n" 681 " fingerprint to validate the certificate manually!\n")); 682 } 683 684 if (failures & SVN_AUTH_SSL_CNMISMATCH) 685 { 686 svn_stringbuf_appendcstr 687 (buf, _(" - The certificate hostname does not match.\n")); 688 } 689 690 if (failures & SVN_AUTH_SSL_NOTYETVALID) 691 { 692 svn_stringbuf_appendcstr 693 (buf, _(" - The certificate is not yet valid.\n")); 694 } 695 696 if (failures & SVN_AUTH_SSL_EXPIRED) 697 { 698 svn_stringbuf_appendcstr 699 (buf, _(" - The certificate has expired.\n")); 700 } 701 702 if (failures & SVN_AUTH_SSL_OTHER) 703 { 704 svn_stringbuf_appendcstr 705 (buf, _(" - The certificate has an unknown error.\n")); 706 } 707 708 msg = svn_stringbuf_createf 709 (pool, 710 _("Certificate information:\n" 711 " - Hostname: %s\n" 712 " - Valid: from %s until %s\n" 713 " - Issuer: %s\n" 714 " - Fingerprint: %s\n"), 715 cert_info->hostname, 716 cert_info->valid_from, 717 cert_info->valid_until, 718 cert_info->issuer_dname, 719 cert_info->fingerprint); 720 svn_stringbuf_appendstr(buf, msg); 721 722 if (may_save) 723 { 724 svn_stringbuf_appendcstr 725 (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? ")); 726 } 727 else 728 { 729 svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? ")); 730 } 731 SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool)); 732 733 if (choice[0] == 't' || choice[0] == 'T') 734 { 735 *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 736 (*cred_p)->may_save = FALSE; 737 (*cred_p)->accepted_failures = failures; 738 } 739 else if (may_save && (choice[0] == 'p' || choice[0] == 'P')) 740 { 741 *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 742 (*cred_p)->may_save = TRUE; 743 (*cred_p)->accepted_failures = failures; 744 } 745 else 746 { 747 *cred_p = NULL; 748 } 749 750 return SVN_NO_ERROR; 751} 752 753 754/* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */ 755svn_error_t * 756svn_cmdline_auth_ssl_client_cert_prompt 757 (svn_auth_cred_ssl_client_cert_t **cred_p, 758 void *baton, 759 const char *realm, 760 svn_boolean_t may_save, 761 apr_pool_t *pool) 762{ 763 svn_auth_cred_ssl_client_cert_t *cred = NULL; 764 const char *cert_file = NULL; 765 const char *abs_cert_file = NULL; 766 svn_cmdline_prompt_baton2_t *pb = baton; 767 768 SVN_ERR(maybe_print_realm(realm, pool)); 769 SVN_ERR(prompt(&cert_file, _("Client certificate filename: "), 770 FALSE, pb, pool)); 771 SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool)); 772 773 cred = apr_palloc(pool, sizeof(*cred)); 774 cred->cert_file = abs_cert_file; 775 cred->may_save = may_save; 776 *cred_p = cred; 777 778 return SVN_NO_ERROR; 779} 780 781 782/* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ 783svn_error_t * 784svn_cmdline_auth_ssl_client_cert_pw_prompt 785 (svn_auth_cred_ssl_client_cert_pw_t **cred_p, 786 void *baton, 787 const char *realm, 788 svn_boolean_t may_save, 789 apr_pool_t *pool) 790{ 791 svn_auth_cred_ssl_client_cert_pw_t *cred = NULL; 792 const char *result; 793 const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm); 794 svn_cmdline_prompt_baton2_t *pb = baton; 795 796 SVN_ERR(prompt(&result, text, TRUE, pb, pool)); 797 798 cred = apr_pcalloc(pool, sizeof(*cred)); 799 cred->password = result; 800 cred->may_save = may_save; 801 *cred_p = cred; 802 803 return SVN_NO_ERROR; 804} 805 806/* This is a helper for plaintext prompt functions. */ 807static svn_error_t * 808plaintext_prompt_helper(svn_boolean_t *may_save_plaintext, 809 const char *realmstring, 810 const char *prompt_string, 811 const char *prompt_text, 812 void *baton, 813 apr_pool_t *pool) 814{ 815 const char *answer = NULL; 816 svn_boolean_t answered = FALSE; 817 svn_cmdline_prompt_baton2_t *pb = baton; 818 const char *config_path = NULL; 819 terminal_handle_t *terminal; 820 821 if (pb) 822 SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir, 823 SVN_CONFIG_CATEGORY_SERVERS, pool)); 824 825 SVN_ERR(terminal_open(&terminal, FALSE, pool)); 826 SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text, 827 realmstring, config_path), 828 terminal, pool)); 829 SVN_ERR(terminal_close(terminal)); 830 831 do 832 { 833 svn_error_t *err = prompt(&answer, prompt_string, FALSE, pb, pool); 834 if (err) 835 { 836 if (err->apr_err == SVN_ERR_CANCELLED) 837 { 838 svn_error_clear(err); 839 *may_save_plaintext = FALSE; 840 return SVN_NO_ERROR; 841 } 842 else 843 return err; 844 } 845 if (apr_strnatcasecmp(answer, _("yes")) == 0 || 846 apr_strnatcasecmp(answer, _("y")) == 0) 847 { 848 *may_save_plaintext = TRUE; 849 answered = TRUE; 850 } 851 else if (apr_strnatcasecmp(answer, _("no")) == 0 || 852 apr_strnatcasecmp(answer, _("n")) == 0) 853 { 854 *may_save_plaintext = FALSE; 855 answered = TRUE; 856 } 857 else 858 prompt_string = _("Please type 'yes' or 'no': "); 859 } 860 while (! answered); 861 862 return SVN_NO_ERROR; 863} 864 865/* This implements 'svn_auth_plaintext_prompt_func_t'. */ 866svn_error_t * 867svn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext, 868 const char *realmstring, 869 void *baton, 870 apr_pool_t *pool) 871{ 872 const char *prompt_string = _("Store password unencrypted (yes/no)? "); 873 const char *prompt_text = 874 _("\n-----------------------------------------------------------------------" 875 "\nATTENTION! Your password for authentication realm:\n" 876 "\n" 877 " %s\n" 878 "\n" 879 "can only be stored to disk unencrypted! You are advised to configure\n" 880 "your system so that Subversion can store passwords encrypted, if\n" 881 "possible. See the documentation for details.\n" 882 "\n" 883 "You can avoid future appearances of this warning by setting the value\n" 884 "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n" 885 "'%s'.\n" 886 "-----------------------------------------------------------------------\n" 887 ); 888 889 return plaintext_prompt_helper(may_save_plaintext, realmstring, 890 prompt_string, prompt_text, baton, 891 pool); 892} 893 894/* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */ 895svn_error_t * 896svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, 897 const char *realmstring, 898 void *baton, 899 apr_pool_t *pool) 900{ 901 const char *prompt_string = _("Store passphrase unencrypted (yes/no)? "); 902 const char *prompt_text = 903 _("\n-----------------------------------------------------------------------\n" 904 "ATTENTION! Your passphrase for client certificate:\n" 905 "\n" 906 " %s\n" 907 "\n" 908 "can only be stored to disk unencrypted! You are advised to configure\n" 909 "your system so that Subversion can store passphrase encrypted, if\n" 910 "possible. See the documentation for details.\n" 911 "\n" 912 "You can avoid future appearances of this warning by setting the value\n" 913 "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n" 914 "'no' in '%s'.\n" 915 "-----------------------------------------------------------------------\n" 916 ); 917 918 return plaintext_prompt_helper(may_save_plaintext, realmstring, 919 prompt_string, prompt_text, baton, 920 pool); 921} 922 923 924/** Generic prompting. **/ 925 926svn_error_t * 927svn_cmdline_prompt_user2(const char **result, 928 const char *prompt_str, 929 svn_cmdline_prompt_baton_t *baton, 930 apr_pool_t *pool) 931{ 932 /* XXX: We know prompt doesn't use the new members 933 * of svn_cmdline_prompt_baton2_t. */ 934 return prompt(result, prompt_str, FALSE /* don't hide input */, 935 (svn_cmdline_prompt_baton2_t *)baton, pool); 936} 937 938/* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */ 939svn_error_t * 940svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password, 941 const char *keyring_name, 942 void *baton, 943 apr_pool_t *pool) 944{ 945 const char *password; 946 const char *pass_prompt; 947 svn_cmdline_prompt_baton2_t *pb = baton; 948 949 pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "), 950 keyring_name); 951 SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool)); 952 *keyring_password = apr_pstrdup(pool, password); 953 return SVN_NO_ERROR; 954} 955