diff options
author | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
commit | d25dac7fcc6acc838b71bbda8916fd9665c709ab (patch) | |
tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_subr/prompt.c | |
download | FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz |
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_subr/prompt.c')
-rw-r--r-- | subversion/libsvn_subr/prompt.c | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/prompt.c b/subversion/libsvn_subr/prompt.c new file mode 100644 index 0000000..92ee6a2 --- /dev/null +++ b/subversion/libsvn_subr/prompt.c @@ -0,0 +1,954 @@ +/* + * prompt.c -- ask the user for authentication information. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include <apr_lib.h> +#include <apr_poll.h> +#include <apr_portable.h> + +#include "svn_cmdline.h" +#include "svn_ctype.h" +#include "svn_string.h" +#include "svn_auth.h" +#include "svn_error.h" +#include "svn_path.h" + +#include "private/svn_cmdline_private.h" +#include "svn_private_config.h" + +#ifdef WIN32 +#include <conio.h> +#elif defined(HAVE_TERMIOS_H) +#include <signal.h> +#include <termios.h> +#endif + + + +/* Descriptor of an open terminal */ +typedef struct terminal_handle_t terminal_handle_t; +struct terminal_handle_t +{ + apr_file_t *infd; /* input file handle */ + apr_file_t *outfd; /* output file handle */ + svn_boolean_t noecho; /* terminal echo was turned off */ + svn_boolean_t close_handles; /* close handles when closing the terminal */ + apr_pool_t *pool; /* pool associated with the file handles */ + +#ifdef HAVE_TERMIOS_H + svn_boolean_t restore_state; /* terminal state was changed */ + apr_os_file_t osinfd; /* OS-specific handle for infd */ + struct termios attr; /* saved terminal attributes */ +#endif +}; + +/* Initialize safe state of terminal_handle_t. */ +static void +terminal_handle_init(terminal_handle_t *terminal, + apr_file_t *infd, apr_file_t *outfd, + svn_boolean_t noecho, svn_boolean_t close_handles, + apr_pool_t *pool) +{ + memset(terminal, 0, sizeof(*terminal)); + terminal->infd = infd; + terminal->outfd = outfd; + terminal->noecho = noecho; + terminal->close_handles = close_handles; + terminal->pool = pool; +} + +/* + * Common pool cleanup handler for terminal_handle_t. Closes TERMINAL. + * If CLOSE_HANDLES is TRUE, close the terminal file handles. + * If RESTORE_STATE is TRUE, restores the TERMIOS flags of the terminal. + */ +static apr_status_t +terminal_cleanup_handler(terminal_handle_t *terminal, + svn_boolean_t close_handles, + svn_boolean_t restore_state) +{ + apr_status_t status = APR_SUCCESS; + +#ifdef HAVE_TERMIOS_H + /* Restore terminal state flags. */ + if (restore_state && terminal->restore_state) + tcsetattr(terminal->osinfd, TCSANOW, &terminal->attr); +#endif + + /* Close terminal handles. */ + if (close_handles && terminal->close_handles) + { + apr_file_t *const infd = terminal->infd; + apr_file_t *const outfd = terminal->outfd; + + if (infd) + { + terminal->infd = NULL; + status = apr_file_close(infd); + } + + if (!status && outfd && outfd != infd) + { + terminal->outfd = NULL; + status = apr_file_close(terminal->outfd); + } + } + return status; +} + +/* Normal pool cleanup for a terminal. */ +static apr_status_t terminal_plain_cleanup(void *baton) +{ + return terminal_cleanup_handler(baton, FALSE, TRUE); +} + +/* Child pool cleanup for a terminal -- does not restore echo state. */ +static apr_status_t terminal_child_cleanup(void *baton) +{ + return terminal_cleanup_handler(baton, FALSE, FALSE); +} + +/* Explicitly close the terminal, removing its cleanup handlers. */ +static svn_error_t * +terminal_close(terminal_handle_t *terminal) +{ + apr_status_t status; + + /* apr_pool_cleanup_kill() removes both normal and child cleanup */ + apr_pool_cleanup_kill(terminal->pool, terminal, terminal_plain_cleanup); + + status = terminal_cleanup_handler(terminal, TRUE, TRUE); + if (status) + return svn_error_create(status, NULL, _("Can't close terminal")); + return SVN_NO_ERROR; +} + +/* Allocate and open *TERMINAL. If NOECHO is TRUE, try to turn off + terminal echo. Use POOL for all allocations.*/ +static svn_error_t * +terminal_open(terminal_handle_t **terminal, svn_boolean_t noecho, + apr_pool_t *pool) +{ + apr_status_t status; + +#ifdef WIN32 + /* On Windows, we'll use the console API directly if the process has + a console attached; otherwise we'll just use stdin and stderr. */ + const HANDLE conin = CreateFileW(L"CONIN$", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); + if (conin != INVALID_HANDLE_VALUE) + { + /* The process has a console. */ + CloseHandle(conin); + terminal_handle_init(*terminal, NULL, NULL, noecho, FALSE, NULL); + return SVN_NO_ERROR; + } +#else /* !WIN32 */ + /* Without evidence to the contrary, we'll assume this is *nix and + try to open /dev/tty. If that fails, we'll use stdin for input + and stderr for prompting. */ + apr_file_t *tmpfd; + status = apr_file_open(&tmpfd, "/dev/tty", + APR_FOPEN_READ | APR_FOPEN_WRITE, + APR_OS_DEFAULT, pool); + *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); + if (!status) + { + /* We have a terminal handle that we can use for input and output. */ + terminal_handle_init(*terminal, tmpfd, tmpfd, FALSE, TRUE, pool); + } +#endif /* !WIN32 */ + else + { + /* There is no terminal. Sigh. */ + apr_file_t *infd; + apr_file_t *outfd; + + status = apr_file_open_stdin(&infd, pool); + if (status) + return svn_error_wrap_apr(status, _("Can't open stdin")); + status = apr_file_open_stderr(&outfd, pool); + if (status) + return svn_error_wrap_apr(status, _("Can't open stderr")); + terminal_handle_init(*terminal, infd, outfd, FALSE, FALSE, pool); + } + +#ifdef HAVE_TERMIOS_H + /* Set terminal state */ + if (0 == apr_os_file_get(&(*terminal)->osinfd, (*terminal)->infd)) + { + if (0 == tcgetattr((*terminal)->osinfd, &(*terminal)->attr)) + { + struct termios attr = (*terminal)->attr; + /* Turn off signal handling and canonical input mode */ + attr.c_lflag &= ~(ISIG | ICANON); + attr.c_cc[VMIN] = 1; /* Read one byte at a time */ + attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */ + attr.c_lflag &= ~(ECHO); /* Turn off echo */ + if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr)) + { + (*terminal)->noecho = noecho; + (*terminal)->restore_state = TRUE; + } + } + } +#endif /* HAVE_TERMIOS_H */ + + /* Register pool cleanup to close handles and restore echo state. */ + apr_pool_cleanup_register((*terminal)->pool, *terminal, + terminal_plain_cleanup, + terminal_child_cleanup); + return SVN_NO_ERROR; +} + +/* Write a null-terminated STRING to TERMINAL. + Use POOL for allocations related to converting STRING from UTF-8. */ +static svn_error_t * +terminal_puts(const char *string, terminal_handle_t *terminal, + apr_pool_t *pool) +{ + svn_error_t *err; + apr_status_t status; + const char *converted; + + err = svn_cmdline_cstring_from_utf8(&converted, string, pool); + if (err) + { + svn_error_clear(err); + converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); + } + +#ifdef WIN32 + if (!terminal->outfd) + { + /* See terminal_open; we're using Console I/O. */ + _cputs(converted); + return SVN_NO_ERROR; + } +#endif + + status = apr_file_write_full(terminal->outfd, converted, + strlen(converted), NULL); + if (!status) + status = apr_file_flush(terminal->outfd); + if (status) + return svn_error_wrap_apr(status, _("Can't write to terminal")); + return SVN_NO_ERROR; +} + +/* These codes can be returned from terminal_getc instead of a character. */ +#define TERMINAL_NONE 0x80000 /* no character read, retry */ +#define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */ +#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */ +#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */ + +/* Helper for terminal_getc: writes CH to OUTFD as a control char. */ +#ifndef WIN32 +static void +echo_control_char(char ch, apr_file_t *outfd) +{ + if (svn_ctype_iscntrl(ch)) + { + const char substitute = (ch < 32? '@' + ch : '?'); + apr_file_putc('^', outfd); + apr_file_putc(substitute, outfd); + } + else if (svn_ctype_isprint(ch)) + { + /* Pass printable characters unchanged. */ + apr_file_putc(ch, outfd); + } + else + { + /* Everything else is strange. */ + apr_file_putc('^', outfd); + apr_file_putc('!', outfd); + } +} +#endif /* WIN32 */ + +/* Read one character or control code from TERMINAL, returning it in CODE. + if CAN_ERASE and the input was a deletion, emit codes to erase the + last character displayed on the terminal. + Use POOL for all allocations. */ +static svn_error_t * +terminal_getc(int *code, terminal_handle_t *terminal, + svn_boolean_t can_erase, apr_pool_t *pool) +{ + const svn_boolean_t echo = !terminal->noecho; + apr_status_t status = APR_SUCCESS; + char ch; + +#ifdef WIN32 + if (!terminal->infd) + { + /* See terminal_open; we're using Console I/O. */ + + /* The following was hoisted from APR's getpass for Windows. */ + int concode = _getch(); + switch (concode) + { + case '\r': /* end-of-line */ + *code = TERMINAL_EOL; + if (echo) + _cputs("\r\n"); + break; + + case EOF: /* end-of-file */ + case 26: /* Ctrl+Z */ + *code = TERMINAL_EOF; + if (echo) + _cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n")); + break; + + case 3: /* Ctrl+C, Ctrl+Break */ + /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ + if (echo) + _cputs("^C\r\n"); + return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); + + case 0: /* Function code prefix */ + case 0xE0: + concode = (concode << 4) | _getch(); + /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ + if (concode == 0xE53 || concode == 0xE4B + || concode == 0x053 || concode == 0x04B) + { + *code = TERMINAL_DEL; + if (can_erase) + _cputs("\b \b"); + } + else + { + *code = TERMINAL_NONE; + _putch('\a'); + } + break; + + case '\b': /* BS */ + case 127: /* DEL */ + *code = TERMINAL_DEL; + if (can_erase) + _cputs("\b \b"); + break; + + default: + if (!apr_iscntrl(concode)) + { + *code = (int)(unsigned char)concode; + _putch(echo ? concode : '*'); + } + else + { + *code = TERMINAL_NONE; + _putch('\a'); + } + } + return SVN_NO_ERROR; + } +#elif defined(HAVE_TERMIOS_H) + if (terminal->restore_state) + { + /* We're using a bytewise-immediate termios input */ + const struct termios *const attr = &terminal->attr; + + status = apr_file_getc(&ch, terminal->infd); + if (status) + return svn_error_wrap_apr(status, _("Can't read from terminal")); + + if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT]) + { + /* Break */ + echo_control_char(ch, terminal->outfd); + return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL); + } + else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL]) + { + /* Newline */ + *code = TERMINAL_EOL; + apr_file_putc('\n', terminal->outfd); + } + else if (ch == '\b' || ch == attr->c_cc[VERASE]) + { + /* Delete */ + *code = TERMINAL_DEL; + if (can_erase) + { + apr_file_putc('\b', terminal->outfd); + apr_file_putc(' ', terminal->outfd); + apr_file_putc('\b', terminal->outfd); + } + } + else if (ch == attr->c_cc[VEOF]) + { + /* End of input */ + *code = TERMINAL_EOF; + echo_control_char(ch, terminal->outfd); + } + else if (ch == attr->c_cc[VSUSP]) + { + /* Suspend */ + *code = TERMINAL_NONE; + kill(0, SIGTSTP); + } + else if (!apr_iscntrl(ch)) + { + /* Normal character */ + *code = (int)(unsigned char)ch; + apr_file_putc((echo ? ch : '*'), terminal->outfd); + } + else + { + /* Ignored character */ + *code = TERMINAL_NONE; + apr_file_putc('\a', terminal->outfd); + } + return SVN_NO_ERROR; + } +#endif /* HAVE_TERMIOS_H */ + + /* Fall back to plain stream-based I/O. */ +#ifndef WIN32 + /* Wait for input on termin. This code is based on + apr_wait_for_io_or_timeout(). + Note that this will return an EINTR on a signal. */ + { + apr_pollfd_t pollset; + int n; + + pollset.desc_type = APR_POLL_FILE; + pollset.desc.f = terminal->infd; + pollset.p = pool; + pollset.reqevents = APR_POLLIN; + + status = apr_poll(&pollset, 1, &n, -1); + + if (n == 1 && pollset.rtnevents & APR_POLLIN) + status = APR_SUCCESS; + } +#endif /* !WIN32 */ + + if (!status) + status = apr_file_getc(&ch, terminal->infd); + if (APR_STATUS_IS_EINTR(status)) + { + *code = TERMINAL_NONE; + return SVN_NO_ERROR; + } + else if (APR_STATUS_IS_EOF(status)) + { + *code = TERMINAL_EOF; + return SVN_NO_ERROR; + } + else if (status) + return svn_error_wrap_apr(status, _("Can't read from terminal")); + + *code = (int)(unsigned char)ch; + return SVN_NO_ERROR; +} + + +/* Set @a *result to the result of prompting the user with @a + * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton. + * Do not call the cancel_func if @a *pb is NULL. + * Allocate @a *result in @a pool. + * + * If @a hide is true, then try to avoid displaying the user's input. + */ +static svn_error_t * +prompt(const char **result, + const char *prompt_msg, + svn_boolean_t hide, + svn_cmdline_prompt_baton2_t *pb, + apr_pool_t *pool) +{ + /* XXX: If this functions ever starts using members of *pb + * which were not included in svn_cmdline_prompt_baton_t, + * we need to update svn_cmdline_prompt_user2 and its callers. */ + + svn_boolean_t saw_first_half_of_eol = FALSE; + svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool); + terminal_handle_t *terminal; + int code; + char c; + + SVN_ERR(terminal_open(&terminal, hide, pool)); + SVN_ERR(terminal_puts(prompt_msg, terminal, pool)); + + while (1) + { + SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool)); + + /* Check for cancellation after a character has been read, some + input processing modes may eat ^C and we'll only notice a + cancellation signal after characters have been read -- + sometimes even after a newline. */ + if (pb) + SVN_ERR(pb->cancel_func(pb->cancel_baton)); + + switch (code) + { + case TERMINAL_NONE: + /* Nothing useful happened; retry. */ + continue; + + case TERMINAL_DEL: + /* Delete the last input character. terminal_getc takes care + of erasing the feedback from the terminal, if applicable. */ + svn_stringbuf_chop(strbuf, 1); + continue; + + case TERMINAL_EOL: + /* End-of-line means end of input. Trick the EOL-detection code + below to stop reading. */ + saw_first_half_of_eol = TRUE; + c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */ + break; + + case TERMINAL_EOF: + return svn_error_create( + APR_EOF, + terminal_close(terminal), + _("End of file while reading from terminal")); + + default: + /* Convert the returned code back to the character. */ + c = (char)code; + } + + if (saw_first_half_of_eol) + { + if (c == APR_EOL_STR[1]) + break; + else + saw_first_half_of_eol = FALSE; + } + else if (c == APR_EOL_STR[0]) + { + /* GCC might complain here: "warning: will never be executed" + * That's fine. This is a compile-time check for "\r\n\0" */ + if (sizeof(APR_EOL_STR) == 3) + { + saw_first_half_of_eol = TRUE; + continue; + } + else if (sizeof(APR_EOL_STR) == 2) + break; + else + /* ### APR_EOL_STR holds more than two chars? Who + ever heard of such a thing? */ + SVN_ERR_MALFUNCTION(); + } + + svn_stringbuf_appendbyte(strbuf, c); + } + + if (terminal->noecho) + { + /* If terminal echo was turned off, make sure future output + to the terminal starts on a new line, as expected. */ + SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool)); + } + SVN_ERR(terminal_close(terminal)); + + return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool); +} + + + +/** Prompt functions for auth providers. **/ + +/* Helper function for auth provider prompters: mention the + * authentication @a realm on stderr, in a manner appropriate for + * preceding a prompt; or if @a realm is null, then do nothing. + */ +static svn_error_t * +maybe_print_realm(const char *realm, apr_pool_t *pool) +{ + if (realm) + { + terminal_handle_t *terminal; + SVN_ERR(terminal_open(&terminal, FALSE, pool)); + SVN_ERR(terminal_puts( + apr_psprintf(pool, + _("Authentication realm: %s\n"), realm), + terminal, pool)); + SVN_ERR(terminal_close(terminal)); + } + + return SVN_NO_ERROR; +} + + +/* This implements 'svn_auth_simple_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p, + void *baton, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret)); + const char *pass_prompt; + svn_cmdline_prompt_baton2_t *pb = baton; + + SVN_ERR(maybe_print_realm(realm, pool)); + + if (username) + ret->username = apr_pstrdup(pool, username); + else + SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); + + pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username); + SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool)); + ret->may_save = may_save; + *cred_p = ret; + return SVN_NO_ERROR; +} + + +/* This implements 'svn_auth_username_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret)); + svn_cmdline_prompt_baton2_t *pb = baton; + + SVN_ERR(maybe_print_realm(realm, pool)); + + SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); + ret->may_save = may_save; + *cred_p = ret; + return SVN_NO_ERROR; +} + + +/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_ssl_server_trust_prompt + (svn_auth_cred_ssl_server_trust_t **cred_p, + void *baton, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const char *choice; + svn_stringbuf_t *msg; + svn_cmdline_prompt_baton2_t *pb = baton; + svn_stringbuf_t *buf = svn_stringbuf_createf + (pool, _("Error validating server certificate for '%s':\n"), realm); + + if (failures & SVN_AUTH_SSL_UNKNOWNCA) + { + svn_stringbuf_appendcstr + (buf, + _(" - The certificate is not issued by a trusted authority. Use the\n" + " fingerprint to validate the certificate manually!\n")); + } + + if (failures & SVN_AUTH_SSL_CNMISMATCH) + { + svn_stringbuf_appendcstr + (buf, _(" - The certificate hostname does not match.\n")); + } + + if (failures & SVN_AUTH_SSL_NOTYETVALID) + { + svn_stringbuf_appendcstr + (buf, _(" - The certificate is not yet valid.\n")); + } + + if (failures & SVN_AUTH_SSL_EXPIRED) + { + svn_stringbuf_appendcstr + (buf, _(" - The certificate has expired.\n")); + } + + if (failures & SVN_AUTH_SSL_OTHER) + { + svn_stringbuf_appendcstr + (buf, _(" - The certificate has an unknown error.\n")); + } + + msg = svn_stringbuf_createf + (pool, + _("Certificate information:\n" + " - Hostname: %s\n" + " - Valid: from %s until %s\n" + " - Issuer: %s\n" + " - Fingerprint: %s\n"), + cert_info->hostname, + cert_info->valid_from, + cert_info->valid_until, + cert_info->issuer_dname, + cert_info->fingerprint); + svn_stringbuf_appendstr(buf, msg); + + if (may_save) + { + svn_stringbuf_appendcstr + (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? ")); + } + else + { + svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? ")); + } + SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool)); + + if (choice[0] == 't' || choice[0] == 'T') + { + *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); + (*cred_p)->may_save = FALSE; + (*cred_p)->accepted_failures = failures; + } + else if (may_save && (choice[0] == 'p' || choice[0] == 'P')) + { + *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); + (*cred_p)->may_save = TRUE; + (*cred_p)->accepted_failures = failures; + } + else + { + *cred_p = NULL; + } + + return SVN_NO_ERROR; +} + + +/* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_ssl_client_cert_prompt + (svn_auth_cred_ssl_client_cert_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_ssl_client_cert_t *cred = NULL; + const char *cert_file = NULL; + const char *abs_cert_file = NULL; + svn_cmdline_prompt_baton2_t *pb = baton; + + SVN_ERR(maybe_print_realm(realm, pool)); + SVN_ERR(prompt(&cert_file, _("Client certificate filename: "), + FALSE, pb, pool)); + SVN_ERR(svn_dirent_get_absolute(&abs_cert_file, cert_file, pool)); + + cred = apr_palloc(pool, sizeof(*cred)); + cred->cert_file = abs_cert_file; + cred->may_save = may_save; + *cred_p = cred; + + return SVN_NO_ERROR; +} + + +/* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_ssl_client_cert_pw_prompt + (svn_auth_cred_ssl_client_cert_pw_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_ssl_client_cert_pw_t *cred = NULL; + const char *result; + const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm); + svn_cmdline_prompt_baton2_t *pb = baton; + + SVN_ERR(prompt(&result, text, TRUE, pb, pool)); + + cred = apr_pcalloc(pool, sizeof(*cred)); + cred->password = result; + cred->may_save = may_save; + *cred_p = cred; + + return SVN_NO_ERROR; +} + +/* This is a helper for plaintext prompt functions. */ +static svn_error_t * +plaintext_prompt_helper(svn_boolean_t *may_save_plaintext, + const char *realmstring, + const char *prompt_string, + const char *prompt_text, + void *baton, + apr_pool_t *pool) +{ + const char *answer = NULL; + svn_boolean_t answered = FALSE; + svn_cmdline_prompt_baton2_t *pb = baton; + const char *config_path = NULL; + terminal_handle_t *terminal; + + if (pb) + SVN_ERR(svn_config_get_user_config_path(&config_path, pb->config_dir, + SVN_CONFIG_CATEGORY_SERVERS, pool)); + + SVN_ERR(terminal_open(&terminal, FALSE, pool)); + SVN_ERR(terminal_puts(apr_psprintf(pool, prompt_text, + realmstring, config_path), + terminal, pool)); + SVN_ERR(terminal_close(terminal)); + + do + { + svn_error_t *err = prompt(&answer, prompt_string, FALSE, pb, pool); + if (err) + { + if (err->apr_err == SVN_ERR_CANCELLED) + { + svn_error_clear(err); + *may_save_plaintext = FALSE; + return SVN_NO_ERROR; + } + else + return err; + } + if (apr_strnatcasecmp(answer, _("yes")) == 0 || + apr_strnatcasecmp(answer, _("y")) == 0) + { + *may_save_plaintext = TRUE; + answered = TRUE; + } + else if (apr_strnatcasecmp(answer, _("no")) == 0 || + apr_strnatcasecmp(answer, _("n")) == 0) + { + *may_save_plaintext = FALSE; + answered = TRUE; + } + else + prompt_string = _("Please type 'yes' or 'no': "); + } + while (! answered); + + return SVN_NO_ERROR; +} + +/* This implements 'svn_auth_plaintext_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_plaintext_prompt(svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool) +{ + const char *prompt_string = _("Store password unencrypted (yes/no)? "); + const char *prompt_text = + _("\n-----------------------------------------------------------------------" + "\nATTENTION! Your password for authentication realm:\n" + "\n" + " %s\n" + "\n" + "can only be stored to disk unencrypted! You are advised to configure\n" + "your system so that Subversion can store passwords encrypted, if\n" + "possible. See the documentation for details.\n" + "\n" + "You can avoid future appearances of this warning by setting the value\n" + "of the 'store-plaintext-passwords' option to either 'yes' or 'no' in\n" + "'%s'.\n" + "-----------------------------------------------------------------------\n" + ); + + return plaintext_prompt_helper(may_save_plaintext, realmstring, + prompt_string, prompt_text, baton, + pool); +} + +/* This implements 'svn_auth_plaintext_passphrase_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool) +{ + const char *prompt_string = _("Store passphrase unencrypted (yes/no)? "); + const char *prompt_text = + _("\n-----------------------------------------------------------------------\n" + "ATTENTION! Your passphrase for client certificate:\n" + "\n" + " %s\n" + "\n" + "can only be stored to disk unencrypted! You are advised to configure\n" + "your system so that Subversion can store passphrase encrypted, if\n" + "possible. See the documentation for details.\n" + "\n" + "You can avoid future appearances of this warning by setting the value\n" + "of the 'store-ssl-client-cert-pp-plaintext' option to either 'yes' or\n" + "'no' in '%s'.\n" + "-----------------------------------------------------------------------\n" + ); + + return plaintext_prompt_helper(may_save_plaintext, realmstring, + prompt_string, prompt_text, baton, + pool); +} + + +/** Generic prompting. **/ + +svn_error_t * +svn_cmdline_prompt_user2(const char **result, + const char *prompt_str, + svn_cmdline_prompt_baton_t *baton, + apr_pool_t *pool) +{ + /* XXX: We know prompt doesn't use the new members + * of svn_cmdline_prompt_baton2_t. */ + return prompt(result, prompt_str, FALSE /* don't hide input */, + (svn_cmdline_prompt_baton2_t *)baton, pool); +} + +/* This implements 'svn_auth_gnome_keyring_unlock_prompt_func_t'. */ +svn_error_t * +svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password, + const char *keyring_name, + void *baton, + apr_pool_t *pool) +{ + const char *password; + const char *pass_prompt; + svn_cmdline_prompt_baton2_t *pb = baton; + + pass_prompt = apr_psprintf(pool, _("Password for '%s' GNOME keyring: "), + keyring_name); + SVN_ERR(prompt(&password, pass_prompt, TRUE, pb, pool)); + *keyring_password = apr_pstrdup(pool, password); + return SVN_NO_ERROR; +} |