2007-07-05 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / g10 / status.c
blobe786524822bc2d005b75133c525ad462867df7bb
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.
5 * This file is part of GnuPG.
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.
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.
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/>.
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>
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 */
38 #define CONTROL_D ('D' - 'A' + 1)
42 static FILE *statusfp;
45 static void
46 progress_cb (void *ctx, const char *what, int printchar,
47 int current, int total)
49 char buf[50];
51 if ( printchar == '\n' && !strcmp (what, "primegen") )
52 snprintf (buf, sizeof buf -1, "%.20s X 100 100", what );
53 else
54 snprintf (buf, sizeof buf -1, "%.20s %c %d %d",
55 what, printchar=='\n'?'X':printchar, current, total );
56 write_status_text (STATUS_PROGRESS, buf);
59 static const char *
60 get_status_string ( int no )
62 const char *s;
64 switch( no )
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 case STATUS_ERROR : s = "ERROR"; break;
150 default: s = "?"; break;
152 return s;
156 /* Return true if the status message NO may currently be issued. We
157 need this to avoid syncronisation problem while auto retrieving a
158 key. There it may happen that a status NODATA is issued for a non
159 available key and the user may falsely interpret this has a missing
160 signature. */
161 static int
162 status_currently_allowed (int no)
164 if (!glo_ctrl.in_auto_key_retrieve)
165 return 1; /* Yes. */
167 /* We allow some statis anyway, so that import statistics are
168 correct and to avoid problems if the retriebval subsystem will
169 prompt the user. */
170 switch (no)
172 case STATUS_GET_BOOL:
173 case STATUS_GET_LINE:
174 case STATUS_GET_HIDDEN:
175 case STATUS_GOT_IT:
176 case STATUS_IMPORTED:
177 case STATUS_IMPORT_OK:
178 case STATUS_IMPORT_CHECK:
179 case STATUS_IMPORT_RES:
180 return 1; /* Yes. */
181 default:
182 break;
184 return 0; /* No. */
188 void
189 set_status_fd ( int fd )
191 static int last_fd = -1;
193 if ( fd != -1 && last_fd == fd )
194 return;
196 if ( statusfp && statusfp != stdout && statusfp != stderr )
197 fclose (statusfp);
198 statusfp = NULL;
199 if ( fd == -1 )
200 return;
202 if( fd == 1 )
203 statusfp = stdout;
204 else if( fd == 2 )
205 statusfp = stderr;
206 else
207 statusfp = fdopen( fd, "w" );
208 if( !statusfp ) {
209 log_fatal("can't open fd %d for status output: %s\n",
210 fd, strerror(errno));
212 last_fd = fd;
214 gcry_set_progress_handler ( progress_cb, NULL );
218 is_status_enabled()
220 return !!statusfp;
223 void
224 write_status ( int no )
226 write_status_text( no, NULL );
229 void
230 write_status_text ( int no, const char *text)
232 if( !statusfp || !status_currently_allowed (no) )
233 return; /* Not enabled or allowed. */
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 );
248 putc ('\n',statusfp);
249 if ( fflush (statusfp) && opt.exit_on_status_write_error )
250 g10_exit (0);
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.
260 void
261 write_status_text_and_buffer ( int no, const char *string,
262 const char *buffer, size_t len, int wrap )
264 const char *s, *text;
265 int esc, first;
266 int lower_limit = ' ';
267 size_t n, count, dowrap;
269 if( !statusfp || !status_currently_allowed (no) )
270 return; /* Not enabled or allowed. */
272 if (wrap == -1) {
273 lower_limit--;
274 wrap = 0;
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);
287 first = 0;
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;
298 if (esc) {
299 s--; n++;
301 if (s != buffer)
302 fwrite (buffer, s-buffer, 1, statusfp );
303 if ( esc ) {
304 fprintf (statusfp, "%%%02X", *(const byte*)s );
305 s++; n--;
307 buffer = s;
308 len = n;
309 if ( dowrap && len )
310 putc ( '\n', statusfp );
311 } while ( len );
313 putc ('\n',statusfp);
314 if ( fflush (statusfp) && opt.exit_on_status_write_error )
315 g10_exit (0);
318 void
319 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
321 write_status_text_and_buffer (no, NULL, buffer, len, wrap);
325 /* Print the BEGIN_SIGNING status message. If MD is not NULL it is
326 used retrieve the hash algorithms used for the message. */
327 void
328 write_status_begin_signing (gcry_md_hd_t md)
330 if (md)
332 char buf[100];
333 size_t buflen;
334 int i;
336 /* We use a hard coded list of possible algorithms. Using other
337 algorithms than specified by OpenPGP does not make sense
338 anyway. We do this out of performance reasons: Walking all
339 the 110 allowed Ids is not a good idea given the way the
340 check is implemented in libgcrypt. Recall that the only use
341 of this status code is to create the micalg algorithm for
342 PGP/MIME. */
343 buflen = 0;
344 for (i=1; i <= 11; i++)
345 if (i < 4 || i > 7)
346 if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
348 snprintf (buf+buflen, DIM(buf) - buflen - 1,
349 "%sH%d", buflen? " ":"",i);
350 buflen += strlen (buf+buflen);
352 write_status_text ( STATUS_BEGIN_SIGNING, buf );
354 else
355 write_status ( STATUS_BEGIN_SIGNING );
359 static int
360 myread(int fd, void *buf, size_t count)
362 int rc;
363 do {
364 rc = read( fd, buf, count );
365 } while ( rc == -1 && errno == EINTR );
366 if ( !rc && count ) {
367 static int eof_emmited=0;
368 if ( eof_emmited < 3 ) {
369 *(char*)buf = CONTROL_D;
370 rc = 1;
371 eof_emmited++;
373 else { /* Ctrl-D not caught - do something reasonable */
374 #ifdef HAVE_DOSISH_SYSTEM
375 raise (SIGINT); /* nothing to hangup under DOS */
376 #else
377 raise (SIGHUP); /* no more input data */
378 #endif
381 return rc;
386 /****************
387 * Request a string from the client over the command-fd
388 * If bool, returns static string on true (do not free) or NULL for false
390 static char *
391 do_get_from_fd( const char *keyword, int hidden, int bool )
393 int i, len;
394 char *string;
396 if(statusfp!=stdout)
397 fflush(stdout);
399 write_status_text( bool? STATUS_GET_BOOL :
400 hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword );
402 for( string = NULL, i = len = 200; ; i++ ) {
403 if( i >= len-1 ) {
404 char *save = string;
405 len += 100;
406 string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
407 if( save )
408 memcpy(string, save, i );
409 else
410 i=0;
412 /* Hmmm: why not use our read_line function here */
413 if( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
414 break;
415 else if ( string[i] == CONTROL_D ) {
416 /* found ETX - cancel the line and return a sole ETX */
417 string[0] = CONTROL_D;
418 i=1;
419 break;
422 string[i] = 0;
424 write_status( STATUS_GOT_IT );
426 if( bool ) /* Fixme: is this correct??? */
427 return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
429 return string;
435 cpr_enabled()
437 if( opt.command_fd != -1 )
438 return 1;
439 #ifdef USE_SHM_COPROCESSING
440 if( opt.shm_coprocess )
441 return 1;
442 #endif
443 return 0;
446 char *
447 cpr_get_no_help( const char *keyword, const char *prompt )
449 char *p;
451 if( opt.command_fd != -1 )
452 return do_get_from_fd ( keyword, 0, 0 );
453 #ifdef USE_SHM_COPROCESSING
454 if( opt.shm_coprocess )
455 return do_shm_get( keyword, 0, 0 );
456 #endif
457 for(;;) {
458 p = tty_get( prompt );
459 return p;
463 char *
464 cpr_get( const char *keyword, const char *prompt )
466 char *p;
468 if( opt.command_fd != -1 )
469 return do_get_from_fd ( keyword, 0, 0 );
470 #ifdef USE_SHM_COPROCESSING
471 if( opt.shm_coprocess )
472 return do_shm_get( keyword, 0, 0 );
473 #endif
474 for(;;) {
475 p = tty_get( prompt );
476 if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
477 xfree(p);
478 display_online_help( keyword );
480 else
481 return p;
486 char *
487 cpr_get_utf8( const char *keyword, const char *prompt )
489 char *p;
490 p = cpr_get( keyword, prompt );
491 if( p ) {
492 char *utf8 = native_to_utf8( p );
493 xfree( p );
494 p = utf8;
496 return p;
499 char *
500 cpr_get_hidden( const char *keyword, const char *prompt )
502 char *p;
504 if( opt.command_fd != -1 )
505 return do_get_from_fd ( keyword, 1, 0 );
506 #ifdef USE_SHM_COPROCESSING
507 if( opt.shm_coprocess )
508 return do_shm_get( keyword, 1, 0 );
509 #endif
510 for(;;) {
511 p = tty_get_hidden( prompt );
512 if( *p == '?' && !p[1] ) {
513 xfree(p);
514 display_online_help( keyword );
516 else
517 return p;
521 void
522 cpr_kill_prompt(void)
524 if( opt.command_fd != -1 )
525 return;
526 #ifdef USE_SHM_COPROCESSING
527 if( opt.shm_coprocess )
528 return;
529 #endif
530 tty_kill_prompt();
531 return;
535 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
537 int yes;
538 char *p;
540 if( opt.command_fd != -1 )
541 return !!do_get_from_fd ( keyword, 0, 1 );
542 #ifdef USE_SHM_COPROCESSING
543 if( opt.shm_coprocess )
544 return !!do_shm_get( keyword, 0, 1 );
545 #endif
546 for(;;) {
547 p = tty_get( prompt );
548 trim_spaces(p); /* it is okay to do this here */
549 if( *p == '?' && !p[1] ) {
550 xfree(p);
551 display_online_help( keyword );
553 else {
554 tty_kill_prompt();
555 yes = answer_is_yes(p);
556 xfree(p);
557 return yes;
563 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
565 int yes;
566 char *p;
568 if( opt.command_fd != -1 )
569 return !!do_get_from_fd ( keyword, 0, 1 );
570 #ifdef USE_SHM_COPROCESSING
571 if( opt.shm_coprocess )
572 return !!do_shm_get( keyword, 0, 1 );
573 #endif
574 for(;;) {
575 p = tty_get( prompt );
576 trim_spaces(p); /* it is okay to do this here */
577 if( *p == '?' && !p[1] ) {
578 xfree(p);
579 display_online_help( keyword );
581 else {
582 tty_kill_prompt();
583 yes = answer_is_yes_no_quit(p);
584 xfree(p);
585 return yes;
592 cpr_get_answer_okay_cancel (const char *keyword,
593 const char *prompt,
594 int def_answer)
596 int yes;
597 char *answer = NULL;
598 char *p;
600 if( opt.command_fd != -1 )
601 answer = do_get_from_fd ( keyword, 0, 0 );
602 #ifdef USE_SHM_COPROCESSING
603 else if( opt.shm_coprocess )
604 answer = do_shm_get( keyword, 0, 0 );
605 #endif
607 if (answer)
609 yes = answer_is_okay_cancel (answer, def_answer);
610 xfree (answer);
611 return yes;
614 for(;;)
616 p = tty_get( prompt );
617 trim_spaces(p); /* it is okay to do this here */
618 if (*p == '?' && !p[1])
620 xfree(p);
621 display_online_help (keyword);
623 else
625 tty_kill_prompt();
626 yes = answer_is_okay_cancel (p, def_answer);
627 xfree(p);
628 return yes;