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