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