Add new logo.
[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     case STATUS_ERROR          : s = "ERROR"; break;
152     default: s = "?"; break;
153     }
154   return s;
155 }
156
157
158 /* Return true if the status message NO may currently be issued.  We
159    need this to avoid syncronisation problem while auto retrieving a
160    key.  There it may happen that a status NODATA is issued for a non
161    available key and the user may falsely interpret this has a missing
162    signature. */
163 static int
164 status_currently_allowed (int no)
165 {
166   if (!glo_ctrl.in_auto_key_retrieve)
167     return 1; /* Yes. */
168
169   /* We allow some statis anyway, so that import statistics are
170      correct and to avoid problems if the retriebval subsystem will
171      prompt the user. */
172   switch (no)
173     {
174     case STATUS_GET_BOOL:        
175     case STATUS_GET_LINE:        
176     case STATUS_GET_HIDDEN:      
177     case STATUS_GOT_IT:  
178     case STATUS_IMPORTED:
179     case STATUS_IMPORT_OK:      
180     case STATUS_IMPORT_CHECK:  
181     case STATUS_IMPORT_RES:
182       return 1; /* Yes. */
183     default:
184       break;
185     }
186   return 0; /* No. */
187 }
188
189
190 void
191 set_status_fd ( int fd )
192 {
193     static int last_fd = -1;
194
195     if ( fd != -1 && last_fd == fd )
196         return;
197
198     if ( statusfp && statusfp != stdout && statusfp != stderr )
199         fclose (statusfp);
200     statusfp = NULL;
201     if ( fd == -1 ) 
202         return;
203
204     if( fd == 1 )
205         statusfp = stdout;
206     else if( fd == 2 )
207         statusfp = stderr;
208     else
209         statusfp = fdopen( fd, "w" );
210     if( !statusfp ) {
211         log_fatal("can't open fd %d for status output: %s\n",
212                   fd, strerror(errno));
213     }
214     last_fd = fd;
215
216     gcry_set_progress_handler ( progress_cb, NULL );
217 }
218
219 int
220 is_status_enabled()
221 {
222     return !!statusfp;
223 }
224
225 void
226 write_status ( int no )
227 {
228     write_status_text( no, NULL );
229 }
230
231 void
232 write_status_text ( int no, const char *text)
233 {
234     if( !statusfp || !status_currently_allowed (no) )
235         return;  /* Not enabled or allowed. */
236
237     fputs ( "[GNUPG:] ", statusfp );
238     fputs ( get_status_string (no), statusfp );
239     if( text ) {
240         putc ( ' ', statusfp );
241         for (; *text; text++) {
242             if (*text == '\n')
243                 fputs ( "\\n", statusfp );
244             else if (*text == '\r')
245                 fputs ( "\\r", statusfp );
246             else 
247                 putc ( *(const byte *)text,  statusfp );
248         }
249     }
250     putc ('\n',statusfp);
251     if ( fflush (statusfp) && opt.exit_on_status_write_error )
252       g10_exit (0);
253 }
254
255
256 /*
257  * Write a status line with a buffer using %XX escapes.  If WRAP is >
258  * 0 wrap the line after this length.  If STRING is not NULL it will
259  * be prepended to the buffer, no escaping is done for string.
260  * A wrap of -1 forces spaces not to be encoded as %20.
261  */
262 void
263 write_status_text_and_buffer ( int no, const char *string,
264                                const char *buffer, size_t len, int wrap )
265 {
266     const char *s, *text;
267     int esc, first;
268     int lower_limit = ' ';
269     size_t n, count, dowrap;
270
271     if( !statusfp || !status_currently_allowed (no) )
272         return;  /* Not enabled or allowed. */
273     
274     if (wrap == -1) {
275         lower_limit--;
276         wrap = 0;
277     }
278
279     text = get_status_string (no);
280     count = dowrap = first = 1;
281     do {
282         if (dowrap) {
283             fprintf (statusfp, "[GNUPG:] %s ", text );
284             count = dowrap = 0;
285             if (first && string) {
286                 fputs (string, statusfp);
287                 count += strlen (string);
288             }
289             first = 0;
290         }
291         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
292             if ( *s == '%' || *(const byte*)s <= lower_limit 
293                            || *(const byte*)s == 127 ) 
294                 esc = 1;
295             if ( wrap && ++count > wrap ) {
296                 dowrap=1;
297                 break;
298             }
299         }
300         if (esc) {
301             s--; n++;
302         }
303         if (s != buffer) 
304             fwrite (buffer, s-buffer, 1, statusfp );
305         if ( esc ) {
306             fprintf (statusfp, "%%%02X", *(const byte*)s );
307             s++; n--;
308         }
309         buffer = s;
310         len = n;
311         if ( dowrap && len )
312             putc ( '\n', statusfp );
313     } while ( len );
314
315     putc ('\n',statusfp);
316     if ( fflush (statusfp) && opt.exit_on_status_write_error )
317       g10_exit (0);
318 }
319
320 void
321 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
322 {
323     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
324 }
325
326
327 /* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
328    used retrieve the hash algorithms used for the message. */
329 void
330 write_status_begin_signing (gcry_md_hd_t md)
331 {
332   if (md)
333     {
334       char buf[100];
335       size_t buflen;
336       int i;
337       
338       /* We use a hard coded list of possible algorithms.  Using other
339          algorithms than specified by OpenPGP does not make sense
340          anyway.  We do this out of performance reasons: Walking all
341          the 110 allowed Ids is not a good idea given the way the
342          check is implemented in libgcrypt.  Recall that the only use
343          of this status code is to create the micalg algorithm for
344          PGP/MIME. */
345       buflen = 0;
346       for (i=1; i <= 11; i++)
347         if (i < 4 || i > 7)
348           if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
349             {
350               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
351                         "%sH%d", buflen? " ":"",i);
352               buflen += strlen (buf+buflen);
353             }
354       write_status_text ( STATUS_BEGIN_SIGNING, buf );
355     }
356   else
357     write_status ( STATUS_BEGIN_SIGNING );
358 }
359
360
361 static int
362 myread(int fd, void *buf, size_t count)
363 {
364     int rc;
365     do {
366         rc = read( fd, buf, count );
367     } while ( rc == -1 && errno == EINTR );
368     if ( !rc && count ) {
369         static int eof_emmited=0;
370         if ( eof_emmited < 3 ) {
371             *(char*)buf = CONTROL_D;
372             rc = 1;
373             eof_emmited++;
374         }
375         else { /* Ctrl-D not caught - do something reasonable */
376 #ifdef HAVE_DOSISH_SYSTEM
377             raise (SIGINT);  /* nothing to hangup under DOS */
378 #else
379             raise (SIGHUP); /* no more input data */
380 #endif
381         }
382     }    
383     return rc;
384 }
385
386
387
388 /****************
389  * Request a string from the client over the command-fd
390  * If bool, returns static string on true (do not free) or NULL for false
391  */
392 static char *
393 do_get_from_fd( const char *keyword, int hidden, int bool )
394 {
395     int i, len;
396     char *string;
397
398     if(statusfp!=stdout)
399       fflush(stdout);
400
401     write_status_text( bool? STATUS_GET_BOOL :
402                        hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword );
403
404     for( string = NULL, i = len = 200; ; i++ ) {
405         if( i >= len-1 ) {
406             char *save = string;
407             len += 100;
408             string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
409             if( save )
410                 memcpy(string, save, i );
411             else
412                 i=0;
413         }
414         /* Hmmm: why not use our read_line function here */
415         if( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
416             break;
417         else if ( string[i] == CONTROL_D ) {
418             /* found ETX - cancel the line and return a sole ETX */
419             string[0] = CONTROL_D;
420             i=1;
421             break;
422         }
423     }
424     string[i] = 0;
425
426     write_status( STATUS_GOT_IT );
427
428     if( bool )   /* Fixme: is this correct??? */
429         return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
430
431     return string;
432 }
433
434
435
436 int
437 cpr_enabled()
438 {
439     if( opt.command_fd != -1 )
440         return 1;
441 #ifdef USE_SHM_COPROCESSING
442     if( opt.shm_coprocess )
443         return 1;
444 #endif
445     return 0;
446 }
447
448 char *
449 cpr_get_no_help( const char *keyword, const char *prompt )
450 {
451     char *p;
452
453     if( opt.command_fd != -1 )
454         return do_get_from_fd ( keyword, 0, 0 );
455 #ifdef USE_SHM_COPROCESSING
456     if( opt.shm_coprocess )
457         return do_shm_get( keyword, 0, 0 );
458 #endif
459     for(;;) {
460         p = tty_get( prompt );
461         return p;
462     }
463 }
464
465 char *
466 cpr_get( const char *keyword, const char *prompt )
467 {
468     char *p;
469
470     if( opt.command_fd != -1 )
471         return do_get_from_fd ( keyword, 0, 0 );
472 #ifdef USE_SHM_COPROCESSING
473     if( opt.shm_coprocess )
474         return do_shm_get( keyword, 0, 0 );
475 #endif
476     for(;;) {
477         p = tty_get( prompt );
478         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
479             xfree(p);
480             display_online_help( keyword );
481         }
482         else
483             return p;
484     }
485 }
486
487
488 char *
489 cpr_get_utf8( const char *keyword, const char *prompt )
490 {
491     char *p;
492     p = cpr_get( keyword, prompt );
493     if( p ) {
494         char *utf8 = native_to_utf8( p );
495         xfree( p );
496         p = utf8;
497     }
498     return p;
499 }
500
501 char *
502 cpr_get_hidden( const char *keyword, const char *prompt )
503 {
504     char *p;
505
506     if( opt.command_fd != -1 )
507         return do_get_from_fd ( keyword, 1, 0 );
508 #ifdef USE_SHM_COPROCESSING
509     if( opt.shm_coprocess )
510         return do_shm_get( keyword, 1, 0 );
511 #endif
512     for(;;) {
513         p = tty_get_hidden( prompt );
514         if( *p == '?' && !p[1] ) {
515             xfree(p);
516             display_online_help( keyword );
517         }
518         else
519             return p;
520     }
521 }
522
523 void
524 cpr_kill_prompt(void)
525 {
526     if( opt.command_fd != -1 )
527         return;
528 #ifdef USE_SHM_COPROCESSING
529     if( opt.shm_coprocess )
530         return;
531 #endif
532     tty_kill_prompt();
533     return;
534 }
535
536 int
537 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
538 {
539     int yes;
540     char *p;
541
542     if( opt.command_fd != -1 )
543         return !!do_get_from_fd ( keyword, 0, 1 );
544 #ifdef USE_SHM_COPROCESSING
545     if( opt.shm_coprocess )
546         return !!do_shm_get( keyword, 0, 1 );
547 #endif
548     for(;;) {
549         p = tty_get( prompt );
550         trim_spaces(p); /* it is okay to do this here */
551         if( *p == '?' && !p[1] ) {
552             xfree(p);
553             display_online_help( keyword );
554         }
555         else {
556             tty_kill_prompt();
557             yes = answer_is_yes(p);
558             xfree(p);
559             return yes;
560         }
561     }
562 }
563
564 int
565 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
566 {
567     int yes;
568     char *p;
569
570     if( opt.command_fd != -1 )
571         return !!do_get_from_fd ( keyword, 0, 1 );
572 #ifdef USE_SHM_COPROCESSING
573     if( opt.shm_coprocess )
574         return !!do_shm_get( keyword, 0, 1 );
575 #endif
576     for(;;) {
577         p = tty_get( prompt );
578         trim_spaces(p); /* it is okay to do this here */
579         if( *p == '?' && !p[1] ) {
580             xfree(p);
581             display_online_help( keyword );
582         }
583         else {
584             tty_kill_prompt();
585             yes = answer_is_yes_no_quit(p);
586             xfree(p);
587             return yes;
588         }
589     }
590 }
591
592
593 int
594 cpr_get_answer_okay_cancel (const char *keyword,
595                             const char *prompt,
596                             int def_answer)
597 {
598   int yes;
599   char *answer = NULL;
600   char *p;
601
602   if( opt.command_fd != -1 )
603     answer = do_get_from_fd ( keyword, 0, 0 );
604 #ifdef USE_SHM_COPROCESSING
605   else if( opt.shm_coprocess )
606     answer = do_shm_get( keyword, 0, 0 );
607 #endif
608
609   if (answer)
610     {
611       yes = answer_is_okay_cancel (answer, def_answer);
612       xfree (answer);
613       return yes;
614     }
615
616   for(;;)
617     {
618       p = tty_get( prompt );
619       trim_spaces(p); /* it is okay to do this here */
620       if (*p == '?' && !p[1])
621         {
622           xfree(p);
623           display_online_help (keyword);
624         }
625       else
626         {
627           tty_kill_prompt();
628           yes = answer_is_okay_cancel (p, def_answer);
629           xfree(p);
630           return yes;
631         }
632     }
633 }