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