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 110 : status_currently_allowed (int no)
71 : {
72 110 : if (!glo_ctrl.in_auto_key_retrieve)
73 110 : 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 14 : set_status_fd (int fd)
98 : {
99 : static int last_fd = -1;
100 :
101 14 : if (fd != -1 && last_fd == fd)
102 0 : return;
103 :
104 14 : if (statusfp && statusfp != es_stdout && statusfp != es_stderr )
105 0 : es_fclose (statusfp);
106 14 : statusfp = NULL;
107 14 : if (fd == -1)
108 0 : return;
109 :
110 14 : if (fd == 1)
111 14 : statusfp = es_stdout;
112 0 : else if (fd == 2)
113 0 : statusfp = es_stderr;
114 : else
115 0 : statusfp = es_fdopen (fd, "w");
116 14 : if (!statusfp)
117 : {
118 0 : log_fatal ("can't open fd %d for status output: %s\n",
119 0 : fd, strerror (errno));
120 : }
121 14 : last_fd = fd;
122 :
123 14 : gcry_set_progress_handler (progress_cb, NULL);
124 : }
125 :
126 :
127 : int
128 4027 : is_status_enabled ()
129 : {
130 4027 : return !!statusfp;
131 : }
132 :
133 :
134 : void
135 2331 : write_status ( int no )
136 : {
137 2331 : write_status_text( no, NULL );
138 2331 : }
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 3653 : write_status_strings (int no, const char *text, ...)
145 : {
146 : va_list arg_ptr;
147 : const char *s;
148 :
149 3653 : if (!statusfp || !status_currently_allowed (no) )
150 7235 : return; /* Not enabled or allowed. */
151 :
152 71 : es_fputs ("[GNUPG:] ", statusfp);
153 71 : es_fputs (get_status_string (no), statusfp);
154 71 : if ( text )
155 : {
156 58 : es_putc ( ' ', statusfp);
157 58 : va_start (arg_ptr, text);
158 58 : s = text;
159 : do
160 : {
161 2072 : for (; *s; s++)
162 : {
163 1986 : if (*s == '\n')
164 0 : es_fputs ("\\n", statusfp);
165 1986 : else if (*s == '\r')
166 0 : es_fputs ("\\r", statusfp);
167 : else
168 1986 : es_fputc (*(const byte *)s, statusfp);
169 : }
170 : }
171 86 : while ((s = va_arg (arg_ptr, const char*)));
172 58 : va_end (arg_ptr);
173 : }
174 71 : es_putc ('\n', statusfp);
175 71 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
176 0 : g10_exit (0);
177 : }
178 :
179 :
180 : void
181 3616 : write_status_text (int no, const char *text)
182 : {
183 3616 : write_status_strings (no, text, NULL);
184 3616 : }
185 :
186 :
187 : /* Write a status line with code NO followed by the outout of the
188 : * printf style FORMAT. The caller needs to make sure that LFs and
189 : * CRs are not printed. */
190 : void
191 16 : write_status_printf (int no, const char *format, ...)
192 : {
193 : va_list arg_ptr;
194 :
195 16 : if (!statusfp || !status_currently_allowed (no) )
196 19 : return; /* Not enabled or allowed. */
197 :
198 13 : es_fputs ("[GNUPG:] ", statusfp);
199 13 : es_fputs (get_status_string (no), statusfp);
200 13 : if (format)
201 : {
202 13 : es_putc ( ' ', statusfp);
203 13 : va_start (arg_ptr, format);
204 13 : es_vfprintf (statusfp, format, arg_ptr);
205 13 : va_end (arg_ptr);
206 : }
207 13 : es_putc ('\n', statusfp);
208 13 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
209 0 : g10_exit (0);
210 : }
211 :
212 :
213 : /* Write an ERROR status line using a full gpg-error error value. */
214 : void
215 0 : write_status_error (const char *where, gpg_error_t err)
216 : {
217 0 : if (!statusfp || !status_currently_allowed (STATUS_ERROR))
218 0 : return; /* Not enabled or allowed. */
219 :
220 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
221 : get_status_string (STATUS_ERROR), where, err);
222 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
223 0 : g10_exit (0);
224 : }
225 :
226 :
227 : /* Same as above but outputs the error code only. */
228 : void
229 0 : write_status_errcode (const char *where, int errcode)
230 : {
231 0 : if (!statusfp || !status_currently_allowed (STATUS_ERROR))
232 0 : return; /* Not enabled or allowed. */
233 :
234 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
235 0 : get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
236 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
237 0 : g10_exit (0);
238 : }
239 :
240 :
241 : /* Write a FAILURE status line. */
242 : void
243 0 : write_status_failure (const char *where, gpg_error_t err)
244 : {
245 0 : if (!statusfp || !status_currently_allowed (STATUS_FAILURE))
246 0 : return; /* Not enabled or allowed. */
247 :
248 0 : es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
249 : get_status_string (STATUS_FAILURE), where, err);
250 0 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
251 0 : g10_exit (0);
252 : }
253 :
254 :
255 : /*
256 : * Write a status line with a buffer using %XX escapes. If WRAP is >
257 : * 0 wrap the line after this length. If STRING is not NULL it will
258 : * be prepended to the buffer, no escaping is done for string.
259 : * A wrap of -1 forces spaces not to be encoded as %20.
260 : */
261 : void
262 199 : write_status_text_and_buffer (int no, const char *string,
263 : const char *buffer, size_t len, int wrap)
264 : {
265 : const char *s, *text;
266 : int esc, first;
267 199 : int lower_limit = ' ';
268 : size_t n, count, dowrap;
269 :
270 199 : if (!statusfp || !status_currently_allowed (no))
271 372 : return; /* Not enabled or allowed. */
272 :
273 26 : if (wrap == -1)
274 : {
275 13 : lower_limit--;
276 13 : wrap = 0;
277 : }
278 :
279 26 : text = get_status_string (no);
280 26 : count = dowrap = first = 1;
281 : do
282 : {
283 26 : if (dowrap)
284 : {
285 26 : es_fprintf (statusfp, "[GNUPG:] %s ", text);
286 26 : count = dowrap = 0;
287 26 : if (first && string)
288 : {
289 26 : es_fputs (string, statusfp);
290 26 : count += strlen (string);
291 : /* Make sure that there is a space after the string. */
292 26 : if (*string && string[strlen (string)-1] != ' ')
293 : {
294 0 : es_putc (' ', statusfp);
295 0 : count++;
296 : }
297 : }
298 26 : first = 0;
299 : }
300 481 : for (esc=0, s=buffer, n=len; n && !esc; s++, n--)
301 : {
302 455 : if (*s == '%' || *(const byte*)s <= lower_limit
303 455 : || *(const byte*)s == 127 )
304 0 : esc = 1;
305 455 : if (wrap && ++count > wrap)
306 : {
307 0 : dowrap=1;
308 0 : break;
309 : }
310 : }
311 26 : if (esc)
312 : {
313 0 : s--; n++;
314 : }
315 26 : if (s != buffer)
316 13 : es_fwrite (buffer, s-buffer, 1, statusfp);
317 26 : if ( esc )
318 : {
319 0 : es_fprintf (statusfp, "%%%02X", *(const byte*)s );
320 0 : s++; n--;
321 : }
322 26 : buffer = s;
323 26 : len = n;
324 26 : if (dowrap && len)
325 0 : es_putc ('\n', statusfp);
326 : }
327 26 : while (len);
328 :
329 26 : es_putc ('\n',statusfp);
330 26 : if (es_fflush (statusfp) && opt.exit_on_status_write_error)
331 0 : g10_exit (0);
332 : }
333 :
334 :
335 : void
336 20 : write_status_buffer (int no, const char *buffer, size_t len, int wrap)
337 : {
338 20 : write_status_text_and_buffer (no, NULL, buffer, len, wrap);
339 20 : }
340 :
341 :
342 : /* Print the BEGIN_SIGNING status message. If MD is not NULL it is
343 : used to retrieve the hash algorithms used for the message. */
344 : void
345 131 : write_status_begin_signing (gcry_md_hd_t md)
346 : {
347 131 : if (md)
348 : {
349 : char buf[100];
350 : size_t buflen;
351 : int i, ga;
352 :
353 131 : buflen = 0;
354 14541 : for (i=1; i <= 110; i++)
355 : {
356 14410 : ga = map_md_openpgp_to_gcry (i);
357 14410 : if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf))
358 : {
359 131 : snprintf (buf+buflen, DIM(buf) - buflen - 1,
360 : "%sH%d", buflen? " ":"",i);
361 131 : buflen += strlen (buf+buflen);
362 : }
363 : }
364 131 : write_status_text (STATUS_BEGIN_SIGNING, buf);
365 : }
366 : else
367 0 : write_status ( STATUS_BEGIN_SIGNING );
368 131 : }
369 :
370 :
371 : static int
372 0 : myread(int fd, void *buf, size_t count)
373 : {
374 : int rc;
375 : do
376 : {
377 0 : rc = read( fd, buf, count );
378 : }
379 0 : while (rc == -1 && errno == EINTR);
380 :
381 0 : if (!rc && count)
382 : {
383 : static int eof_emmited=0;
384 0 : if ( eof_emmited < 3 )
385 : {
386 0 : *(char*)buf = CONTROL_D;
387 0 : rc = 1;
388 0 : eof_emmited++;
389 : }
390 : else /* Ctrl-D not caught - do something reasonable */
391 : {
392 : #ifdef HAVE_DOSISH_SYSTEM
393 : #ifndef HAVE_W32CE_SYSTEM
394 : raise (SIGINT); /* Nothing to hangup under DOS. */
395 : #endif
396 : #else
397 0 : raise (SIGHUP); /* No more input data. */
398 : #endif
399 : }
400 : }
401 0 : return rc;
402 : }
403 :
404 :
405 :
406 : /* Request a string from the client over the command-fd. If GETBOOL
407 : is set the function returns a static string (do not free) if the
408 : netered value was true or NULL if the entered value was false. */
409 : static char *
410 0 : do_get_from_fd ( const char *keyword, int hidden, int getbool )
411 : {
412 : int i, len;
413 : char *string;
414 :
415 0 : if (statusfp != es_stdout)
416 0 : es_fflush (es_stdout);
417 :
418 0 : write_status_text (getbool? STATUS_GET_BOOL :
419 0 : hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
420 :
421 0 : for (string = NULL, i = len = 200; ; i++ )
422 : {
423 0 : if (i >= len-1 )
424 : {
425 0 : char *save = string;
426 0 : len += 100;
427 0 : string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
428 0 : if (save)
429 0 : memcpy (string, save, i );
430 : else
431 0 : i = 0;
432 : }
433 : /* Fixme: why not use our read_line function here? */
434 0 : if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
435 : break;
436 0 : else if ( string[i] == CONTROL_D )
437 : {
438 : /* Found ETX - Cancel the line and return a sole ETX. */
439 0 : string[0] = CONTROL_D;
440 0 : i = 1;
441 0 : break;
442 : }
443 0 : }
444 0 : string[i] = 0;
445 :
446 0 : write_status (STATUS_GOT_IT);
447 :
448 0 : if (getbool) /* Fixme: is this correct??? */
449 0 : return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
450 :
451 0 : return string;
452 : }
453 :
454 :
455 :
456 : int
457 0 : cpr_enabled()
458 : {
459 0 : if( opt.command_fd != -1 )
460 0 : return 1;
461 0 : return 0;
462 : }
463 :
464 : char *
465 0 : cpr_get_no_help( const char *keyword, const char *prompt )
466 : {
467 : char *p;
468 :
469 0 : if( opt.command_fd != -1 )
470 0 : return do_get_from_fd ( keyword, 0, 0 );
471 : for(;;) {
472 0 : p = tty_get( prompt );
473 0 : return p;
474 : }
475 : }
476 :
477 : char *
478 0 : cpr_get( const char *keyword, const char *prompt )
479 : {
480 : char *p;
481 :
482 0 : if( opt.command_fd != -1 )
483 0 : return do_get_from_fd ( keyword, 0, 0 );
484 : for(;;) {
485 0 : p = tty_get( prompt );
486 0 : if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
487 0 : xfree(p);
488 0 : display_online_help( keyword );
489 : }
490 : else
491 0 : return p;
492 0 : }
493 : }
494 :
495 :
496 : char *
497 0 : cpr_get_utf8( const char *keyword, const char *prompt )
498 : {
499 : char *p;
500 0 : p = cpr_get( keyword, prompt );
501 0 : if( p ) {
502 0 : char *utf8 = native_to_utf8( p );
503 0 : xfree( p );
504 0 : p = utf8;
505 : }
506 0 : return p;
507 : }
508 :
509 : char *
510 0 : cpr_get_hidden( const char *keyword, const char *prompt )
511 : {
512 : char *p;
513 :
514 0 : if( opt.command_fd != -1 )
515 0 : return do_get_from_fd ( keyword, 1, 0 );
516 : for(;;) {
517 0 : p = tty_get_hidden( prompt );
518 0 : if( *p == '?' && !p[1] ) {
519 0 : xfree(p);
520 0 : display_online_help( keyword );
521 : }
522 : else
523 0 : return p;
524 0 : }
525 : }
526 :
527 : void
528 0 : cpr_kill_prompt(void)
529 : {
530 0 : if( opt.command_fd != -1 )
531 0 : return;
532 0 : tty_kill_prompt();
533 0 : return;
534 : }
535 :
536 : int
537 0 : cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes)
538 : {
539 : int yes;
540 : char *p;
541 :
542 0 : if( opt.command_fd != -1 )
543 0 : return !!do_get_from_fd ( keyword, 0, 1 );
544 : for(;;) {
545 0 : p = tty_get( prompt );
546 0 : trim_spaces(p); /* it is okay to do this here */
547 0 : if( *p == '?' && !p[1] ) {
548 0 : xfree(p);
549 0 : display_online_help( keyword );
550 : }
551 : else {
552 0 : tty_kill_prompt();
553 0 : yes = answer_is_yes_no_default (p, def_yes);
554 0 : xfree(p);
555 0 : return yes;
556 : }
557 0 : }
558 : }
559 :
560 : int
561 0 : cpr_get_answer_is_yes (const char *keyword, const char *prompt)
562 : {
563 0 : return cpr_get_answer_is_yes_def (keyword, prompt, 0);
564 : }
565 :
566 : int
567 0 : cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
568 : {
569 : int yes;
570 : char *p;
571 :
572 0 : if( opt.command_fd != -1 )
573 0 : return !!do_get_from_fd ( keyword, 0, 1 );
574 : for(;;) {
575 0 : p = tty_get( prompt );
576 0 : trim_spaces(p); /* it is okay to do this here */
577 0 : if( *p == '?' && !p[1] ) {
578 0 : xfree(p);
579 0 : display_online_help( keyword );
580 : }
581 : else {
582 0 : tty_kill_prompt();
583 0 : yes = answer_is_yes_no_quit(p);
584 0 : xfree(p);
585 0 : return yes;
586 : }
587 0 : }
588 : }
589 :
590 :
591 : int
592 0 : cpr_get_answer_okay_cancel (const char *keyword,
593 : const char *prompt,
594 : int def_answer)
595 : {
596 : int yes;
597 0 : char *answer = NULL;
598 : char *p;
599 :
600 0 : if( opt.command_fd != -1 )
601 0 : answer = do_get_from_fd ( keyword, 0, 0 );
602 :
603 0 : if (answer)
604 : {
605 0 : yes = answer_is_okay_cancel (answer, def_answer);
606 0 : xfree (answer);
607 0 : return yes;
608 : }
609 :
610 : for(;;)
611 : {
612 0 : p = tty_get( prompt );
613 0 : trim_spaces(p); /* it is okay to do this here */
614 0 : if (*p == '?' && !p[1])
615 : {
616 0 : xfree(p);
617 0 : display_online_help (keyword);
618 : }
619 : else
620 : {
621 0 : tty_kill_prompt();
622 0 : yes = answer_is_okay_cancel (p, def_answer);
623 0 : xfree(p);
624 0 : return yes;
625 : }
626 0 : }
627 : }
|