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