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