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_FOPEN_READ | APR_FOPEN_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 const char *converted; 240 241 err = svn_cmdline_cstring_from_utf8(&converted, string, pool); 242 if (err) 243 { 244 svn_error_clear(err); 245 converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); 246 } 247 248#ifdef WIN32 249 if (!terminal->outfd) 250 { 251 /* See terminal_open; we're using Console I/O. */ 252 _cputs(converted); 253 return SVN_NO_ERROR; 254 } 255#endif 256 257 SVN_ERR(svn_io_file_write_full(terminal->outfd, converted, 258 strlen(converted), NULL, pool)); 259 260 return svn_error_trace(svn_io_file_flush(terminal->outfd, pool)); 261} 262 263/* These codes can be returned from terminal_getc instead of a character. */ 264#define TERMINAL_NONE 0x80000 /* no character read, retry */ 265#define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */ 266#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */ 267#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */ 268 269/* Helper for terminal_getc: writes CH to OUTFD as a control char. */ 270#ifndef WIN32 271static void 272echo_control_char(char ch, apr_file_t *outfd) 273{ 274 if (svn_ctype_iscntrl(ch)) 275 { 276 const char substitute = (ch < 32? '@' + ch : '?'); 277 apr_file_putc('^', outfd); 278 apr_file_putc(substitute, outfd); 279 } 280 else if (svn_ctype_isprint(ch)) 281 { 282 /* Pass printable characters unchanged. */ 283 apr_file_putc(ch, outfd); 284 } 285 else 286 { 287 /* Everything else is strange. */ 288 apr_file_putc('^', outfd); 289 apr_file_putc('!', outfd); 290 } 291} 292#endif /* WIN32 */ 293 294/* Read one character or control code from TERMINAL, returning it in CODE. 295 if CAN_ERASE and the input was a deletion, emit codes to erase the 296 last character displayed on the terminal. 297 Use POOL for all allocations. */ 298static svn_error_t * 299terminal_getc(int *code, terminal_handle_t *terminal, 300 svn_boolean_t can_erase, apr_pool_t *pool) 301{ 302 const svn_boolean_t echo = !terminal->noecho; 303 apr_status_t status = APR_SUCCESS; 304 char ch; 305 306#ifdef WIN32 307 if (!terminal->infd) 308 { 309 /* See terminal_open; we're using Console I/O. */ 310 311 /* The following was hoisted from APR's getpass for Windows. */ 312 int concode = _getch(); 313 switch (concode) 314 { 315 case '\r': /* end-of-line */ 316 *code = TERMINAL_EOL; 317 if (echo) 318 _cputs("\r\n"); 319 break; 320 321 case EOF: /* end-of-file */ 322 case 26: /* Ctrl+Z */ 323 *code = TERMINAL_EOF; 324 if (echo) 325 _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n")); 326 break; 327 328 case 3: /* Ctrl+C, Ctrl+Break */ 329 /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ 330 if (echo) 331 _cputs("^C\r\n"); 332 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 333 334 case 0: /* Function code prefix */ 335 case 0xE0: 336 concode = (concode << 4) | _getch(); 337 /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ 338 if (concode == 0xE53 || concode == 0xE4B 339 || concode == 0x053 || concode == 0x04B) 340 { 341 *code = TERMINAL_DEL; 342 if (can_erase) 343 _cputs("\b \b"); 344 } 345 else 346 { 347 *code = TERMINAL_NONE; 348 _putch('\a'); 349 } 350 break; 351 352 case '\b': /* BS */ 353 case 127: /* DEL */ 354 *code = TERMINAL_DEL; 355 if (can_erase) 356 _cputs("\b \b"); 357 break; 358 359 default: 360 if (!apr_iscntrl(concode)) 361 { 362 *code = (int)(unsigned char)concode; 363 _putch(echo ? concode : '*'); 364 } 365 else 366 { 367 *code = TERMINAL_NONE; 368 _putch('\a'); 369 } 370 } 371 return SVN_NO_ERROR; 372 } 373#elif defined(HAVE_TERMIOS_H) 374 if (terminal->restore_state) 375 { 376 /* We're using a bytewise-immediate termios input */ 377 const struct termios *const attr = &terminal->attr; 378 379 status = apr_file_getc(&ch, terminal->infd); 380 if (status) 381 return svn_error_wrap_apr(status, _("Can't read from terminal")); 382 383 if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT]) 384 { 385 /* Break */ 386 echo_control_char(ch, terminal->outfd); 387 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 388 } 389 else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL]) 390 { 391 /* Newline */ 392 *code = TERMINAL_EOL; 393 apr_file_putc('\n', terminal->outfd); 394 } 395 else if (ch == '\b' || ch == attr->c_cc[VERASE]) 396 { 397 /* Delete */ 398 *code = TERMINAL_DEL; 399 if (can_erase) 400 { 401 apr_file_putc('\b', terminal->outfd); 402 apr_file_putc(' ', terminal->outfd); 403 apr_file_putc('\b', terminal->outfd); 404 } 405 } 406 else if (ch == attr->c_cc[VEOF]) 407 { 408 /* End of input */ 409 *code = TERMINAL_EOF; 410 echo_control_char(ch, terminal->outfd); 411 } 412 else if (ch == attr->c_cc[VSUSP]) 413 { 414 /* Suspend */ 415 *code = TERMINAL_NONE; 416 kill(0, SIGTSTP); 417 } 418 else if (!apr_iscntrl(ch)) 419 { 420 /* Normal character */ 421 *code = (int)(unsigned char)ch; 422 apr_file_putc((echo ? ch : '*'), terminal->outfd); 423 } 424 else 425 { 426 /* Ignored character */ 427 *code = TERMINAL_NONE; 428 apr_file_putc('\a', terminal->outfd); 429 } 430 return SVN_NO_ERROR; 431 } 432#endif /* HAVE_TERMIOS_H */ 433 434 /* Fall back to plain stream-based I/O. */ 435#ifndef WIN32 436 /* Wait for input on termin. This code is based on 437 apr_wait_for_io_or_timeout(). 438 Note that this will return an EINTR on a signal. */ 439 { 440 apr_pollfd_t pollset; 441 int n; 442 443 pollset.desc_type = APR_POLL_FILE; 444 pollset.desc.f = terminal->infd; 445 pollset.p = pool; 446 pollset.reqevents = APR_POLLIN; 447 448 status = apr_poll(&pollset, 1, &n, -1); 449 450 if (n == 1 && pollset.rtnevents & APR_POLLIN) 451 status = APR_SUCCESS; 452 } 453#endif /* !WIN32 */ 454 455 if (!status) 456 status = apr_file_getc(&ch, terminal->infd); 457 if (APR_STATUS_IS_EINTR(status)) 458 { 459 *code = TERMINAL_NONE; 460 return SVN_NO_ERROR; 461 } 462 else if (APR_STATUS_IS_EOF(status)) 463 { 464 *code = TERMINAL_EOF; 465 return SVN_NO_ERROR; 466 } 467 else if (status) 468 return svn_error_wrap_apr(status, _("Can't read from terminal")); 469 470 *code = (int)(unsigned char)ch; 471 return SVN_NO_ERROR; 472} 473 474 475/* Set @a *result to the result of prompting the user with @a 476 * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton. 477 * Do not call the cancel_func if @a *pb is NULL. 478 * Allocate @a *result in @a pool. 479 * 480 * If @a hide is true, then try to avoid displaying the user's input. 481 */ 482static svn_error_t * 483prompt(const char **result, 484 const char *prompt_msg, 485 svn_boolean_t hide, 486 svn_cmdline_prompt_baton2_t *pb, 487 apr_pool_t *pool) 488{ 489 /* XXX: If this functions ever starts using members of *pb 490 * which were not included in svn_cmdline_prompt_baton_t, 491 * we need to update svn_cmdline_prompt_user2 and its callers. */ 492 493 svn_boolean_t saw_first_half_of_eol = FALSE; 494 svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool); 495 terminal_handle_t *terminal; 496 int code; 497 char c; 498 499 SVN_ERR(terminal_open(&terminal, hide, pool)); 500 SVN_ERR(terminal_puts(prompt_msg, terminal, pool)); 501 502 while (1) 503 { 504 SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool)); 505 506 /* Check for cancellation after a character has been read, some 507 input processing modes may eat ^C and we'll only notice a 508 cancellation signal after characters have been read -- 509 sometimes even after a newline. */ 510 if (pb) 511 SVN_ERR(pb->cancel_func(pb->cancel_baton)); 512 513 switch (code) 514 { 515 case TERMINAL_NONE: 516 /* Nothing useful happened; retry. */ 517 continue; 518 519 case TERMINAL_DEL: 520 /* Delete the last input character. terminal_getc takes care 521 of erasing the feedback from the terminal, if applicable. */ 522 svn_stringbuf_chop(strbuf, 1); 523 continue; 524 525 case TERMINAL_EOL: 526 /* End-of-line means end of input. Trick the EOL-detection code 527 below to stop reading. */ 528 saw_first_half_of_eol = TRUE; 529 c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */ 530 break; 531 532 case TERMINAL_EOF: 533 return svn_error_create( 534 APR_EOF, 535 terminal_close(terminal), 536 _("End of file while reading from terminal")); 537 538 default: 539 /* Convert the returned code back to the character. */ 540 c = (char)code; 541 } 542 543 if (saw_first_half_of_eol) 544 { 545 if (c == APR_EOL_STR[1]) 546 break; 547 else 548 saw_first_half_of_eol = FALSE; 549 } 550 else if (c == APR_EOL_STR[0]) 551 { 552 /* GCC might complain here: "warning: will never be executed" 553 * That's fine. This is a compile-time check for "\r\n\0" */ 554 if (sizeof(APR_EOL_STR) == 3) 555 { 556 saw_first_half_of_eol = TRUE; 557 continue; 558 } 559 else if (sizeof(APR_EOL_STR) == 2) 560 break; 561 else 562 /* ### APR_EOL_STR holds more than two chars? Who 563 ever heard of such a thing? */ 564 SVN_ERR_MALFUNCTION(); 565 } 566 567 svn_stringbuf_appendbyte(strbuf, c); 568 } 569 570 if (terminal->noecho) 571 { 572 /* If terminal echo was turned off, make sure future output 573 to the terminal starts on a new line, as expected. */ 574 SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool)); 575 } 576 SVN_ERR(terminal_close(terminal)); 577 578 return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool); 579} 580 581 582 583/** Prompt functions for auth providers. **/ 584 585/* Helper function for auth provider prompters: mention the 586 * authentication @a realm on stderr, in a manner appropriate for 587 * preceding a prompt; or if @a realm is null, then do nothing. 588 */ 589static svn_error_t * 590maybe_print_realm(const char *realm, apr_pool_t *pool) 591{ 592 if (realm) 593 { 594 terminal_handle_t *terminal; 595 SVN_ERR(terminal_open(&terminal, FALSE, pool)); 596 SVN_ERR(terminal_puts( 597 apr_psprintf(pool, 598 _("Authentication realm: %s\n"), realm), 599 terminal, pool)); 600 SVN_ERR(terminal_close(terminal)); 601 } 602 603 return SVN_NO_ERROR; 604} 605 606 607/* This implements 'svn_auth_simple_prompt_func_t'. */ 608svn_error_t * 609svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p, 610 void *baton, 611 const char *realm, 612 const char *username, 613 svn_boolean_t may_save, 614 apr_pool_t *pool) 615{ 616 svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret)); 617 const char *pass_prompt; 618 svn_cmdline_prompt_baton2_t *pb = baton; 619 620 SVN_ERR(maybe_print_realm(realm, pool)); 621 622 if (username) 623 ret->username = apr_pstrdup(pool, username); 624 else 625 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 626 627 pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username); 628 SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool)); 629 ret->may_save = may_save; 630 *cred_p = ret; 631 return SVN_NO_ERROR; 632} 633 634 635/* This implements 'svn_auth_username_prompt_func_t'. */ 636svn_error_t * 637svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p, 638 void *baton, 639 const char *realm, 640 svn_boolean_t may_save, 641 apr_pool_t *pool) 642{ 643 svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret)); 644 svn_cmdline_prompt_baton2_t *pb = baton; 645 646 SVN_ERR(maybe_print_realm(realm, pool)); 647 648 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 649 ret->may_save = may_save; 650 *cred_p = ret; 651 return SVN_NO_ERROR; 652} 653 654 655/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */ 656svn_error_t * 657svn_cmdline_auth_ssl_server_trust_prompt 658 (svn_auth_cred_ssl_server_trust_t **cred_p, 659 void *baton, 660 const char *realm, 661 apr_uint32_t failures, 662 const svn_auth_ssl_server_cert_info_t *cert_info, 663 svn_boolean_t may_save, 664 apr_pool_t *pool) 665{ 666 const char *choice; 667 svn_stringbuf_t *msg; 668 svn_cmdline_prompt_baton2_t *pb = baton; 669 svn_stringbuf_t *buf = svn_stringbuf_createf 670 (pool, _("Error validating server certificate for '%s':\n"), realm); 671 672 if (failures & SVN_AUTH_SSL_UNKNOWNCA) 673 { 674 svn_stringbuf_appendcstr 675 (buf, 676 _(" - The certificate is not issued by a trusted authority. Use the\n" 677 " fingerprint to validate the certificate manually!\n")); 678 } 679 680 if (failures & SVN_AUTH_SSL_CNMISMATCH) 681 { 682 svn_stringbuf_appendcstr 683 (buf, _(" - The certificate hostname does not match.\n")); 684 } 685 686 if (failures & SVN_AUTH_SSL_NOTYETVALID) 687 { 688 svn_stringbuf_appendcstr 689 (buf, _(" - The certificate is not yet valid.\n")); 690 } 691 692 if (failures & SVN_AUTH_SSL_EXPIRED) 693 { 694 svn_stringbuf_appendcstr 695 (buf, _(" - The certificate has expired.\n")); 696 } 697 698 if (failures & SVN_AUTH_SSL_OTHER) 699 { 700 svn_stringbuf_appendcstr 701 (buf, _(" - The certificate has an unknown error.\n")); 702 } 703 704 msg = svn_stringbuf_createf 705 (pool, 706 _("Certificate information:\n" 707 " - Hostname: %s\n" 708 " - Valid: from %s until %s\n" 709 " - Issuer: %s\n" 710 " - Fingerprint: %s\n"), 711 cert_info->hostname, 712 cert_info->valid_from, 713 cert_info->valid_until, 714 cert_info->issuer_dname, 715 cert_info->fingerprint); 716 svn_stringbuf_appendstr(buf, msg); 717 718 if (may_save) 719 { 720 svn_stringbuf_appendcstr 721 (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? ")); 722 } 723 else 724 { 725 svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? ")); 726 } 727 SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool)); 728 729 if (choice[0] == 't' || choice[0] == 'T') 730 { 731 *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 732 (*cred_p)->may_save = FALSE; 733 (*cred_p)->accepted_failures = failures; 734 } 735 else if (may_save && (choice[0] == 'p' || choice[0] == 'P')) 736 { 737 *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 738 (*cred_p)->may_save = TRUE; 739 (*cred_p)->accepted_failures = failures; 740 } 741 else 742 { 743 *cred_p = NULL; 744 } 745 746 return SVN_NO_ERROR; 747} 748 749 750/* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */ 751svn_error_t * 752svn_cmdline_auth_ssl_client_cert_prompt 753 (svn_auth_cred_ssl_client_cert_t **cred_p, 754 void *baton, 755 const char *realm, 756 svn_boolean_t may_save, 757 apr_pool_t *pool) 758{ 759 svn_auth_cred_ssl_client_cert_t *cred = NULL; 760 const char *cert_file = NULL; 761 const char *abs_cert_file = NULL; 762 svn_cmdline_prompt_baton2_t *pb = baton; 763 764 SVN_ERR(maybe_print_realm(realm, pool)); 765 SVN_ERR(prompt(&cert_file, _("Client certificate filename: "), 766 FALSE, pb, pool)); 767 SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool)); 768 769 cred = apr_palloc(pool, sizeof(*cred)); 770 cred->cert_file = abs_cert_file; 771 cred->may_save = may_save; 772 *cred_p = cred; 773 774 return SVN_NO_ERROR; 775} 776 777 778/* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ 779svn_error_t * 780svn_cmdline_auth_ssl_client_cert_pw_prompt 781 (svn_auth_cred_ssl_client_cert_pw_t **cred_p, 782 void *baton, 783 const char *realm, 784 svn_boolean_t may_save, 785 apr_pool_t *pool) 786{ 787 svn_auth_cred_ssl_client_cert_pw_t *cred = NULL; 788 const char *result; 789 const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm); 790 svn_cmdline_prompt_baton2_t *pb = baton; 791 792 SVN_ERR(prompt(&result, text, TRUE, pb, pool)); 793 794 cred = apr_pcalloc(pool, sizeof(*cred)); 795 cred->password = result; 796 cred->may_save = may_save; 797 *cred_p = cred; 798 799 return SVN_NO_ERROR; 800} 801 802/* This is a helper for plaintext prompt functions. */ 803static svn_error_t * 804plaintext_prompt_helper(svn_boolean_t *may_save_plaintext, 805 const char *realmstring, 806 const char *prompt_string, 807 const char *prompt_text, 808 void *baton, 809 apr_pool_t *pool) 810{ 811 const char *answer = NULL; 812 svn_boolean_t answered = FALSE; 813 svn_cmdline_prompt_baton2_t *pb = baton; 814 const char *config_path = NULL; 815 terminal_handle_t *terminal; 816 817 *may_save_plaintext = FALSE; /* de facto API promise */ 818 819 if (pb) 820 SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir, 821 SVN_CONFIG_CATEGORY_SERVERS, pool)); 822 823 SVN_ERR(terminal_open(&terminal, FALSE, pool)); 824 SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text, 825 realmstring, config_path), 826 terminal, pool)); 827 SVN_ERR(terminal_close(terminal)); 828 829 do 830 { 831 SVN_ERR(prompt(&answer, prompt_string, FALSE, pb, pool)); 832 if (apr_strnatcasecmp(answer, _("yes")) == 0 || 833 apr_strnatcasecmp(answer, _("y")) == 0) 834 { 835 *may_save_plaintext = TRUE; 836 answered = TRUE; 837 } 838 else if (apr_strnatcasecmp(answer, _("no")) == 0 || 839 apr_strnatcasecmp(answer, _("n")) == 0) 840 { 841 *may_save_plaintext = FALSE; 842 answered = TRUE; 843 } 844 else 845 prompt_string = _("Please type 'yes' or 'no': "); 846 } 847 while (! answered); 848 849 return SVN_NO_ERROR; 850} 851 852/* This implements 'svn_auth_plaintext_prompt_func_t'. */ 853svn_error_t * 854svn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext, 855 const char *realmstring, 856 void *baton, 857 apr_pool_t *pool) 858{ 859 const char *prompt_string = _("Store password unencrypted (yes/no)? "); 860 const char *prompt_text = 861 _("\n-----------------------------------------------------------------------" 862 "\nATTENTION! Your password for authentication realm:\n" 863 "\n" 864 " %s\n" 865 "\n" 866 "can only be stored to disk unencrypted! You are advised to configure\n" 867 "your system so that Subversion can store passwords encrypted, if\n" 868 "possible. See the documentation for details.\n" 869 "\n" 870 "You can avoid future appearances of this warning by setting the value\n" 871 "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n" 872 "'%s'.\n" 873 "-----------------------------------------------------------------------\n" 874 ); 875 876 return plaintext_prompt_helper(may_save_plaintext, realmstring, 877 prompt_string, prompt_text, baton, 878 pool); 879} 880 881/* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */ 882svn_error_t * 883svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, 884 const char *realmstring, 885 void *baton, 886 apr_pool_t *pool) 887{ 888 const char *prompt_string = _("Store passphrase unencrypted (yes/no)? "); 889 const char *prompt_text = 890 _("\n-----------------------------------------------------------------------\n" 891 "ATTENTION! Your passphrase for client certificate:\n" 892 "\n" 893 " %s\n" 894 "\n" 895 "can only be stored to disk unencrypted! You are advised to configure\n" 896 "your system so that Subversion can store passphrase encrypted, if\n" 897 "possible. See the documentation for details.\n" 898 "\n" 899 "You can avoid future appearances of this warning by setting the value\n" 900 "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n" 901 "'no' in '%s'.\n" 902 "-----------------------------------------------------------------------\n" 903 ); 904 905 return plaintext_prompt_helper(may_save_plaintext, realmstring, 906 prompt_string, prompt_text, baton, 907 pool); 908} 909 910 911/** Generic prompting. **/ 912 913svn_error_t * 914svn_cmdline_prompt_user2(const char **result, 915 const char *prompt_str, 916 svn_cmdline_prompt_baton_t *baton, 917 apr_pool_t *pool) 918{ 919 /* XXX: We know prompt doesn't use the new members 920 * of svn_cmdline_prompt_baton2_t. */ 921 return prompt(result, prompt_str, FALSE /* don't hide input */, 922 (svn_cmdline_prompt_baton2_t *)baton, pool); 923} 924 925/* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */ 926svn_error_t * 927svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password, 928 const char *keyring_name, 929 void *baton, 930 apr_pool_t *pool) 931{ 932 const char *password; 933 const char *pass_prompt; 934 svn_cmdline_prompt_baton2_t *pb = baton; 935 936 pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "), 937 keyring_name); 938 SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool)); 939 *keyring_password = apr_pstrdup(pool, password); 940 return SVN_NO_ERROR; 941} 942