Line data Source code
1 : /* status.c - Status message and command-fd interface
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3 : * 2004, 2005, 2006, 2010 Free Software Foundation, Inc.
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * GnuPG is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 3 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * GnuPG is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <errno.h>
26 : #include <unistd.h>
27 : #ifdef HAVE_SIGNAL_H
28 : # include <signal.h>
29 : #endif
30 :
31 : #include "gpg.h"
32 : #include "util.h"
33 : #include "status.h"
34 : #include "ttyio.h"
35 : #include "options.h"
36 : #include "main.h"
37 : #include "i18n.h"
38 :
39 : #define CONTROL_D ('D' - 'A' + 1)
40 :
41 :
42 : /* The stream to output the status information. Output is disabled if
43 : this is NULL. */
44 : static estream_t statusfp;
45 :
46 :
47 : static void
48 0 : progress_cb (void *ctx, const char *what, int printchar,
49 : int current, int total)
50 : {
51 : char buf[50];
52 :
53 : (void)ctx;
54 :
55 0 : if ( printchar == '\n' && !strcmp (what, "primegen") )
56 0 : snprintf (buf, sizeof buf -1, "%.20s X 100 100", what );
57 : else
58 0 : snprintf (buf, sizeof buf -1, "%.20s %c %d %d",
59 : what, printchar=='\n'?'X':printchar, current, total );
60 0 : write_status_text (STATUS_PROGRESS, buf);
61 0 : }
62 :
63 :
64 : /* Return true if the status message NO may currently be issued. We
65 : need this to avoid syncronisation problem while auto retrieving a
66 : key. There it may happen that a status NODATA is issued for a non
67 : available key and the user may falsely interpret this has a missing
68 : signature. */
69 : static int
70 0 : status_currently_allowed (int no)
71 : {
72 0 : if (!glo_ctrl.in_auto_key_retrieve)
73 0 : return 1; /* Yes. */
74 :
75 : /* We allow some statis anyway, so that import statistics are
76 : correct and to avoid problems if the retriebval subsystem will
77 : prompt the user. */
78 0 : switch (no)
79 : {
80 : case STATUS_GET_BOOL:
81 : case STATUS_GET_LINE:
82 : case STATUS_GET_HIDDEN:
83 : case STATUS_GOT_IT:
84 : case STATUS_IMPORTED:
85 : case STATUS_IMPORT_OK:
86 : case STATUS_IMPORT_CHECK:
87 : case STATUS_IMPORT_RES:
88 0 : return 1; /* Yes. */
89 : default:
90 0 : break;
91 : }
92 0 : return 0; /* No. */
93 : }
94 :
95 :
96 : void
97 0 : set_status_fd (int fd)
98 : {
99 : static int last_fd = -1;
100 :
101 0 : if (fd != -1 && last_fd == fd)
102 0 : return;
103 :
104 0 : if (statusfp && statusfp != es_stdout && statusfp != es_stderr )
105 0 : es_fclose (statusfp);
106 0 : statusfp = NULL;
107 0 : if (fd == -1)
108 0 : return;
109 :
110 0 : if (fd == 1)
111 0 : statusfp = es_stdout;
112 0 : else if (fd == 2)
113 0 : statusfp = es_stderr;
114 : else
115 0 : statusfp = es_fdopen (fd, "w");
116 0 : if (!statusfp)
117 : {
118 0 : log_fatal ("can't open fd %d for status output: %s\n",
119 0 : fd, strerror (errno));
120 : }
121 0 : last_fd = fd;
122 :
123 0 : gcry_set_progress_handler (progress_cb, NULL);
124 : }
125 :
126 :
127 : int
128 1734 : is_status_enabled ()
129 : {
130 1734 : return !!statusfp;
131 : }
132 :
133 :
134 : void
135 2424 : write_status ( int no )
136 : {
137 2424 : write_status_text( no, NULL );
138 2424 : }
139 :
140 :
141 : /* Write a status line with code NO followed by the string TEXT and
142 : directly followed by the remaining strings up to a NULL. */
143 : void
144 3635 : write_status_strings (int no, const char *text, ...)
145 : {
146 : va_list arg_ptr;
147 : const char *s;
148 :
149 3635 : if (!statusfp || !status_currently_allowed (no) )
150 7270 : return; /* Not enabled or allowed. */
151 :
152 0 : es_fputs ("[GNUPG:] ", statusfp);
153 0 : es_fputs (get_status_string (no), statusfp);
154 0 : if ( text )
155 : {
156 0 : es_putc ( ' ', statusfp);
157 0 : va_start (arg_ptr, text);
158 0 : s = text;
159 : do
160 : {
161 0 : for (; *s; s++)
162 : {
163 0 : if (*s == '\n')
164 0 : es_fputs ("\\n", statusfp);
165 0 : else if (*s == '\r')
166 0 : es_fputs ("\\r", statusfp);
167 : else
168 0 : es_fputc (*(const byte *)s, statusfp);
169 : }
170 : }
171 0 : while ((s = va_arg (arg_ptr, const char*)));
172 0 : va_end (arg_ptr);
173 : }
174 0 : es_putc ('\n', statusfp);
175 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
176 0 : g10_exit (0);
177 : }
178 :
179 :
180 : void
181 3635 : write_status_text (int no, const char *text)
182 : {
183 3635 : write_status_strings (no, text, NULL);
184 3635 : }
185 :
186 : /* Write an ERROR status line using a full gpg-error error value. */
187 : void
188 0 : write_status_error (const char *where, gpg_error_t err)
189 : {
190 0 : if (!statusfp || !status_currently_allowed (STATUS_ERROR))
191 0 : return; /* Not enabled or allowed. */
192 :
193 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
194 : get_status_string (STATUS_ERROR), where, err);
195 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
196 0 : g10_exit (0);
197 : }
198 :
199 :
200 : /* Same as above but outputs the error code only. */
201 : void
202 0 : write_status_errcode (const char *where, int errcode)
203 : {
204 0 : if (!statusfp || !status_currently_allowed (STATUS_ERROR))
205 0 : return; /* Not enabled or allowed. */
206 :
207 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
208 0 : get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
209 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
210 0 : g10_exit (0);
211 : }
212 :
213 :
214 : /* Write a FAILURE status line. */
215 : void
216 0 : write_status_failure (const char *where, gpg_error_t err)
217 : {
218 0 : if (!statusfp || !status_currently_allowed (STATUS_FAILURE))
219 0 : return; /* Not enabled or allowed. */
220 :
221 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
222 : get_status_string (STATUS_FAILURE), where, err);
223 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
224 0 : g10_exit (0);
225 : }
226 :
227 :
228 : /*
229 : * Write a status line with a buffer using %XX escapes. If WRAP is >
230 : * 0 wrap the line after this length. If STRING is not NULL it will
231 : * be prepended to the buffer, no escaping is done for string.
232 : * A wrap of -1 forces spaces not to be encoded as %20.
233 : */
234 : void
235 147 : write_status_text_and_buffer (int no, const char *string,
236 : const char *buffer, size_t len, int wrap)
237 : {
238 : const char *s, *text;
239 : int esc, first;
240 147 : int lower_limit = ' ';
241 : size_t n, count, dowrap;
242 :
243 147 : if (!statusfp || !status_currently_allowed (no))
244 294 : return; /* Not enabled or allowed. */
245 :
246 0 : if (wrap == -1)
247 : {
248 0 : lower_limit--;
249 0 : wrap = 0;
250 : }
251 :
252 0 : text = get_status_string (no);
253 0 : count = dowrap = first = 1;
254 : do
255 : {
256 0 : if (dowrap)
257 : {
258 0 : es_fprintf (statusfp, "[GNUPG:] %s ", text);
259 0 : count = dowrap = 0;
260 0 : if (first && string)
261 : {
262 0 : es_fputs (string, statusfp);
263 0 : count += strlen (string);
264 : /* Make sure that there is a space after the string. */
265 0 : if (*string && string[strlen (string)-1] != ' ')
266 : {
267 0 : es_putc (' ', statusfp);
268 0 : count++;
269 : }
270 : }
271 0 : first = 0;
272 : }
273 0 : for (esc=0, s=buffer, n=len; n && !esc; s++, n--)
274 : {
275 0 : if (*s == '%' || *(const byte*)s <= lower_limit
276 0 : || *(const byte*)s == 127 )
277 0 : esc = 1;
278 0 : if (wrap && ++count > wrap)
279 : {
280 0 : dowrap=1;
281 0 : break;
282 : }
283 : }
284 0 : if (esc)
285 : {
286 0 : s--; n++;
287 : }
288 0 : if (s != buffer)
289 0 : es_fwrite (buffer, s-buffer, 1, statusfp);
290 0 : if ( esc )
291 : {
292 0 : es_fprintf (statusfp, "%%%02X", *(const byte*)s );
293 0 : s++; n--;
294 : }
295 0 : buffer = s;
296 0 : len = n;
297 0 : if (dowrap && len)
298 0 : es_putc ('\n', statusfp);
299 : }
300 0 : while (len);
301 :
302 0 : es_putc ('\n',statusfp);
303 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
304 0 : g10_exit (0);
305 : }
306 :
307 :
308 : void
309 0 : write_status_buffer (int no, const char *buffer, size_t len, int wrap)
310 : {
311 0 : write_status_text_and_buffer (no, NULL, buffer, len, wrap);
312 0 : }
313 :
314 :
315 : /* Print the BEGIN_SIGNING status message. If MD is not NULL it is
316 : used to retrieve the hash algorithms used for the message. */
317 : void
318 115 : write_status_begin_signing (gcry_md_hd_t md)
319 : {
320 115 : if (md)
321 : {
322 : char buf[100];
323 : size_t buflen;
324 : int i, ga;
325 :
326 115 : buflen = 0;
327 12765 : for (i=1; i <= 110; i++)
328 : {
329 12650 : ga = map_md_openpgp_to_gcry (i);
330 12650 : if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf))
331 : {
332 115 : snprintf (buf+buflen, DIM(buf) - buflen - 1,
333 : "%sH%d", buflen? " ":"",i);
334 115 : buflen += strlen (buf+buflen);
335 : }
336 : }
337 115 : write_status_text (STATUS_BEGIN_SIGNING, buf);
338 : }
339 : else
340 0 : write_status ( STATUS_BEGIN_SIGNING );
341 115 : }
342 :
343 :
344 : static int
345 0 : myread(int fd, void *buf, size_t count)
346 : {
347 : int rc;
348 : do
349 : {
350 0 : rc = read( fd, buf, count );
351 : }
352 0 : while (rc == -1 && errno == EINTR);
353 :
354 0 : if (!rc && count)
355 : {
356 : static int eof_emmited=0;
357 0 : if ( eof_emmited < 3 )
358 : {
359 0 : *(char*)buf = CONTROL_D;
360 0 : rc = 1;
361 0 : eof_emmited++;
362 : }
363 : else /* Ctrl-D not caught - do something reasonable */
364 : {
365 : #ifdef HAVE_DOSISH_SYSTEM
366 : #ifndef HAVE_W32CE_SYSTEM
367 : raise (SIGINT); /* Nothing to hangup under DOS. */
368 : #endif
369 : #else
370 0 : raise (SIGHUP); /* No more input data. */
371 : #endif
372 : }
373 : }
374 0 : return rc;
375 : }
376 :
377 :
378 :
379 : /* Request a string from the client over the command-fd. If GETBOOL
380 : is set the function returns a static string (do not free) if the
381 : netered value was true or NULL if the entered value was false. */
382 : static char *
383 0 : do_get_from_fd ( const char *keyword, int hidden, int getbool )
384 : {
385 : int i, len;
386 : char *string;
387 :
388 0 : if (statusfp != es_stdout)
389 0 : es_fflush (es_stdout);
390 :
391 0 : write_status_text (getbool? STATUS_GET_BOOL :
392 0 : hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
393 :
394 0 : for (string = NULL, i = len = 200; ; i++ )
395 : {
396 0 : if (i >= len-1 )
397 : {
398 0 : char *save = string;
399 0 : len += 100;
400 0 : string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
401 0 : if (save)
402 0 : memcpy (string, save, i );
403 : else
404 0 : i = 0;
405 : }
406 : /* Fixme: why not use our read_line function here? */
407 0 : if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
408 : break;
409 0 : else if ( string[i] == CONTROL_D )
410 : {
411 : /* Found ETX - Cancel the line and return a sole ETX. */
412 0 : string[0] = CONTROL_D;
413 0 : i = 1;
414 0 : break;
415 : }
416 0 : }
417 0 : string[i] = 0;
418 :
419 0 : write_status (STATUS_GOT_IT);
420 :
421 0 : if (getbool) /* Fixme: is this correct??? */
422 0 : return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
423 :
424 0 : return string;
425 : }
426 :
427 :
428 :
429 : int
430 0 : cpr_enabled()
431 : {
432 0 : if( opt.command_fd != -1 )
433 0 : return 1;
434 0 : return 0;
435 : }
436 :
437 : char *
438 0 : cpr_get_no_help( const char *keyword, const char *prompt )
439 : {
440 : char *p;
441 :
442 0 : if( opt.command_fd != -1 )
443 0 : return do_get_from_fd ( keyword, 0, 0 );
444 : for(;;) {
445 0 : p = tty_get( prompt );
446 0 : return p;
447 : }
448 : }
449 :
450 : char *
451 0 : cpr_get( const char *keyword, const char *prompt )
452 : {
453 : char *p;
454 :
455 0 : if( opt.command_fd != -1 )
456 0 : return do_get_from_fd ( keyword, 0, 0 );
457 : for(;;) {
458 0 : p = tty_get( prompt );
459 0 : if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
460 0 : xfree(p);
461 0 : display_online_help( keyword );
462 : }
463 : else
464 0 : return p;
465 0 : }
466 : }
467 :
468 :
469 : char *
470 0 : cpr_get_utf8( const char *keyword, const char *prompt )
471 : {
472 : char *p;
473 0 : p = cpr_get( keyword, prompt );
474 0 : if( p ) {
475 0 : char *utf8 = native_to_utf8( p );
476 0 : xfree( p );
477 0 : p = utf8;
478 : }
479 0 : return p;
480 : }
481 :
482 : char *
483 0 : cpr_get_hidden( const char *keyword, const char *prompt )
484 : {
485 : char *p;
486 :
487 0 : if( opt.command_fd != -1 )
488 0 : return do_get_from_fd ( keyword, 1, 0 );
489 : for(;;) {
490 0 : p = tty_get_hidden( prompt );
491 0 : if( *p == '?' && !p[1] ) {
492 0 : xfree(p);
493 0 : display_online_help( keyword );
494 : }
495 : else
496 0 : return p;
497 0 : }
498 : }
499 :
500 : void
501 0 : cpr_kill_prompt(void)
502 : {
503 0 : if( opt.command_fd != -1 )
504 0 : return;
505 0 : tty_kill_prompt();
506 0 : return;
507 : }
508 :
509 : int
510 0 : cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes)
511 : {
512 : int yes;
513 : char *p;
514 :
515 0 : if( opt.command_fd != -1 )
516 0 : return !!do_get_from_fd ( keyword, 0, 1 );
517 : for(;;) {
518 0 : p = tty_get( prompt );
519 0 : trim_spaces(p); /* it is okay to do this here */
520 0 : if( *p == '?' && !p[1] ) {
521 0 : xfree(p);
522 0 : display_online_help( keyword );
523 : }
524 : else {
525 0 : tty_kill_prompt();
526 0 : yes = answer_is_yes_no_default (p, def_yes);
527 0 : xfree(p);
528 0 : return yes;
529 : }
530 0 : }
531 : }
532 :
533 : int
534 0 : cpr_get_answer_is_yes (const char *keyword, const char *prompt)
535 : {
536 0 : return cpr_get_answer_is_yes_def (keyword, prompt, 0);
537 : }
538 :
539 : int
540 0 : cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
541 : {
542 : int yes;
543 : char *p;
544 :
545 0 : if( opt.command_fd != -1 )
546 0 : return !!do_get_from_fd ( keyword, 0, 1 );
547 : for(;;) {
548 0 : p = tty_get( prompt );
549 0 : trim_spaces(p); /* it is okay to do this here */
550 0 : if( *p == '?' && !p[1] ) {
551 0 : xfree(p);
552 0 : display_online_help( keyword );
553 : }
554 : else {
555 0 : tty_kill_prompt();
556 0 : yes = answer_is_yes_no_quit(p);
557 0 : xfree(p);
558 0 : return yes;
559 : }
560 0 : }
561 : }
562 :
563 :
564 : int
565 0 : cpr_get_answer_okay_cancel (const char *keyword,
566 : const char *prompt,
567 : int def_answer)
568 : {
569 : int yes;
570 0 : char *answer = NULL;
571 : char *p;
572 :
573 0 : if( opt.command_fd != -1 )
574 0 : answer = do_get_from_fd ( keyword, 0, 0 );
575 :
576 0 : if (answer)
577 : {
578 0 : yes = answer_is_okay_cancel (answer, def_answer);
579 0 : xfree (answer);
580 0 : return yes;
581 : }
582 :
583 : for(;;)
584 : {
585 0 : p = tty_get( prompt );
586 0 : trim_spaces(p); /* it is okay to do this here */
587 0 : if (*p == '?' && !p[1])
588 : {
589 0 : xfree(p);
590 0 : display_online_help (keyword);
591 : }
592 : else
593 : {
594 0 : tty_kill_prompt();
595 0 : yes = answer_is_okay_cancel (p, def_answer);
596 0 : xfree(p);
597 0 : return yes;
598 : }
599 0 : }
600 : }
|