1251881Speter/* 2251881Speter * prompt.c -- ask the user for authentication information. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include <apr_lib.h> 31251881Speter#include <apr_poll.h> 32251881Speter#include <apr_portable.h> 33251881Speter 34251881Speter#include "svn_cmdline.h" 35251881Speter#include "svn_ctype.h" 36251881Speter#include "svn_string.h" 37251881Speter#include "svn_auth.h" 38251881Speter#include "svn_error.h" 39251881Speter#include "svn_path.h" 40251881Speter 41251881Speter#include "private/svn_cmdline_private.h" 42251881Speter#include "svn_private_config.h" 43251881Speter 44251881Speter#ifdef WIN32 45251881Speter#include <conio.h> 46251881Speter#elif defined(HAVE_TERMIOS_H) 47251881Speter#include <signal.h> 48251881Speter#include <termios.h> 49251881Speter#endif 50251881Speter 51251881Speter 52251881Speter 53251881Speter/* Descriptor of an open terminal */ 54251881Spetertypedef struct terminal_handle_t terminal_handle_t; 55251881Speterstruct terminal_handle_t 56251881Speter{ 57251881Speter apr_file_t *infd; /* input file handle */ 58251881Speter apr_file_t *outfd; /* output file handle */ 59251881Speter svn_boolean_t noecho; /* terminal echo was turned off */ 60251881Speter svn_boolean_t close_handles; /* close handles when closing the terminal */ 61251881Speter apr_pool_t *pool; /* pool associated with the file handles */ 62251881Speter 63251881Speter#ifdef HAVE_TERMIOS_H 64251881Speter svn_boolean_t restore_state; /* terminal state was changed */ 65251881Speter apr_os_file_t osinfd; /* OS-specific handle for infd */ 66251881Speter struct termios attr; /* saved terminal attributes */ 67251881Speter#endif 68251881Speter}; 69251881Speter 70251881Speter/* Initialize safe state of terminal_handle_t. */ 71251881Speterstatic void 72251881Speterterminal_handle_init(terminal_handle_t *terminal, 73251881Speter apr_file_t *infd, apr_file_t *outfd, 74251881Speter svn_boolean_t noecho, svn_boolean_t close_handles, 75251881Speter apr_pool_t *pool) 76251881Speter{ 77251881Speter memset(terminal, 0, sizeof(*terminal)); 78251881Speter terminal->infd = infd; 79251881Speter terminal->outfd = outfd; 80251881Speter terminal->noecho = noecho; 81251881Speter terminal->close_handles = close_handles; 82251881Speter terminal->pool = pool; 83251881Speter} 84251881Speter 85251881Speter/* 86251881Speter * Common pool cleanup handler for terminal_handle_t. Closes TERMINAL. 87251881Speter * If CLOSE_HANDLES is TRUE, close the terminal file handles. 88251881Speter * If RESTORE_STATE is TRUE, restores the TERMIOS flags of the terminal. 89251881Speter */ 90251881Speterstatic apr_status_t 91251881Speterterminal_cleanup_handler(terminal_handle_t *terminal, 92251881Speter svn_boolean_t close_handles, 93251881Speter svn_boolean_t restore_state) 94251881Speter{ 95251881Speter apr_status_t status = APR_SUCCESS; 96251881Speter 97251881Speter#ifdef HAVE_TERMIOS_H 98251881Speter /* Restore terminal state flags. */ 99251881Speter if (restore_state && terminal->restore_state) 100251881Speter tcsetattr(terminal->osinfd, TCSANOW, &terminal->attr); 101251881Speter#endif 102251881Speter 103251881Speter /* Close terminal handles. */ 104251881Speter if (close_handles && terminal->close_handles) 105251881Speter { 106251881Speter apr_file_t *const infd = terminal->infd; 107251881Speter apr_file_t *const outfd = terminal->outfd; 108251881Speter 109251881Speter if (infd) 110251881Speter { 111251881Speter terminal->infd = NULL; 112251881Speter status = apr_file_close(infd); 113251881Speter } 114251881Speter 115251881Speter if (!status && outfd && outfd != infd) 116251881Speter { 117251881Speter terminal->outfd = NULL; 118251881Speter status = apr_file_close(terminal->outfd); 119251881Speter } 120251881Speter } 121251881Speter return status; 122251881Speter} 123251881Speter 124251881Speter/* Normal pool cleanup for a terminal. */ 125251881Speterstatic apr_status_t terminal_plain_cleanup(void *baton) 126251881Speter{ 127251881Speter return terminal_cleanup_handler(baton, FALSE, TRUE); 128251881Speter} 129251881Speter 130251881Speter/* Child pool cleanup for a terminal -- does not restore echo state. */ 131251881Speterstatic apr_status_t terminal_child_cleanup(void *baton) 132251881Speter{ 133251881Speter return terminal_cleanup_handler(baton, FALSE, FALSE); 134251881Speter} 135251881Speter 136251881Speter/* Explicitly close the terminal, removing its cleanup handlers. */ 137251881Speterstatic svn_error_t * 138251881Speterterminal_close(terminal_handle_t *terminal) 139251881Speter{ 140251881Speter apr_status_t status; 141251881Speter 142251881Speter /* apr_pool_cleanup_kill() removes both normal and child cleanup */ 143251881Speter apr_pool_cleanup_kill(terminal->pool, terminal, terminal_plain_cleanup); 144251881Speter 145251881Speter status = terminal_cleanup_handler(terminal, TRUE, TRUE); 146251881Speter if (status) 147251881Speter return svn_error_create(status, NULL, _("Can't close terminal")); 148251881Speter return SVN_NO_ERROR; 149251881Speter} 150251881Speter 151251881Speter/* Allocate and open *TERMINAL. If NOECHO is TRUE, try to turn off 152251881Speter terminal echo. Use POOL for all allocations.*/ 153251881Speterstatic svn_error_t * 154251881Speterterminal_open(terminal_handle_t **terminal, svn_boolean_t noecho, 155251881Speter apr_pool_t *pool) 156251881Speter{ 157251881Speter apr_status_t status; 158251881Speter 159251881Speter#ifdef WIN32 160251881Speter /* On Windows, we'll use the console API directly if the process has 161251881Speter a console attached; otherwise we'll just use stdin and stderr. */ 162251881Speter const HANDLE conin = CreateFileW(L"CONIN$", GENERIC_READ, 163251881Speter FILE_SHARE_READ | FILE_SHARE_WRITE, 164251881Speter NULL, OPEN_EXISTING, 165251881Speter FILE_ATTRIBUTE_NORMAL, NULL); 166251881Speter *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); 167251881Speter if (conin != INVALID_HANDLE_VALUE) 168251881Speter { 169251881Speter /* The process has a console. */ 170251881Speter CloseHandle(conin); 171251881Speter terminal_handle_init(*terminal, NULL, NULL, noecho, FALSE, NULL); 172251881Speter return SVN_NO_ERROR; 173251881Speter } 174251881Speter#else /* !WIN32 */ 175251881Speter /* Without evidence to the contrary, we'll assume this is *nix and 176251881Speter try to open /dev/tty. If that fails, we'll use stdin for input 177251881Speter and stderr for prompting. */ 178251881Speter apr_file_t *tmpfd; 179251881Speter status = apr_file_open(&tmpfd, "/dev/tty", 180251881Speter APR_FOPEN_READ | APR_FOPEN_WRITE, 181251881Speter APR_OS_DEFAULT, pool); 182251881Speter *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); 183251881Speter if (!status) 184251881Speter { 185251881Speter /* We have a terminal handle that we can use for input and output. */ 186251881Speter terminal_handle_init(*terminal, tmpfd, tmpfd, FALSE, TRUE, pool); 187251881Speter } 188251881Speter#endif /* !WIN32 */ 189251881Speter else 190251881Speter { 191251881Speter /* There is no terminal. Sigh. */ 192251881Speter apr_file_t *infd; 193251881Speter apr_file_t *outfd; 194251881Speter 195251881Speter status = apr_file_open_stdin(&infd, pool); 196251881Speter if (status) 197251881Speter return svn_error_wrap_apr(status, _("Can't open stdin")); 198251881Speter status = apr_file_open_stderr(&outfd, pool); 199251881Speter if (status) 200251881Speter return svn_error_wrap_apr(status, _("Can't open stderr")); 201251881Speter terminal_handle_init(*terminal, infd, outfd, FALSE, FALSE, pool); 202251881Speter } 203251881Speter 204251881Speter#ifdef HAVE_TERMIOS_H 205251881Speter /* Set terminal state */ 206251881Speter if (0 == apr_os_file_get(&(*terminal)->osinfd, (*terminal)->infd)) 207251881Speter { 208251881Speter if (0 == tcgetattr((*terminal)->osinfd, &(*terminal)->attr)) 209251881Speter { 210251881Speter struct termios attr = (*terminal)->attr; 211251881Speter /* Turn off signal handling and canonical input mode */ 212251881Speter attr.c_lflag &= ~(ISIG | ICANON); 213251881Speter attr.c_cc[VMIN] = 1; /* Read one byte at a time */ 214251881Speter attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */ 215251881Speter attr.c_lflag &= ~(ECHO); /* Turn off echo */ 216251881Speter if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr)) 217251881Speter { 218251881Speter (*terminal)->noecho = noecho; 219251881Speter (*terminal)->restore_state = TRUE; 220251881Speter } 221251881Speter } 222251881Speter } 223251881Speter#endif /* HAVE_TERMIOS_H */ 224251881Speter 225251881Speter /* Register pool cleanup to close handles and restore echo state. */ 226251881Speter apr_pool_cleanup_register((*terminal)->pool, *terminal, 227251881Speter terminal_plain_cleanup, 228251881Speter terminal_child_cleanup); 229251881Speter return SVN_NO_ERROR; 230251881Speter} 231251881Speter 232251881Speter/* Write a null-terminated STRING to TERMINAL. 233251881Speter Use POOL for allocations related to converting STRING from UTF-8. */ 234251881Speterstatic svn_error_t * 235251881Speterterminal_puts(const char *string, terminal_handle_t *terminal, 236251881Speter apr_pool_t *pool) 237251881Speter{ 238251881Speter svn_error_t *err; 239251881Speter apr_status_t status; 240251881Speter const char *converted; 241251881Speter 242251881Speter err = svn_cmdline_cstring_from_utf8(&converted, string, pool); 243251881Speter if (err) 244251881Speter { 245251881Speter svn_error_clear(err); 246251881Speter converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); 247251881Speter } 248251881Speter 249251881Speter#ifdef WIN32 250251881Speter if (!terminal->outfd) 251251881Speter { 252251881Speter /* See terminal_open; we're using Console I/O. */ 253251881Speter _cputs(converted); 254251881Speter return SVN_NO_ERROR; 255251881Speter } 256251881Speter#endif 257251881Speter 258251881Speter status = apr_file_write_full(terminal->outfd, converted, 259251881Speter strlen(converted), NULL); 260251881Speter if (!status) 261251881Speter status = apr_file_flush(terminal->outfd); 262251881Speter if (status) 263251881Speter return svn_error_wrap_apr(status, _("Can't write to terminal")); 264251881Speter return SVN_NO_ERROR; 265251881Speter} 266251881Speter 267251881Speter/* These codes can be returned from terminal_getc instead of a character. */ 268251881Speter#define TERMINAL_NONE 0x80000 /* no character read, retry */ 269251881Speter#define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */ 270251881Speter#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */ 271251881Speter#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */ 272251881Speter 273251881Speter/* Helper for terminal_getc: writes CH to OUTFD as a control char. */ 274251881Speter#ifndef WIN32 275251881Speterstatic void 276251881Speterecho_control_char(char ch, apr_file_t *outfd) 277251881Speter{ 278251881Speter if (svn_ctype_iscntrl(ch)) 279251881Speter { 280251881Speter const char substitute = (ch < 32? '@' + ch : '?'); 281251881Speter apr_file_putc('^', outfd); 282251881Speter apr_file_putc(substitute, outfd); 283251881Speter } 284251881Speter else if (svn_ctype_isprint(ch)) 285251881Speter { 286251881Speter /* Pass printable characters unchanged. */ 287251881Speter apr_file_putc(ch, outfd); 288251881Speter } 289251881Speter else 290251881Speter { 291251881Speter /* Everything else is strange. */ 292251881Speter apr_file_putc('^', outfd); 293251881Speter apr_file_putc('!', outfd); 294251881Speter } 295251881Speter} 296251881Speter#endif /* WIN32 */ 297251881Speter 298251881Speter/* Read one character or control code from TERMINAL, returning it in CODE. 299251881Speter if CAN_ERASE and the input was a deletion, emit codes to erase the 300251881Speter last character displayed on the terminal. 301251881Speter Use POOL for all allocations. */ 302251881Speterstatic svn_error_t * 303251881Speterterminal_getc(int *code, terminal_handle_t *terminal, 304251881Speter svn_boolean_t can_erase, apr_pool_t *pool) 305251881Speter{ 306251881Speter const svn_boolean_t echo = !terminal->noecho; 307251881Speter apr_status_t status = APR_SUCCESS; 308251881Speter char ch; 309251881Speter 310251881Speter#ifdef WIN32 311251881Speter if (!terminal->infd) 312251881Speter { 313251881Speter /* See terminal_open; we're using Console I/O. */ 314251881Speter 315251881Speter /* The following was hoisted from APR's getpass for Windows. */ 316251881Speter int concode = _getch(); 317251881Speter switch (concode) 318251881Speter { 319251881Speter case '\r': /* end-of-line */ 320251881Speter *code = TERMINAL_EOL; 321251881Speter if (echo) 322251881Speter _cputs("\r\n"); 323251881Speter break; 324251881Speter 325251881Speter case EOF: /* end-of-file */ 326251881Speter case 26: /* Ctrl+Z */ 327251881Speter *code = TERMINAL_EOF; 328251881Speter if (echo) 329251881Speter _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n")); 330251881Speter break; 331251881Speter 332251881Speter case 3: /* Ctrl+C, Ctrl+Break */ 333251881Speter /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ 334251881Speter if (echo) 335251881Speter _cputs("^C\r\n"); 336251881Speter return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 337251881Speter 338251881Speter case 0: /* Function code prefix */ 339251881Speter case 0xE0: 340251881Speter concode = (concode << 4) | _getch(); 341251881Speter /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ 342251881Speter if (concode == 0xE53 || concode == 0xE4B 343251881Speter || concode == 0x053 || concode == 0x04B) 344251881Speter { 345251881Speter *code = TERMINAL_DEL; 346251881Speter if (can_erase) 347251881Speter _cputs("\b \b"); 348251881Speter } 349251881Speter else 350251881Speter { 351251881Speter *code = TERMINAL_NONE; 352251881Speter _putch('\a'); 353251881Speter } 354251881Speter break; 355251881Speter 356251881Speter case '\b': /* BS */ 357251881Speter case 127: /* DEL */ 358251881Speter *code = TERMINAL_DEL; 359251881Speter if (can_erase) 360251881Speter _cputs("\b \b"); 361251881Speter break; 362251881Speter 363251881Speter default: 364251881Speter if (!apr_iscntrl(concode)) 365251881Speter { 366251881Speter *code = (int)(unsigned char)concode; 367251881Speter _putch(echo ? concode : '*'); 368251881Speter } 369251881Speter else 370251881Speter { 371251881Speter *code = TERMINAL_NONE; 372251881Speter _putch('\a'); 373251881Speter } 374251881Speter } 375251881Speter return SVN_NO_ERROR; 376251881Speter } 377251881Speter#elif defined(HAVE_TERMIOS_H) 378251881Speter if (terminal->restore_state) 379251881Speter { 380251881Speter /* We're using a bytewise-immediate termios input */ 381251881Speter const struct termios *const attr = &terminal->attr; 382251881Speter 383251881Speter status = apr_file_getc(&ch, terminal->infd); 384251881Speter if (status) 385251881Speter return svn_error_wrap_apr(status, _("Can't read from terminal")); 386251881Speter 387251881Speter if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT]) 388251881Speter { 389251881Speter /* Break */ 390251881Speter echo_control_char(ch, terminal->outfd); 391251881Speter return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); 392251881Speter } 393251881Speter else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL]) 394251881Speter { 395251881Speter /* Newline */ 396251881Speter *code = TERMINAL_EOL; 397251881Speter apr_file_putc('\n', terminal->outfd); 398251881Speter } 399251881Speter else if (ch == '\b' || ch == attr->c_cc[VERASE]) 400251881Speter { 401251881Speter /* Delete */ 402251881Speter *code = TERMINAL_DEL; 403251881Speter if (can_erase) 404251881Speter { 405251881Speter apr_file_putc('\b', terminal->outfd); 406251881Speter apr_file_putc(' ', terminal->outfd); 407251881Speter apr_file_putc('\b', terminal->outfd); 408251881Speter } 409251881Speter } 410251881Speter else if (ch == attr->c_cc[VEOF]) 411251881Speter { 412251881Speter /* End of input */ 413251881Speter *code = TERMINAL_EOF; 414251881Speter echo_control_char(ch, terminal->outfd); 415251881Speter } 416251881Speter else if (ch == attr->c_cc[VSUSP]) 417251881Speter { 418251881Speter /* Suspend */ 419251881Speter *code = TERMINAL_NONE; 420251881Speter kill(0, SIGTSTP); 421251881Speter } 422251881Speter else if (!apr_iscntrl(ch)) 423251881Speter { 424251881Speter /* Normal character */ 425251881Speter *code = (int)(unsigned char)ch; 426251881Speter apr_file_putc((echo ? ch : '*'), terminal->outfd); 427251881Speter } 428251881Speter else 429251881Speter { 430251881Speter /* Ignored character */ 431251881Speter *code = TERMINAL_NONE; 432251881Speter apr_file_putc('\a', terminal->outfd); 433251881Speter } 434251881Speter return SVN_NO_ERROR; 435251881Speter } 436251881Speter#endif /* HAVE_TERMIOS_H */ 437251881Speter 438251881Speter /* Fall back to plain stream-based I/O. */ 439251881Speter#ifndef WIN32 440251881Speter /* Wait for input on termin. This code is based on 441251881Speter apr_wait_for_io_or_timeout(). 442251881Speter Note that this will return an EINTR on a signal. */ 443251881Speter { 444251881Speter apr_pollfd_t pollset; 445251881Speter int n; 446251881Speter 447251881Speter pollset.desc_type = APR_POLL_FILE; 448251881Speter pollset.desc.f = terminal->infd; 449251881Speter pollset.p = pool; 450251881Speter pollset.reqevents = APR_POLLIN; 451251881Speter 452251881Speter status = apr_poll(&pollset, 1, &n, -1); 453251881Speter 454251881Speter if (n == 1 && pollset.rtnevents & APR_POLLIN) 455251881Speter status = APR_SUCCESS; 456251881Speter } 457251881Speter#endif /* !WIN32 */ 458251881Speter 459251881Speter if (!status) 460251881Speter status = apr_file_getc(&ch, terminal->infd); 461251881Speter if (APR_STATUS_IS_EINTR(status)) 462251881Speter { 463251881Speter *code = TERMINAL_NONE; 464251881Speter return SVN_NO_ERROR; 465251881Speter } 466251881Speter else if (APR_STATUS_IS_EOF(status)) 467251881Speter { 468251881Speter *code = TERMINAL_EOF; 469251881Speter return SVN_NO_ERROR; 470251881Speter } 471251881Speter else if (status) 472251881Speter return svn_error_wrap_apr(status, _("Can't read from terminal")); 473251881Speter 474251881Speter *code = (int)(unsigned char)ch; 475251881Speter return SVN_NO_ERROR; 476251881Speter} 477251881Speter 478251881Speter 479251881Speter/* Set @a *result to the result of prompting the user with @a 480251881Speter * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton. 481251881Speter * Do not call the cancel_func if @a *pb is NULL. 482251881Speter * Allocate @a *result in @a pool. 483251881Speter * 484251881Speter * If @a hide is true, then try to avoid displaying the user's input. 485251881Speter */ 486251881Speterstatic svn_error_t * 487251881Speterprompt(const char **result, 488251881Speter const char *prompt_msg, 489251881Speter svn_boolean_t hide, 490251881Speter svn_cmdline_prompt_baton2_t *pb, 491251881Speter apr_pool_t *pool) 492251881Speter{ 493251881Speter /* XXX: If this functions ever starts using members of *pb 494251881Speter * which were not included in svn_cmdline_prompt_baton_t, 495251881Speter * we need to update svn_cmdline_prompt_user2 and its callers. */ 496251881Speter 497251881Speter svn_boolean_t saw_first_half_of_eol = FALSE; 498251881Speter svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool); 499251881Speter terminal_handle_t *terminal; 500251881Speter int code; 501251881Speter char c; 502251881Speter 503251881Speter SVN_ERR(terminal_open(&terminal, hide, pool)); 504251881Speter SVN_ERR(terminal_puts(prompt_msg, terminal, pool)); 505251881Speter 506251881Speter while (1) 507251881Speter { 508251881Speter SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool)); 509251881Speter 510251881Speter /* Check for cancellation after a character has been read, some 511251881Speter input processing modes may eat ^C and we'll only notice a 512251881Speter cancellation signal after characters have been read -- 513251881Speter sometimes even after a newline. */ 514251881Speter if (pb) 515251881Speter SVN_ERR(pb->cancel_func(pb->cancel_baton)); 516251881Speter 517251881Speter switch (code) 518251881Speter { 519251881Speter case TERMINAL_NONE: 520251881Speter /* Nothing useful happened; retry. */ 521251881Speter continue; 522251881Speter 523251881Speter case TERMINAL_DEL: 524251881Speter /* Delete the last input character. terminal_getc takes care 525251881Speter of erasing the feedback from the terminal, if applicable. */ 526251881Speter svn_stringbuf_chop(strbuf, 1); 527251881Speter continue; 528251881Speter 529251881Speter case TERMINAL_EOL: 530251881Speter /* End-of-line means end of input. Trick the EOL-detection code 531251881Speter below to stop reading. */ 532251881Speter saw_first_half_of_eol = TRUE; 533251881Speter c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */ 534251881Speter break; 535251881Speter 536251881Speter case TERMINAL_EOF: 537251881Speter return svn_error_create( 538251881Speter APR_EOF, 539251881Speter terminal_close(terminal), 540251881Speter _("End of file while reading from terminal")); 541251881Speter 542251881Speter default: 543251881Speter /* Convert the returned code back to the character. */ 544251881Speter c = (char)code; 545251881Speter } 546251881Speter 547251881Speter if (saw_first_half_of_eol) 548251881Speter { 549251881Speter if (c == APR_EOL_STR[1]) 550251881Speter break; 551251881Speter else 552251881Speter saw_first_half_of_eol = FALSE; 553251881Speter } 554251881Speter else if (c == APR_EOL_STR[0]) 555251881Speter { 556251881Speter /* GCC might complain here: "warning: will never be executed" 557251881Speter * That's fine. This is a compile-time check for "\r\n\0" */ 558251881Speter if (sizeof(APR_EOL_STR) == 3) 559251881Speter { 560251881Speter saw_first_half_of_eol = TRUE; 561251881Speter continue; 562251881Speter } 563251881Speter else if (sizeof(APR_EOL_STR) == 2) 564251881Speter break; 565251881Speter else 566251881Speter /* ### APR_EOL_STR holds more than two chars? Who 567251881Speter ever heard of such a thing? */ 568251881Speter SVN_ERR_MALFUNCTION(); 569251881Speter } 570251881Speter 571251881Speter svn_stringbuf_appendbyte(strbuf, c); 572251881Speter } 573251881Speter 574251881Speter if (terminal->noecho) 575251881Speter { 576251881Speter /* If terminal echo was turned off, make sure future output 577251881Speter to the terminal starts on a new line, as expected. */ 578251881Speter SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool)); 579251881Speter } 580251881Speter SVN_ERR(terminal_close(terminal)); 581251881Speter 582251881Speter return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool); 583251881Speter} 584251881Speter 585251881Speter 586251881Speter 587251881Speter/** Prompt functions for auth providers. **/ 588251881Speter 589251881Speter/* Helper function for auth provider prompters: mention the 590251881Speter * authentication @a realm on stderr, in a manner appropriate for 591251881Speter * preceding a prompt; or if @a realm is null, then do nothing. 592251881Speter */ 593251881Speterstatic svn_error_t * 594251881Spetermaybe_print_realm(const char *realm, apr_pool_t *pool) 595251881Speter{ 596251881Speter if (realm) 597251881Speter { 598251881Speter terminal_handle_t *terminal; 599251881Speter SVN_ERR(terminal_open(&terminal, FALSE, pool)); 600251881Speter SVN_ERR(terminal_puts( 601251881Speter apr_psprintf(pool, 602251881Speter _("Authentication realm: %s\n"), realm), 603251881Speter terminal, pool)); 604251881Speter SVN_ERR(terminal_close(terminal)); 605251881Speter } 606251881Speter 607251881Speter return SVN_NO_ERROR; 608251881Speter} 609251881Speter 610251881Speter 611251881Speter/* This implements 'svn_auth_simple_prompt_func_t'. */ 612251881Spetersvn_error_t * 613251881Spetersvn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p, 614251881Speter void *baton, 615251881Speter const char *realm, 616251881Speter const char *username, 617251881Speter svn_boolean_t may_save, 618251881Speter apr_pool_t *pool) 619251881Speter{ 620251881Speter svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret)); 621251881Speter const char *pass_prompt; 622251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 623251881Speter 624251881Speter SVN_ERR(maybe_print_realm(realm, pool)); 625251881Speter 626251881Speter if (username) 627251881Speter ret->username = apr_pstrdup(pool, username); 628251881Speter else 629251881Speter SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 630251881Speter 631251881Speter pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username); 632251881Speter SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool)); 633251881Speter ret->may_save = may_save; 634251881Speter *cred_p = ret; 635251881Speter return SVN_NO_ERROR; 636251881Speter} 637251881Speter 638251881Speter 639251881Speter/* This implements 'svn_auth_username_prompt_func_t'. */ 640251881Spetersvn_error_t * 641251881Spetersvn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p, 642251881Speter void *baton, 643251881Speter const char *realm, 644251881Speter svn_boolean_t may_save, 645251881Speter apr_pool_t *pool) 646251881Speter{ 647251881Speter svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret)); 648251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 649251881Speter 650251881Speter SVN_ERR(maybe_print_realm(realm, pool)); 651251881Speter 652251881Speter SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); 653251881Speter ret->may_save = may_save; 654251881Speter *cred_p = ret; 655251881Speter return SVN_NO_ERROR; 656251881Speter} 657251881Speter 658251881Speter 659251881Speter/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */ 660251881Spetersvn_error_t * 661251881Spetersvn_cmdline_auth_ssl_server_trust_prompt 662251881Speter (svn_auth_cred_ssl_server_trust_t **cred_p, 663251881Speter void *baton, 664251881Speter const char *realm, 665251881Speter apr_uint32_t failures, 666251881Speter const svn_auth_ssl_server_cert_info_t *cert_info, 667251881Speter svn_boolean_t may_save, 668251881Speter apr_pool_t *pool) 669251881Speter{ 670251881Speter const char *choice; 671251881Speter svn_stringbuf_t *msg; 672251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 673251881Speter svn_stringbuf_t *buf = svn_stringbuf_createf 674251881Speter (pool, _("Error validating server certificate for '%s':\n"), realm); 675251881Speter 676251881Speter if (failures & SVN_AUTH_SSL_UNKNOWNCA) 677251881Speter { 678251881Speter svn_stringbuf_appendcstr 679251881Speter (buf, 680251881Speter _(" - The certificate is not issued by a trusted authority. Use the\n" 681251881Speter " fingerprint to validate the certificate manually!\n")); 682251881Speter } 683251881Speter 684251881Speter if (failures & SVN_AUTH_SSL_CNMISMATCH) 685251881Speter { 686251881Speter svn_stringbuf_appendcstr 687251881Speter (buf, _(" - The certificate hostname does not match.\n")); 688251881Speter } 689251881Speter 690251881Speter if (failures & SVN_AUTH_SSL_NOTYETVALID) 691251881Speter { 692251881Speter svn_stringbuf_appendcstr 693251881Speter (buf, _(" - The certificate is not yet valid.\n")); 694251881Speter } 695251881Speter 696251881Speter if (failures & SVN_AUTH_SSL_EXPIRED) 697251881Speter { 698251881Speter svn_stringbuf_appendcstr 699251881Speter (buf, _(" - The certificate has expired.\n")); 700251881Speter } 701251881Speter 702251881Speter if (failures & SVN_AUTH_SSL_OTHER) 703251881Speter { 704251881Speter svn_stringbuf_appendcstr 705251881Speter (buf, _(" - The certificate has an unknown error.\n")); 706251881Speter } 707251881Speter 708251881Speter msg = svn_stringbuf_createf 709251881Speter (pool, 710251881Speter _("Certificate information:\n" 711251881Speter " - Hostname: %s\n" 712251881Speter " - Valid: from %s until %s\n" 713251881Speter " - Issuer: %s\n" 714251881Speter " - Fingerprint: %s\n"), 715251881Speter cert_info->hostname, 716251881Speter cert_info->valid_from, 717251881Speter cert_info->valid_until, 718251881Speter cert_info->issuer_dname, 719251881Speter cert_info->fingerprint); 720251881Speter svn_stringbuf_appendstr(buf, msg); 721251881Speter 722251881Speter if (may_save) 723251881Speter { 724251881Speter svn_stringbuf_appendcstr 725251881Speter (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? ")); 726251881Speter } 727251881Speter else 728251881Speter { 729251881Speter svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? ")); 730251881Speter } 731251881Speter SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool)); 732251881Speter 733251881Speter if (choice[0] == 't' || choice[0] == 'T') 734251881Speter { 735251881Speter *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 736251881Speter (*cred_p)->may_save = FALSE; 737251881Speter (*cred_p)->accepted_failures = failures; 738251881Speter } 739251881Speter else if (may_save && (choice[0] == 'p' || choice[0] == 'P')) 740251881Speter { 741251881Speter *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); 742251881Speter (*cred_p)->may_save = TRUE; 743251881Speter (*cred_p)->accepted_failures = failures; 744251881Speter } 745251881Speter else 746251881Speter { 747251881Speter *cred_p = NULL; 748251881Speter } 749251881Speter 750251881Speter return SVN_NO_ERROR; 751251881Speter} 752251881Speter 753251881Speter 754251881Speter/* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */ 755251881Spetersvn_error_t * 756251881Spetersvn_cmdline_auth_ssl_client_cert_prompt 757251881Speter (svn_auth_cred_ssl_client_cert_t **cred_p, 758251881Speter void *baton, 759251881Speter const char *realm, 760251881Speter svn_boolean_t may_save, 761251881Speter apr_pool_t *pool) 762251881Speter{ 763251881Speter svn_auth_cred_ssl_client_cert_t *cred = NULL; 764251881Speter const char *cert_file = NULL; 765251881Speter const char *abs_cert_file = NULL; 766251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 767251881Speter 768251881Speter SVN_ERR(maybe_print_realm(realm, pool)); 769251881Speter SVN_ERR(prompt(&cert_file, _("Client certificate filename: "), 770251881Speter FALSE, pb, pool)); 771251881Speter SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool)); 772251881Speter 773251881Speter cred = apr_palloc(pool, sizeof(*cred)); 774251881Speter cred->cert_file = abs_cert_file; 775251881Speter cred->may_save = may_save; 776251881Speter *cred_p = cred; 777251881Speter 778251881Speter return SVN_NO_ERROR; 779251881Speter} 780251881Speter 781251881Speter 782251881Speter/* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ 783251881Spetersvn_error_t * 784251881Spetersvn_cmdline_auth_ssl_client_cert_pw_prompt 785251881Speter (svn_auth_cred_ssl_client_cert_pw_t **cred_p, 786251881Speter void *baton, 787251881Speter const char *realm, 788251881Speter svn_boolean_t may_save, 789251881Speter apr_pool_t *pool) 790251881Speter{ 791251881Speter svn_auth_cred_ssl_client_cert_pw_t *cred = NULL; 792251881Speter const char *result; 793251881Speter const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm); 794251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 795251881Speter 796251881Speter SVN_ERR(prompt(&result, text, TRUE, pb, pool)); 797251881Speter 798251881Speter cred = apr_pcalloc(pool, sizeof(*cred)); 799251881Speter cred->password = result; 800251881Speter cred->may_save = may_save; 801251881Speter *cred_p = cred; 802251881Speter 803251881Speter return SVN_NO_ERROR; 804251881Speter} 805251881Speter 806251881Speter/* This is a helper for plaintext prompt functions. */ 807251881Speterstatic svn_error_t * 808251881Speterplaintext_prompt_helper(svn_boolean_t *may_save_plaintext, 809251881Speter const char *realmstring, 810251881Speter const char *prompt_string, 811251881Speter const char *prompt_text, 812251881Speter void *baton, 813251881Speter apr_pool_t *pool) 814251881Speter{ 815251881Speter const char *answer = NULL; 816251881Speter svn_boolean_t answered = FALSE; 817251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 818251881Speter const char *config_path = NULL; 819251881Speter terminal_handle_t *terminal; 820251881Speter 821251881Speter if (pb) 822251881Speter SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir, 823251881Speter SVN_CONFIG_CATEGORY_SERVERS, pool)); 824251881Speter 825251881Speter SVN_ERR(terminal_open(&terminal, FALSE, pool)); 826251881Speter SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text, 827251881Speter realmstring, config_path), 828251881Speter terminal, pool)); 829251881Speter SVN_ERR(terminal_close(terminal)); 830251881Speter 831251881Speter do 832251881Speter { 833251881Speter svn_error_t *err = prompt(&answer, prompt_string, FALSE, pb, pool); 834251881Speter if (err) 835251881Speter { 836251881Speter if (err->apr_err == SVN_ERR_CANCELLED) 837251881Speter { 838251881Speter svn_error_clear(err); 839251881Speter *may_save_plaintext = FALSE; 840251881Speter return SVN_NO_ERROR; 841251881Speter } 842251881Speter else 843251881Speter return err; 844251881Speter } 845251881Speter if (apr_strnatcasecmp(answer, _("yes")) == 0 || 846251881Speter apr_strnatcasecmp(answer, _("y")) == 0) 847251881Speter { 848251881Speter *may_save_plaintext = TRUE; 849251881Speter answered = TRUE; 850251881Speter } 851251881Speter else if (apr_strnatcasecmp(answer, _("no")) == 0 || 852251881Speter apr_strnatcasecmp(answer, _("n")) == 0) 853251881Speter { 854251881Speter *may_save_plaintext = FALSE; 855251881Speter answered = TRUE; 856251881Speter } 857251881Speter else 858251881Speter prompt_string = _("Please type 'yes' or 'no': "); 859251881Speter } 860251881Speter while (! answered); 861251881Speter 862251881Speter return SVN_NO_ERROR; 863251881Speter} 864251881Speter 865251881Speter/* This implements 'svn_auth_plaintext_prompt_func_t'. */ 866251881Spetersvn_error_t * 867251881Spetersvn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext, 868251881Speter const char *realmstring, 869251881Speter void *baton, 870251881Speter apr_pool_t *pool) 871251881Speter{ 872251881Speter const char *prompt_string = _("Store password unencrypted (yes/no)? "); 873251881Speter const char *prompt_text = 874251881Speter _("\n-----------------------------------------------------------------------" 875251881Speter "\nATTENTION! Your password for authentication realm:\n" 876251881Speter "\n" 877251881Speter " %s\n" 878251881Speter "\n" 879251881Speter "can only be stored to disk unencrypted! You are advised to configure\n" 880251881Speter "your system so that Subversion can store passwords encrypted, if\n" 881251881Speter "possible. See the documentation for details.\n" 882251881Speter "\n" 883251881Speter "You can avoid future appearances of this warning by setting the value\n" 884251881Speter "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n" 885251881Speter "'%s'.\n" 886251881Speter "-----------------------------------------------------------------------\n" 887251881Speter ); 888251881Speter 889251881Speter return plaintext_prompt_helper(may_save_plaintext, realmstring, 890251881Speter prompt_string, prompt_text, baton, 891251881Speter pool); 892251881Speter} 893251881Speter 894251881Speter/* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */ 895251881Spetersvn_error_t * 896251881Spetersvn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, 897251881Speter const char *realmstring, 898251881Speter void *baton, 899251881Speter apr_pool_t *pool) 900251881Speter{ 901251881Speter const char *prompt_string = _("Store passphrase unencrypted (yes/no)? "); 902251881Speter const char *prompt_text = 903251881Speter _("\n-----------------------------------------------------------------------\n" 904251881Speter "ATTENTION! Your passphrase for client certificate:\n" 905251881Speter "\n" 906251881Speter " %s\n" 907251881Speter "\n" 908251881Speter "can only be stored to disk unencrypted! You are advised to configure\n" 909251881Speter "your system so that Subversion can store passphrase encrypted, if\n" 910251881Speter "possible. See the documentation for details.\n" 911251881Speter "\n" 912251881Speter "You can avoid future appearances of this warning by setting the value\n" 913251881Speter "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n" 914251881Speter "'no' in '%s'.\n" 915251881Speter "-----------------------------------------------------------------------\n" 916251881Speter ); 917251881Speter 918251881Speter return plaintext_prompt_helper(may_save_plaintext, realmstring, 919251881Speter prompt_string, prompt_text, baton, 920251881Speter pool); 921251881Speter} 922251881Speter 923251881Speter 924251881Speter/** Generic prompting. **/ 925251881Speter 926251881Spetersvn_error_t * 927251881Spetersvn_cmdline_prompt_user2(const char **result, 928251881Speter const char *prompt_str, 929251881Speter svn_cmdline_prompt_baton_t *baton, 930251881Speter apr_pool_t *pool) 931251881Speter{ 932251881Speter /* XXX: We know prompt doesn't use the new members 933251881Speter * of svn_cmdline_prompt_baton2_t. */ 934251881Speter return prompt(result, prompt_str, FALSE /* don't hide input */, 935251881Speter (svn_cmdline_prompt_baton2_t *)baton, pool); 936251881Speter} 937251881Speter 938251881Speter/* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */ 939251881Spetersvn_error_t * 940251881Spetersvn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password, 941251881Speter const char *keyring_name, 942251881Speter void *baton, 943251881Speter apr_pool_t *pool) 944251881Speter{ 945251881Speter const char *password; 946251881Speter const char *pass_prompt; 947251881Speter svn_cmdline_prompt_baton2_t *pb = baton; 948251881Speter 949251881Speter pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "), 950251881Speter keyring_name); 951251881Speter SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool)); 952251881Speter *keyring_password = apr_pstrdup(pool, password); 953251881Speter return SVN_NO_ERROR; 954251881Speter} 955