gpg: Remove cipher.h and put algo ids into a common file.
[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;
311
312       /* We use a hard coded list of possible algorithms.  Using other
313          algorithms than specified by OpenPGP does not make sense
314          anyway.  We do this out of performance reasons: Walking all
315          the 110 allowed Ids is not a good idea given the way the
316          check is implemented in libgcrypt.  Recall that the only use
317          of this status code is to create the micalg algorithm for
318          PGP/MIME. */
319       buflen = 0;
320       for (i=1; i <= 11; i++)
321         if (i < 4 || i > 7)
322           if (gcry_md_is_enabled (md, i) && buflen < DIM(buf))
323             {
324               snprintf (buf+buflen, DIM(buf) - buflen - 1,
325                         "%sH%d", buflen? " ":"",i);
326               buflen += strlen (buf+buflen);
327             }
328       write_status_text (STATUS_BEGIN_SIGNING, buf);
329     }
330   else
331     write_status ( STATUS_BEGIN_SIGNING );
332 }
333
334
335 static int
336 myread(int fd, void *buf, size_t count)
337 {
338   int rc;
339   do
340     {
341       rc = read( fd, buf, count );
342     }
343   while (rc == -1 && errno == EINTR);
344
345   if (!rc && count)
346     {
347       static int eof_emmited=0;
348       if ( eof_emmited < 3 )
349         {
350           *(char*)buf = CONTROL_D;
351           rc = 1;
352           eof_emmited++;
353         }
354       else /* Ctrl-D not caught - do something reasonable */
355         {
356 #ifdef HAVE_DOSISH_SYSTEM
357 #ifndef HAVE_W32CE_SYSTEM
358           raise (SIGINT); /* Nothing to hangup under DOS.  */
359 #endif
360 #else
361           raise (SIGHUP); /* No more input data.  */
362 #endif
363         }
364     }
365   return rc;
366 }
367
368
369
370 /* Request a string from the client over the command-fd.  If GETBOOL
371    is set the function returns a static string (do not free) if the
372    netered value was true or NULL if the entered value was false.  */
373 static char *
374 do_get_from_fd ( const char *keyword, int hidden, int getbool )
375 {
376   int i, len;
377   char *string;
378
379   if (statusfp != es_stdout)
380     es_fflush (es_stdout);
381
382   write_status_text (getbool? STATUS_GET_BOOL :
383                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
384
385   for (string = NULL, i = len = 200; ; i++ )
386     {
387       if (i >= len-1 )
388         {
389           char *save = string;
390           len += 100;
391           string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
392           if (save)
393             memcpy (string, save, i );
394           else
395             i = 0;
396         }
397       /* Fixme: why not use our read_line function here? */
398       if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
399         break;
400       else if ( string[i] == CONTROL_D )
401         {
402           /* Found ETX - Cancel the line and return a sole ETX.  */
403           string[0] = CONTROL_D;
404           i = 1;
405           break;
406         }
407     }
408   string[i] = 0;
409
410   write_status (STATUS_GOT_IT);
411
412   if (getbool)   /* Fixme: is this correct??? */
413     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
414
415   return string;
416 }
417
418
419
420 int
421 cpr_enabled()
422 {
423     if( opt.command_fd != -1 )
424         return 1;
425     return 0;
426 }
427
428 char *
429 cpr_get_no_help( const char *keyword, const char *prompt )
430 {
431     char *p;
432
433     if( opt.command_fd != -1 )
434         return do_get_from_fd ( keyword, 0, 0 );
435     for(;;) {
436         p = tty_get( prompt );
437         return p;
438     }
439 }
440
441 char *
442 cpr_get( const char *keyword, const char *prompt )
443 {
444     char *p;
445
446     if( opt.command_fd != -1 )
447         return do_get_from_fd ( keyword, 0, 0 );
448     for(;;) {
449         p = tty_get( prompt );
450         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
451             xfree(p);
452             display_online_help( keyword );
453         }
454         else
455             return p;
456     }
457 }
458
459
460 char *
461 cpr_get_utf8( const char *keyword, const char *prompt )
462 {
463     char *p;
464     p = cpr_get( keyword, prompt );
465     if( p ) {
466         char *utf8 = native_to_utf8( p );
467         xfree( p );
468         p = utf8;
469     }
470     return p;
471 }
472
473 char *
474 cpr_get_hidden( const char *keyword, const char *prompt )
475 {
476     char *p;
477
478     if( opt.command_fd != -1 )
479         return do_get_from_fd ( keyword, 1, 0 );
480     for(;;) {
481         p = tty_get_hidden( prompt );
482         if( *p == '?' && !p[1] ) {
483             xfree(p);
484             display_online_help( keyword );
485         }
486         else
487             return p;
488     }
489 }
490
491 void
492 cpr_kill_prompt(void)
493 {
494     if( opt.command_fd != -1 )
495         return;
496     tty_kill_prompt();
497     return;
498 }
499
500 int
501 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
502 {
503     int yes;
504     char *p;
505
506     if( opt.command_fd != -1 )
507         return !!do_get_from_fd ( keyword, 0, 1 );
508     for(;;) {
509         p = tty_get( prompt );
510         trim_spaces(p); /* it is okay to do this here */
511         if( *p == '?' && !p[1] ) {
512             xfree(p);
513             display_online_help( keyword );
514         }
515         else {
516             tty_kill_prompt();
517             yes = answer_is_yes(p);
518             xfree(p);
519             return yes;
520         }
521     }
522 }
523
524 int
525 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
526 {
527     int yes;
528     char *p;
529
530     if( opt.command_fd != -1 )
531         return !!do_get_from_fd ( keyword, 0, 1 );
532     for(;;) {
533         p = tty_get( prompt );
534         trim_spaces(p); /* it is okay to do this here */
535         if( *p == '?' && !p[1] ) {
536             xfree(p);
537             display_online_help( keyword );
538         }
539         else {
540             tty_kill_prompt();
541             yes = answer_is_yes_no_quit(p);
542             xfree(p);
543             return yes;
544         }
545     }
546 }
547
548
549 int
550 cpr_get_answer_okay_cancel (const char *keyword,
551                             const char *prompt,
552                             int def_answer)
553 {
554   int yes;
555   char *answer = NULL;
556   char *p;
557
558   if( opt.command_fd != -1 )
559     answer = do_get_from_fd ( keyword, 0, 0 );
560
561   if (answer)
562     {
563       yes = answer_is_okay_cancel (answer, def_answer);
564       xfree (answer);
565       return yes;
566     }
567
568   for(;;)
569     {
570       p = tty_get( prompt );
571       trim_spaces(p); /* it is okay to do this here */
572       if (*p == '?' && !p[1])
573         {
574           xfree(p);
575           display_online_help (keyword);
576         }
577       else
578         {
579           tty_kill_prompt();
580           yes = answer_is_okay_cancel (p, def_answer);
581           xfree(p);
582           return yes;
583         }
584     }
585 }