* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / prompt.c
blobfe0175d0d18d9d5d2d17335b594fe2751341a9ad
1 /*
2 * prompt.c -- ask the user for authentication information.
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include <apr_lib.h>
26 #include <apr_poll.h>
28 #include "svn_cmdline.h"
29 #include "svn_string.h"
30 #include "svn_auth.h"
31 #include "svn_error.h"
33 #include "svn_private_config.h"
37 /* Wait for input on @a *f. Doing all allocations
38 * in @a pool. This functions is based on apr_wait_for_io_or_timeout().
39 * Note that this will return an EINTR on a signal.
41 * ### FIX: When APR gives us a better way of doing this use it. */
42 static apr_status_t wait_for_input(apr_file_t *f,
43 apr_pool_t *pool)
45 apr_pollfd_t pollset;
46 int srv, n;
48 /* APR specs say things that are unimplemented are supposed to return
49 * APR_ENOTIMPL. But when trying to use APR_POLL_FILE with apr_poll
50 * on Windows it returns APR_EBADF instead. So just return APR_ENOTIMPL
51 * ourselves here.
53 #ifdef WIN32
54 return APR_ENOTIMPL;
55 #endif /* WIN32 */
57 pollset.desc_type = APR_POLL_FILE;
58 pollset.desc.f = f;
59 pollset.p = pool;
60 pollset.reqevents = APR_POLLIN;
62 #ifndef AS400
63 srv = apr_poll(&pollset, 1, &n, -1);
64 #else
65 /* OS400 requires a pool argument for apr_poll(). */
66 srv = apr_poll(&pollset, 1, &n, -1, pool);
67 #endif
69 if (n == 1 && pollset.rtnevents & APR_POLLIN)
70 return APR_SUCCESS;
72 return srv;
75 /* Set @a *result to the result of prompting the user with @a
76 * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton.
77 * Do not call the cancel_func if @a *pb is NULL.
78 * Allocate @a *result in @a pool.
80 * If @a hide is true, then try to avoid displaying the user's input.
82 static svn_error_t *
83 prompt(const char **result,
84 const char *prompt_msg,
85 svn_boolean_t hide,
86 svn_cmdline_prompt_baton_t *pb,
87 apr_pool_t *pool)
89 apr_status_t status;
90 apr_file_t *fp;
91 char c;
93 svn_stringbuf_t *strbuf = svn_stringbuf_create("", pool);
95 status = apr_file_open_stdin(&fp, pool);
96 if (status)
97 return svn_error_wrap_apr(status, _("Can't open stdin"));
99 if (! hide)
101 svn_boolean_t saw_first_half_of_eol = FALSE;
102 SVN_ERR(svn_cmdline_fputs(prompt_msg, stderr, pool));
103 fflush(stderr);
105 while (1)
107 /* Hack to allow us to not block for io on the prompt, so
108 * we can cancel. */
109 if (pb)
110 SVN_ERR(pb->cancel_func(pb->cancel_baton));
111 status = wait_for_input(fp, pool);
112 if (APR_STATUS_IS_EINTR(status))
113 continue;
114 else if (status && status != APR_ENOTIMPL)
115 return svn_error_wrap_apr(status, _("Can't read stdin"));
117 status = apr_file_getc(&c, fp);
118 if (status)
119 return svn_error_wrap_apr(status, _("Can't read stdin"));
121 if (saw_first_half_of_eol)
123 if (c == APR_EOL_STR[1])
124 break;
125 else
126 saw_first_half_of_eol = FALSE;
128 else if (c == APR_EOL_STR[0])
130 if (sizeof(APR_EOL_STR) == 3)
132 saw_first_half_of_eol = TRUE;
133 continue;
135 else if (sizeof(APR_EOL_STR) == 2)
136 break;
137 else
138 /* ### APR_EOL_STR holds more than two chars? Who
139 ever heard of such a thing? */
140 abort();
143 svn_stringbuf_appendbytes(strbuf, &c, 1);
146 else
148 const char *prompt_stdout;
149 size_t bufsize = 300;
150 SVN_ERR(svn_cmdline_cstring_from_utf8(&prompt_stdout, prompt_msg,
151 pool));
152 svn_stringbuf_ensure(strbuf, bufsize);
154 status = apr_password_get(prompt_stdout, strbuf->data, &bufsize);
155 if (status)
156 return svn_error_wrap_apr(status, _("Can't get password"));
159 SVN_ERR(svn_cmdline_cstring_to_utf8(result, strbuf->data, pool));
161 return SVN_NO_ERROR;
166 /** Prompt functions for auth providers. **/
168 /* Helper function for auth provider prompters: mention the
169 * authentication @a realm on stderr, in a manner appropriate for
170 * preceding a prompt; or if @a realm is null, then do nothing.
172 static svn_error_t *
173 maybe_print_realm(const char *realm, apr_pool_t *pool)
175 if (realm)
177 SVN_ERR(svn_cmdline_fprintf(stderr, pool,
178 _("Authentication realm: %s\n"), realm));
179 fflush(stderr);
182 return SVN_NO_ERROR;
186 /* This implements 'svn_auth_simple_prompt_func_t'. */
187 svn_error_t *
188 svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p,
189 void *baton,
190 const char *realm,
191 const char *username,
192 svn_boolean_t may_save,
193 apr_pool_t *pool)
195 svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret));
196 const char *pass_prompt;
197 svn_cmdline_prompt_baton_t *pb = baton;
199 SVN_ERR(maybe_print_realm(realm, pool));
201 if (username)
202 ret->username = apr_pstrdup(pool, username);
203 else
204 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
206 pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username);
207 SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool));
208 ret->may_save = may_save;
209 *cred_p = ret;
210 return SVN_NO_ERROR;
214 /* This implements 'svn_auth_username_prompt_func_t'. */
215 svn_error_t *
216 svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p,
217 void *baton,
218 const char *realm,
219 svn_boolean_t may_save,
220 apr_pool_t *pool)
222 svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret));
223 svn_cmdline_prompt_baton_t *pb = baton;
225 SVN_ERR(maybe_print_realm(realm, pool));
227 SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool));
228 ret->may_save = may_save;
229 *cred_p = ret;
230 return SVN_NO_ERROR;
234 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */
235 svn_error_t *
236 svn_cmdline_auth_ssl_server_trust_prompt
237 (svn_auth_cred_ssl_server_trust_t **cred_p,
238 void *baton,
239 const char *realm,
240 apr_uint32_t failures,
241 const svn_auth_ssl_server_cert_info_t *cert_info,
242 svn_boolean_t may_save,
243 apr_pool_t *pool)
245 const char *choice;
246 svn_stringbuf_t *msg;
247 svn_cmdline_prompt_baton_t *pb = baton;
248 svn_stringbuf_t *buf = svn_stringbuf_createf
249 (pool, _("Error validating server certificate for '%s':\n"), realm);
251 if (failures & SVN_AUTH_SSL_UNKNOWNCA)
253 svn_stringbuf_appendcstr
254 (buf,
255 _(" - The certificate is not issued by a trusted authority. Use the\n"
256 " fingerprint to validate the certificate manually!\n"));
259 if (failures & SVN_AUTH_SSL_CNMISMATCH)
261 svn_stringbuf_appendcstr
262 (buf, _(" - The certificate hostname does not match.\n"));
265 if (failures & SVN_AUTH_SSL_NOTYETVALID)
267 svn_stringbuf_appendcstr
268 (buf, _(" - The certificate is not yet valid.\n"));
271 if (failures & SVN_AUTH_SSL_EXPIRED)
273 svn_stringbuf_appendcstr
274 (buf, _(" - The certificate has expired.\n"));
277 if (failures & SVN_AUTH_SSL_OTHER)
279 svn_stringbuf_appendcstr
280 (buf, _(" - The certificate has an unknown error.\n"));
283 msg = svn_stringbuf_createf
284 (pool,
285 _("Certificate information:\n"
286 " - Hostname: %s\n"
287 " - Valid: from %s until %s\n"
288 " - Issuer: %s\n"
289 " - Fingerprint: %s\n"),
290 cert_info->hostname,
291 cert_info->valid_from,
292 cert_info->valid_until,
293 cert_info->issuer_dname,
294 cert_info->fingerprint);
295 svn_stringbuf_appendstr(buf, msg);
297 if (may_save)
299 svn_stringbuf_appendcstr
300 (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? "));
302 else
304 svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? "));
306 SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool));
308 if (choice[0] == 't' || choice[0] == 'T')
310 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
311 (*cred_p)->may_save = FALSE;
312 (*cred_p)->accepted_failures = failures;
314 else if (may_save && (choice[0] == 'p' || choice[0] == 'P'))
316 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
317 (*cred_p)->may_save = TRUE;
318 (*cred_p)->accepted_failures = failures;
320 else
322 *cred_p = NULL;
325 return SVN_NO_ERROR;
329 /* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */
330 svn_error_t *
331 svn_cmdline_auth_ssl_client_cert_prompt
332 (svn_auth_cred_ssl_client_cert_t **cred_p,
333 void *baton,
334 const char *realm,
335 svn_boolean_t may_save,
336 apr_pool_t *pool)
338 svn_auth_cred_ssl_client_cert_t *cred = NULL;
339 const char *cert_file = NULL;
340 svn_cmdline_prompt_baton_t *pb = baton;
342 SVN_ERR(maybe_print_realm(realm, pool));
343 SVN_ERR(prompt(&cert_file, _("Client certificate filename: "),
344 FALSE, pb, pool));
346 cred = apr_palloc(pool, sizeof(*cred));
347 cred->cert_file = cert_file;
348 cred->may_save = may_save;
349 *cred_p = cred;
351 return SVN_NO_ERROR;
355 /* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */
356 svn_error_t *
357 svn_cmdline_auth_ssl_client_cert_pw_prompt
358 (svn_auth_cred_ssl_client_cert_pw_t **cred_p,
359 void *baton,
360 const char *realm,
361 svn_boolean_t may_save,
362 apr_pool_t *pool)
364 svn_auth_cred_ssl_client_cert_pw_t *cred = NULL;
365 const char *result;
366 const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm);
367 svn_cmdline_prompt_baton_t *pb = baton;
369 SVN_ERR(prompt(&result, text, TRUE, pb, pool));
371 cred = apr_pcalloc(pool, sizeof(*cred));
372 cred->password = result;
373 cred->may_save = may_save;
374 *cred_p = cred;
376 return SVN_NO_ERROR;
381 /** Generic prompting. **/
383 svn_error_t *
384 svn_cmdline_prompt_user2(const char **result,
385 const char *prompt_str,
386 svn_cmdline_prompt_baton_t *baton,
387 apr_pool_t *pool)
389 return prompt(result, prompt_str, FALSE /* don't hide input */, baton, pool);
393 svn_error_t *
394 svn_cmdline_prompt_user(const char **result,
395 const char *prompt_str,
396 apr_pool_t *pool)
398 return svn_cmdline_prompt_user2(result, prompt_str, NULL, pool);