Added LIBINTL to more Makefile targets.
[gnupg.git] / g10 / status.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 2 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, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20  * USA.
21  */
22
23 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <signal.h>
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
44 static FILE *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   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 static const char *
62 get_status_string ( int no ) 
63 {
64   const char *s;
65
66   switch( no )
67     {
68     case STATUS_ENTER  : s = "ENTER"; break;
69     case STATUS_LEAVE  : s = "LEAVE"; break;
70     case STATUS_ABORT  : s = "ABORT"; break;
71     case STATUS_NEWSIG : s = "NEWSIG"; break;
72     case STATUS_GOODSIG: s = "GOODSIG"; break;
73     case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break;
74     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
75     case STATUS_BADSIG : s = "BADSIG"; break;
76     case STATUS_ERRSIG : s = "ERRSIG"; break;
77     case STATUS_BADARMOR : s = "BADARMOR"; break;
78     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
79     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
80     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
81     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
82     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
83     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
84     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
85     case STATUS_GET_LINE         : s = "GET_LINE"; break;
86     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
87     case STATUS_GOT_IT   : s = "GOT_IT"; break;
88     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
89     case STATUS_SHM_GET  : s = "SHM_GET"; break;
90     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
91     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
92     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
93     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
94     case STATUS_SIG_ID   : s = "SIG_ID"; break;
95     case STATUS_ENC_TO   : s = "ENC_TO"; break;
96     case STATUS_NODATA   : s = "NODATA"; break;
97     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
98     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
99     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
100     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
101     case STATUS_NEED_PASSPHRASE_PIN: s = "NEED_PASSPHRASE_PIN"; break;
102     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
103     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
104     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
105     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
106     case STATUS_GOODMDC  : s = "GOODMDC"; break;
107     case STATUS_BADMDC   : s = "BADMDC"; break;
108     case STATUS_ERRMDC   : s = "ERRMDC"; break;
109     case STATUS_IMPORTED         : s = "IMPORTED"; break;
110     case STATUS_IMPORT_OK        : s = "IMPORT_OK"; break;
111     case STATUS_IMPORT_CHECK   : s = "IMPORT_CHECK"; break;
112     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
113     case STATUS_FILE_START       : s = "FILE_START"; break;
114     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
115     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
116     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
117     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
118     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
119     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
120     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
121     case STATUS_PROGRESS       : s = "PROGRESS"; break;
122     case STATUS_SIG_CREATED    : s = "SIG_CREATED"; break;
123     case STATUS_SESSION_KEY    : s = "SESSION_KEY"; break;
124     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
125     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
126     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
127     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
128     case STATUS_END_STREAM     : s = "END_STREAM"; break;
129     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
130     case STATUS_KEY_NOT_CREATED: s = "KEY_NOT_CREATED"; break;
131     case STATUS_USERID_HINT    : s = "USERID_HINT"; break;
132     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
133     case STATUS_INV_RECP       : s = "INV_RECP"; break;
134     case STATUS_NO_RECP        : s = "NO_RECP"; break;
135     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
136     case STATUS_SIGEXPIRED     : s = "SIGEXPIRED deprecated-use-keyexpired-instead"; break;
137     case STATUS_EXPSIG         : s = "EXPSIG"; break;
138     case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
139     case STATUS_REVKEYSIG      : s = "REVKEYSIG"; break;
140     case STATUS_ATTRIBUTE      : s = "ATTRIBUTE"; break;
141     case STATUS_CARDCTRL       : s = "CARDCTRL"; break;
142     case STATUS_PLAINTEXT      : s = "PLAINTEXT"; break;
143     case STATUS_PLAINTEXT_LENGTH:s = "PLAINTEXT_LENGTH"; break;
144     case STATUS_SIG_SUBPACKET  : s = "SIG_SUBPACKET"; break;
145     case STATUS_SC_OP_SUCCESS  : s = "SC_OP_SUCCESS"; break;
146     case STATUS_SC_OP_FAILURE  : s = "SC_OP_FAILURE"; break;
147     case STATUS_BACKUP_KEY_CREATED:s="BACKUP_KEY_CREATED"; break;
148     case STATUS_PKA_TRUST_BAD  : s = "PKA_TRUST_BAD"; break;
149     case STATUS_PKA_TRUST_GOOD : s = "PKA_TRUST_GOOD"; break;
150     case STATUS_BEGIN_SIGNING  : s = "BEGIN_SIGNING"; break;
151     default: s = "?"; break;
152     }
153   return s;
154 }
155
156
157 /* Return true if the status message NO may currently be issued.  We
158    need this to avoid syncronisation problem while auto retrieving a
159    key.  There it may happen that a status NODATA is issued for a non
160    available key and the user may falsely interpret this has a missing
161    signature. */
162 static int
163 status_currently_allowed (int no)
164 {
165   if (!glo_ctrl.in_auto_key_retrieve)
166     return 1; /* Yes. */
167
168   /* We allow some statis anyway, so that import statistics are
169      correct and to avoid problems if the retriebval subsystem will
170      prompt the user. */
171   switch (no)
172     {
173     case STATUS_GET_BOOL:        
174     case STATUS_GET_LINE:        
175     case STATUS_GET_HIDDEN:      
176     case STATUS_GOT_IT:  
177     case STATUS_IMPORTED:
178     case STATUS_IMPORT_OK:      
179     case STATUS_IMPORT_CHECK:  
180     case STATUS_IMPORT_RES:
181       return 1; /* Yes. */
182     default:
183       break;
184     }
185   return 0; /* No. */
186 }
187
188
189 void
190 set_status_fd ( int fd )
191 {
192     static int last_fd = -1;
193
194     if ( fd != -1 && last_fd == fd )
195         return;
196
197     if ( statusfp && statusfp != stdout && statusfp != stderr )
198         fclose (statusfp);
199     statusfp = NULL;
200     if ( fd == -1 ) 
201         return;
202
203     if( fd == 1 )
204         statusfp = stdout;
205     else if( fd == 2 )
206         statusfp = stderr;
207     else
208         statusfp = fdopen( fd, "w" );
209     if( !statusfp ) {
210         log_fatal("can't open fd %d for status output: %s\n",
211                   fd, strerror(errno));
212     }
213     last_fd = fd;
214
215     gcry_set_progress_handler ( progress_cb, NULL );
216 }
217
218 int
219 is_status_enabled()
220 {
221     return !!statusfp;
222 }
223
224 void
225 write_status ( int no )
226 {
227     write_status_text( no, NULL );
228 }
229
230 void
231 write_status_text ( int no, const char *text)
232 {
233     if( !statusfp || !status_currently_allowed (no) )
234         return;  /* Not enabled or allowed. */
235
236     fputs ( "[GNUPG:] ", statusfp );
237     fputs ( get_status_string (no), statusfp );
238     if( text ) {
239         putc ( ' ', statusfp );
240         for (; *text; text++) {
241             if (*text == '\n')
242                 fputs ( "\\n", statusfp );
243             else if (*text == '\r')
244                 fputs ( "\\r", statusfp );
245             else 
246                 putc ( *(const byte *)text,  statusfp );
247         }
248     }
249     putc ('\n',statusfp);
250     if ( fflush (statusfp) && opt.exit_on_status_write_error )
251       g10_exit (0);
252 }
253
254
255 /*
256  * Write a status line with a buffer using %XX escapes.  If WRAP is >
257  * 0 wrap the line after this length.  If STRING is not NULL it will
258  * be prepended to the buffer, no escaping is done for string.
259  * A wrap of -1 forces spaces not to be encoded as %20.
260  */
261 void
262 write_status_text_and_buffer ( int no, const char *string,
263                                const char *buffer, size_t len, int wrap )
264 {
265     const char *s, *text;
266     int esc, first;
267     int lower_limit = ' ';
268     size_t n, count, dowrap;
269
270     if( !statusfp || !status_currently_allowed (no) )
271         return;  /* Not enabled or allowed. */
272     
273     if (wrap == -1) {
274         lower_limit--;
275         wrap = 0;
276     }
277
278     text = get_status_string (no);
279     count = dowrap = first = 1;
280     do {
281         if (dowrap) {
282             fprintf (statusfp, "[GNUPG:] %s ", text );
283             count = dowrap = 0;
284             if (first && string) {
285                 fputs (string, statusfp);
286                 count += strlen (string);
287             }
288             first = 0;
289         }
290         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
291             if ( *s == '%' || *(const byte*)s <= lower_limit 
292                            || *(const byte*)s == 127 ) 
293                 esc = 1;
294             if ( wrap && ++count > wrap ) {
295                 dowrap=1;
296                 break;
297             }
298         }
299         if (esc) {
300             s--; n++;
301         }
302         if (s != buffer) 
303             fwrite (buffer, s-buffer, 1, statusfp );
304         if ( esc ) {
305             fprintf (statusfp, "%%%02X", *(const byte*)s );
306             s++; n--;
307         }
308         buffer = s;
309         len = n;
310         if ( dowrap && len )
311             putc ( '\n', statusfp );
312     } while ( len );
313
314     putc ('\n',statusfp);
315     if ( fflush (statusfp) && opt.exit_on_status_write_error )
316       g10_exit (0);
317 }
318
319 void
320 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
321 {
322     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
323 }
324
325
326 /* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
327    used retrieve the hash algorithms used for the message. */
328 void
329 write_status_begin_signing (gcry_md_hd_t md)
330 {
331   if (md)
332     {
333       char buf[100];
334       size_t buflen;
335       int i;
336       
337       /* We use a hard coded list of possible algorithms.  Using other
338          algorithms than specified by OpenPGP does not make sense
339          anyway.  We do this out of performance reasons: Walking all
340          the 110 allowed Ids is not a good idea given the way the
341          check is implemented in libgcrypt.  Recall that the only use
342          of this status code is to create the micalg algorithm for
343          PGP/MIME. */
344       buflen = 0;
345       for (i=1; i <= 11; i++)
346         if (i < 4 || i > 7)
347           if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
348             {
349               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
350                         "%sH%d", buflen? " ":"",i);
351               buflen += strlen (buf+buflen);
352             }
353       write_status_text ( STATUS_BEGIN_SIGNING, buf );
354     }
355   else
356     write_status ( STATUS_BEGIN_SIGNING );
357 }
358
359
360 static int
361 myread(int fd, void *buf, size_t count)
362 {
363     int rc;
364     do {
365         rc = read( fd, buf, count );
366     } while ( rc == -1 && errno == EINTR );
367     if ( !rc && count ) {
368         static int eof_emmited=0;
369         if ( eof_emmited < 3 ) {
370             *(char*)buf = CONTROL_D;
371             rc = 1;
372             eof_emmited++;
373         }
374         else { /* Ctrl-D not caught - do something reasonable */
375 #ifdef HAVE_DOSISH_SYSTEM
376             raise (SIGINT);  /* nothing to hangup under DOS */
377 #else
378             raise (SIGHUP); /* no more input data */
379 #endif
380         }
381     }    
382     return rc;
383 }
384
385
386
387 /****************
388  * Request a string from the client over the command-fd
389  * If bool, returns static string on true (do not free) or NULL for false
390  */
391 static char *
392 do_get_from_fd( const char *keyword, int hidden, int bool )
393 {
394     int i, len;
395     char *string;
396
397     if(statusfp!=stdout)
398       fflush(stdout);
399
400     write_status_text( bool? STATUS_GET_BOOL :
401                        hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword );
402
403     for( string = NULL, i = len = 200; ; i++ ) {
404         if( i >= len-1 ) {
405             char *save = string;
406             len += 100;
407             string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
408             if( save )
409                 memcpy(string, save, i );
410             else
411                 i=0;
412         }
413         /* Hmmm: why not use our read_line function here */
414         if( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
415             break;
416         else if ( string[i] == CONTROL_D ) {
417             /* found ETX - cancel the line and return a sole ETX */
418             string[0] = CONTROL_D;
419             i=1;
420             break;
421         }
422     }
423     string[i] = 0;
424
425     write_status( STATUS_GOT_IT );
426
427     if( bool )   /* Fixme: is this correct??? */
428         return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
429
430     return string;
431 }
432
433
434
435 int
436 cpr_enabled()
437 {
438     if( opt.command_fd != -1 )
439         return 1;
440 #ifdef USE_SHM_COPROCESSING
441     if( opt.shm_coprocess )
442         return 1;
443 #endif
444     return 0;
445 }
446
447 char *
448 cpr_get_no_help( const char *keyword, const char *prompt )
449 {
450     char *p;
451
452     if( opt.command_fd != -1 )
453         return do_get_from_fd ( keyword, 0, 0 );
454 #ifdef USE_SHM_COPROCESSING
455     if( opt.shm_coprocess )
456         return do_shm_get( keyword, 0, 0 );
457 #endif
458     for(;;) {
459         p = tty_get( prompt );
460         return p;
461     }
462 }
463
464 char *
465 cpr_get( const char *keyword, const char *prompt )
466 {
467     char *p;
468
469     if( opt.command_fd != -1 )
470         return do_get_from_fd ( keyword, 0, 0 );
471 #ifdef USE_SHM_COPROCESSING
472     if( opt.shm_coprocess )
473         return do_shm_get( keyword, 0, 0 );
474 #endif
475     for(;;) {
476         p = tty_get( prompt );
477         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
478             xfree(p);
479             display_online_help( keyword );
480         }
481         else
482             return p;
483     }
484 }
485
486
487 char *
488 cpr_get_utf8( const char *keyword, const char *prompt )
489 {
490     char *p;
491     p = cpr_get( keyword, prompt );
492     if( p ) {
493         char *utf8 = native_to_utf8( p );
494         xfree( p );
495         p = utf8;
496     }
497     return p;
498 }
499
500 char *
501 cpr_get_hidden( const char *keyword, const char *prompt )
502 {
503     char *p;
504
505     if( opt.command_fd != -1 )
506         return do_get_from_fd ( keyword, 1, 0 );
507 #ifdef USE_SHM_COPROCESSING
508     if( opt.shm_coprocess )
509         return do_shm_get( keyword, 1, 0 );
510 #endif
511     for(;;) {
512         p = tty_get_hidden( prompt );
513         if( *p == '?' && !p[1] ) {
514             xfree(p);
515             display_online_help( keyword );
516         }
517         else
518             return p;
519     }
520 }
521
522 void
523 cpr_kill_prompt(void)
524 {
525     if( opt.command_fd != -1 )
526         return;
527 #ifdef USE_SHM_COPROCESSING
528     if( opt.shm_coprocess )
529         return;
530 #endif
531     tty_kill_prompt();
532     return;
533 }
534
535 int
536 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
537 {
538     int yes;
539     char *p;
540
541     if( opt.command_fd != -1 )
542         return !!do_get_from_fd ( keyword, 0, 1 );
543 #ifdef USE_SHM_COPROCESSING
544     if( opt.shm_coprocess )
545         return !!do_shm_get( keyword, 0, 1 );
546 #endif
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(p);
557             xfree(p);
558             return yes;
559         }
560     }
561 }
562
563 int
564 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
565 {
566     int yes;
567     char *p;
568
569     if( opt.command_fd != -1 )
570         return !!do_get_from_fd ( keyword, 0, 1 );
571 #ifdef USE_SHM_COPROCESSING
572     if( opt.shm_coprocess )
573         return !!do_shm_get( keyword, 0, 1 );
574 #endif
575     for(;;) {
576         p = tty_get( prompt );
577         trim_spaces(p); /* it is okay to do this here */
578         if( *p == '?' && !p[1] ) {
579             xfree(p);
580             display_online_help( keyword );
581         }
582         else {
583             tty_kill_prompt();
584             yes = answer_is_yes_no_quit(p);
585             xfree(p);
586             return yes;
587         }
588     }
589 }
590
591
592 int
593 cpr_get_answer_okay_cancel (const char *keyword,
594                             const char *prompt,
595                             int def_answer)
596 {
597   int yes;
598   char *answer = NULL;
599   char *p;
600
601   if( opt.command_fd != -1 )
602     answer = do_get_from_fd ( keyword, 0, 0 );
603 #ifdef USE_SHM_COPROCESSING
604   else if( opt.shm_coprocess )
605     answer = do_shm_get( keyword, 0, 0 );
606 #endif
607
608   if (answer)
609     {
610       yes = answer_is_okay_cancel (answer, def_answer);
611       xfree (answer);
612       return yes;
613     }
614
615   for(;;)
616     {
617       p = tty_get( prompt );
618       trim_spaces(p); /* it is okay to do this here */
619       if (*p == '?' && !p[1])
620         {
621           xfree(p);
622           display_online_help (keyword);
623         }
624       else
625         {
626           tty_kill_prompt();
627           yes = answer_is_okay_cancel (p, def_answer);
628           xfree(p);
629           return yes;
630         }
631     }
632 }