g10: Fix segfault on unsupported curve.
[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 /* Write 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 /* Write a FAILURE status line.  */
215 void
216 write_status_failure (const char *where, gpg_error_t err)
217 {
218   if (!statusfp || !status_currently_allowed (STATUS_FAILURE))
219     return;  /* Not enabled or allowed. */
220
221   es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
222               get_status_string (STATUS_FAILURE), where, err);
223   if (es_fflush (statusfp) && opt.exit_on_status_write_error)
224     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 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   int lower_limit = ' ';
241   size_t n, count, dowrap;
242
243   if (!statusfp || !status_currently_allowed (no))
244     return;  /* Not enabled or allowed. */
245
246   if (wrap == -1)
247     {
248       lower_limit--;
249       wrap = 0;
250     }
251
252   text = get_status_string (no);
253   count = dowrap = first = 1;
254   do
255     {
256       if (dowrap)
257         {
258           es_fprintf (statusfp, "[GNUPG:] %s ", text);
259           count = dowrap = 0;
260           if (first && string)
261             {
262               es_fputs (string, statusfp);
263               count += strlen (string);
264               /* Make sure that there is a space after the string.  */
265               if (*string && string[strlen (string)-1] != ' ')
266                 {
267                   es_putc (' ', statusfp);
268                   count++;
269                 }
270             }
271           first = 0;
272         }
273       for (esc=0, s=buffer, n=len; n && !esc; s++, n--)
274         {
275           if (*s == '%' || *(const byte*)s <= lower_limit
276               || *(const byte*)s == 127 )
277             esc = 1;
278           if (wrap && ++count > wrap)
279             {
280               dowrap=1;
281               break;
282             }
283         }
284       if (esc)
285         {
286           s--; n++;
287         }
288       if (s != buffer)
289         es_fwrite (buffer, s-buffer, 1, statusfp);
290       if ( esc )
291         {
292           es_fprintf (statusfp, "%%%02X", *(const byte*)s );
293           s++; n--;
294         }
295       buffer = s;
296       len = n;
297       if (dowrap && len)
298         es_putc ('\n', statusfp);
299     }
300   while (len);
301
302   es_putc ('\n',statusfp);
303   if (es_fflush (statusfp) && opt.exit_on_status_write_error)
304     g10_exit (0);
305 }
306
307
308 void
309 write_status_buffer (int no, const char *buffer, size_t len, int wrap)
310 {
311   write_status_text_and_buffer (no, NULL, buffer, len, wrap);
312 }
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 write_status_begin_signing (gcry_md_hd_t md)
319 {
320   if (md)
321     {
322       char buf[100];
323       size_t buflen;
324       int i, ga;
325
326       buflen = 0;
327       for (i=1; i <= 110; i++)
328         {
329           ga = map_md_openpgp_to_gcry (i);
330           if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf))
331             {
332               snprintf (buf+buflen, DIM(buf) - buflen - 1,
333                         "%sH%d", buflen? " ":"",i);
334               buflen += strlen (buf+buflen);
335             }
336         }
337       write_status_text (STATUS_BEGIN_SIGNING, buf);
338     }
339   else
340     write_status ( STATUS_BEGIN_SIGNING );
341 }
342
343
344 static int
345 myread(int fd, void *buf, size_t count)
346 {
347   int rc;
348   do
349     {
350       rc = read( fd, buf, count );
351     }
352   while (rc == -1 && errno == EINTR);
353
354   if (!rc && count)
355     {
356       static int eof_emmited=0;
357       if ( eof_emmited < 3 )
358         {
359           *(char*)buf = CONTROL_D;
360           rc = 1;
361           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           raise (SIGHUP); /* No more input data.  */
371 #endif
372         }
373     }
374   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 do_get_from_fd ( const char *keyword, int hidden, int getbool )
384 {
385   int i, len;
386   char *string;
387
388   if (statusfp != es_stdout)
389     es_fflush (es_stdout);
390
391   write_status_text (getbool? STATUS_GET_BOOL :
392                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
393
394   for (string = NULL, i = len = 200; ; i++ )
395     {
396       if (i >= len-1 )
397         {
398           char *save = string;
399           len += 100;
400           string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
401           if (save)
402             memcpy (string, save, i );
403           else
404             i = 0;
405         }
406       /* Fixme: why not use our read_line function here? */
407       if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
408         break;
409       else if ( string[i] == CONTROL_D )
410         {
411           /* Found ETX - Cancel the line and return a sole ETX.  */
412           string[0] = CONTROL_D;
413           i = 1;
414           break;
415         }
416     }
417   string[i] = 0;
418
419   write_status (STATUS_GOT_IT);
420
421   if (getbool)   /* Fixme: is this correct??? */
422     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
423
424   return string;
425 }
426
427
428
429 int
430 cpr_enabled()
431 {
432     if( opt.command_fd != -1 )
433         return 1;
434     return 0;
435 }
436
437 char *
438 cpr_get_no_help( const char *keyword, const char *prompt )
439 {
440     char *p;
441
442     if( opt.command_fd != -1 )
443         return do_get_from_fd ( keyword, 0, 0 );
444     for(;;) {
445         p = tty_get( prompt );
446         return p;
447     }
448 }
449
450 char *
451 cpr_get( const char *keyword, const char *prompt )
452 {
453     char *p;
454
455     if( opt.command_fd != -1 )
456         return do_get_from_fd ( keyword, 0, 0 );
457     for(;;) {
458         p = tty_get( prompt );
459         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
460             xfree(p);
461             display_online_help( keyword );
462         }
463         else
464             return p;
465     }
466 }
467
468
469 char *
470 cpr_get_utf8( const char *keyword, const char *prompt )
471 {
472     char *p;
473     p = cpr_get( keyword, prompt );
474     if( p ) {
475         char *utf8 = native_to_utf8( p );
476         xfree( p );
477         p = utf8;
478     }
479     return p;
480 }
481
482 char *
483 cpr_get_hidden( const char *keyword, const char *prompt )
484 {
485     char *p;
486
487     if( opt.command_fd != -1 )
488         return do_get_from_fd ( keyword, 1, 0 );
489     for(;;) {
490         p = tty_get_hidden( prompt );
491         if( *p == '?' && !p[1] ) {
492             xfree(p);
493             display_online_help( keyword );
494         }
495         else
496             return p;
497     }
498 }
499
500 void
501 cpr_kill_prompt(void)
502 {
503     if( opt.command_fd != -1 )
504         return;
505     tty_kill_prompt();
506     return;
507 }
508
509 int
510 cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes)
511 {
512     int yes;
513     char *p;
514
515     if( opt.command_fd != -1 )
516         return !!do_get_from_fd ( keyword, 0, 1 );
517     for(;;) {
518         p = tty_get( prompt );
519         trim_spaces(p); /* it is okay to do this here */
520         if( *p == '?' && !p[1] ) {
521             xfree(p);
522             display_online_help( keyword );
523         }
524         else {
525             tty_kill_prompt();
526             yes = answer_is_yes_no_default (p, def_yes);
527             xfree(p);
528             return yes;
529         }
530     }
531 }
532
533 int
534 cpr_get_answer_is_yes (const char *keyword, const char *prompt)
535 {
536   return cpr_get_answer_is_yes_def (keyword, prompt, 0);
537 }
538
539 int
540 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
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_quit(p);
557             xfree(p);
558             return yes;
559         }
560     }
561 }
562
563
564 int
565 cpr_get_answer_okay_cancel (const char *keyword,
566                             const char *prompt,
567                             int def_answer)
568 {
569   int yes;
570   char *answer = NULL;
571   char *p;
572
573   if( opt.command_fd != -1 )
574     answer = do_get_from_fd ( keyword, 0, 0 );
575
576   if (answer)
577     {
578       yes = answer_is_okay_cancel (answer, def_answer);
579       xfree (answer);
580       return yes;
581     }
582
583   for(;;)
584     {
585       p = tty_get( prompt );
586       trim_spaces(p); /* it is okay to do this here */
587       if (*p == '?' && !p[1])
588         {
589           xfree(p);
590           display_online_help (keyword);
591         }
592       else
593         {
594           tty_kill_prompt();
595           yes = answer_is_okay_cancel (p, def_answer);
596           xfree(p);
597           return yes;
598         }
599     }
600 }