Fix signal handling race condition.
[gnupg.git] / g10 / cpr.c
blob60cc97bf6679090ee316f7a2a634ca6d1b134344
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 (void)ctx;
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);
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)
70 if (!glo_ctrl.in_auto_key_retrieve)
71 return 1; /* Yes. */
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)
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;
90 return 0; /* No. */
94 void
95 set_status_fd ( int fd )
97 static int last_fd = -1;
99 if ( fd != -1 && last_fd == fd )
100 return;
102 if ( statusfp && statusfp != stdout && statusfp != stderr )
103 fclose (statusfp);
104 statusfp = NULL;
105 if ( fd == -1 )
106 return;
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));
118 last_fd = fd;
120 gcry_set_progress_handler ( progress_cb, NULL );
124 is_status_enabled()
126 return !!statusfp;
129 void
130 write_status ( int no )
132 write_status_text( no, NULL );
135 void
136 write_status_text ( int no, const char *text)
138 if( !statusfp || !status_currently_allowed (no) )
139 return; /* Not enabled or allowed. */
141 fputs ( "[GNUPG:] ", statusfp );
142 fputs ( get_status_string (no), statusfp );
143 if( text ) {
144 putc ( ' ', statusfp );
145 for (; *text; text++) {
146 if (*text == '\n')
147 fputs ( "\\n", statusfp );
148 else if (*text == '\r')
149 fputs ( "\\r", statusfp );
150 else
151 putc ( *(const byte *)text, statusfp );
154 putc ('\n',statusfp);
155 if ( fflush (statusfp) && opt.exit_on_status_write_error )
156 g10_exit (0);
160 void
161 write_status_error (const char *where, int errcode)
163 if (!statusfp || !status_currently_allowed (STATUS_ERROR))
164 return; /* Not enabled or allowed. */
166 fprintf (statusfp, "[GNUPG:] %s %s %u\n",
167 get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
168 if (fflush (statusfp) && opt.exit_on_status_write_error)
169 g10_exit (0);
174 * Write a status line with a buffer using %XX escapes. If WRAP is >
175 * 0 wrap the line after this length. If STRING is not NULL it will
176 * be prepended to the buffer, no escaping is done for string.
177 * A wrap of -1 forces spaces not to be encoded as %20.
179 void
180 write_status_text_and_buffer ( int no, const char *string,
181 const char *buffer, size_t len, int wrap )
183 const char *s, *text;
184 int esc, first;
185 int lower_limit = ' ';
186 size_t n, count, dowrap;
188 if( !statusfp || !status_currently_allowed (no) )
189 return; /* Not enabled or allowed. */
191 if (wrap == -1) {
192 lower_limit--;
193 wrap = 0;
196 text = get_status_string (no);
197 count = dowrap = first = 1;
198 do {
199 if (dowrap) {
200 fprintf (statusfp, "[GNUPG:] %s ", text );
201 count = dowrap = 0;
202 if (first && string) {
203 fputs (string, statusfp);
204 count += strlen (string);
206 first = 0;
208 for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
209 if ( *s == '%' || *(const byte*)s <= lower_limit
210 || *(const byte*)s == 127 )
211 esc = 1;
212 if ( wrap && ++count > wrap ) {
213 dowrap=1;
214 break;
217 if (esc) {
218 s--; n++;
220 if (s != buffer)
221 fwrite (buffer, s-buffer, 1, statusfp );
222 if ( esc ) {
223 fprintf (statusfp, "%%%02X", *(const byte*)s );
224 s++; n--;
226 buffer = s;
227 len = n;
228 if ( dowrap && len )
229 putc ( '\n', statusfp );
230 } while ( len );
232 putc ('\n',statusfp);
233 if ( fflush (statusfp) && opt.exit_on_status_write_error )
234 g10_exit (0);
237 void
238 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
240 write_status_text_and_buffer (no, NULL, buffer, len, wrap);
244 /* Print the BEGIN_SIGNING status message. If MD is not NULL it is
245 used to retrieve the hash algorithms used for the message. */
246 void
247 write_status_begin_signing (gcry_md_hd_t md)
249 if (md)
251 char buf[100];
252 size_t buflen;
253 int i;
255 /* We use a hard coded list of possible algorithms. Using other
256 algorithms than specified by OpenPGP does not make sense
257 anyway. We do this out of performance reasons: Walking all
258 the 110 allowed Ids is not a good idea given the way the
259 check is implemented in libgcrypt. Recall that the only use
260 of this status code is to create the micalg algorithm for
261 PGP/MIME. */
262 buflen = 0;
263 for (i=1; i <= 11; i++)
264 if (i < 4 || i > 7)
265 if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
267 snprintf (buf+buflen, DIM(buf) - buflen - 1,
268 "%sH%d", buflen? " ":"",i);
269 buflen += strlen (buf+buflen);
271 write_status_text ( STATUS_BEGIN_SIGNING, buf );
273 else
274 write_status ( STATUS_BEGIN_SIGNING );
278 static int
279 myread(int fd, void *buf, size_t count)
281 int rc;
282 do {
283 rc = read( fd, buf, count );
284 } while ( rc == -1 && errno == EINTR );
285 if ( !rc && count ) {
286 static int eof_emmited=0;
287 if ( eof_emmited < 3 ) {
288 *(char*)buf = CONTROL_D;
289 rc = 1;
290 eof_emmited++;
292 else { /* Ctrl-D not caught - do something reasonable */
293 #ifdef HAVE_DOSISH_SYSTEM
294 raise (SIGINT); /* nothing to hangup under DOS */
295 #else
296 raise (SIGHUP); /* no more input data */
297 #endif
300 return rc;
305 /* Request a string from the client over the command-fd. If GETBOOL
306 is set the function returns a static string (do not free) if the
307 netered value was true or NULL if the entered value was false. */
308 static char *
309 do_get_from_fd ( const char *keyword, int hidden, int getbool )
311 int i, len;
312 char *string;
314 if (statusfp != stdout)
315 fflush (stdout);
317 write_status_text (getbool? STATUS_GET_BOOL :
318 hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
320 for (string = NULL, i = len = 200; ; i++ )
322 if (i >= len-1 )
324 char *save = string;
325 len += 100;
326 string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
327 if (save)
328 memcpy (string, save, i );
329 else
330 i = 0;
332 /* Fixme: why not use our read_line function here? */
333 if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
334 break;
335 else if ( string[i] == CONTROL_D )
337 /* Found ETX - Cancel the line and return a sole ETX. */
338 string[0] = CONTROL_D;
339 i = 1;
340 break;
343 string[i] = 0;
345 write_status (STATUS_GOT_IT);
347 if (getbool) /* Fixme: is this correct??? */
348 return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
350 return string;
356 cpr_enabled()
358 if( opt.command_fd != -1 )
359 return 1;
360 #ifdef USE_SHM_COPROCESSING
361 if( opt.shm_coprocess )
362 return 1;
363 #endif
364 return 0;
367 char *
368 cpr_get_no_help( const char *keyword, const char *prompt )
370 char *p;
372 if( opt.command_fd != -1 )
373 return do_get_from_fd ( keyword, 0, 0 );
374 #ifdef USE_SHM_COPROCESSING
375 if( opt.shm_coprocess )
376 return do_shm_get( keyword, 0, 0 );
377 #endif
378 for(;;) {
379 p = tty_get( prompt );
380 return p;
384 char *
385 cpr_get( const char *keyword, const char *prompt )
387 char *p;
389 if( opt.command_fd != -1 )
390 return do_get_from_fd ( keyword, 0, 0 );
391 #ifdef USE_SHM_COPROCESSING
392 if( opt.shm_coprocess )
393 return do_shm_get( keyword, 0, 0 );
394 #endif
395 for(;;) {
396 p = tty_get( prompt );
397 if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
398 xfree(p);
399 display_online_help( keyword );
401 else
402 return p;
407 char *
408 cpr_get_utf8( const char *keyword, const char *prompt )
410 char *p;
411 p = cpr_get( keyword, prompt );
412 if( p ) {
413 char *utf8 = native_to_utf8( p );
414 xfree( p );
415 p = utf8;
417 return p;
420 char *
421 cpr_get_hidden( const char *keyword, const char *prompt )
423 char *p;
425 if( opt.command_fd != -1 )
426 return do_get_from_fd ( keyword, 1, 0 );
427 #ifdef USE_SHM_COPROCESSING
428 if( opt.shm_coprocess )
429 return do_shm_get( keyword, 1, 0 );
430 #endif
431 for(;;) {
432 p = tty_get_hidden( prompt );
433 if( *p == '?' && !p[1] ) {
434 xfree(p);
435 display_online_help( keyword );
437 else
438 return p;
442 void
443 cpr_kill_prompt(void)
445 if( opt.command_fd != -1 )
446 return;
447 #ifdef USE_SHM_COPROCESSING
448 if( opt.shm_coprocess )
449 return;
450 #endif
451 tty_kill_prompt();
452 return;
456 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
458 int yes;
459 char *p;
461 if( opt.command_fd != -1 )
462 return !!do_get_from_fd ( keyword, 0, 1 );
463 #ifdef USE_SHM_COPROCESSING
464 if( opt.shm_coprocess )
465 return !!do_shm_get( keyword, 0, 1 );
466 #endif
467 for(;;) {
468 p = tty_get( prompt );
469 trim_spaces(p); /* it is okay to do this here */
470 if( *p == '?' && !p[1] ) {
471 xfree(p);
472 display_online_help( keyword );
474 else {
475 tty_kill_prompt();
476 yes = answer_is_yes(p);
477 xfree(p);
478 return yes;
484 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
486 int yes;
487 char *p;
489 if( opt.command_fd != -1 )
490 return !!do_get_from_fd ( keyword, 0, 1 );
491 #ifdef USE_SHM_COPROCESSING
492 if( opt.shm_coprocess )
493 return !!do_shm_get( keyword, 0, 1 );
494 #endif
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 );
502 else {
503 tty_kill_prompt();
504 yes = answer_is_yes_no_quit(p);
505 xfree(p);
506 return yes;
513 cpr_get_answer_okay_cancel (const char *keyword,
514 const char *prompt,
515 int def_answer)
517 int yes;
518 char *answer = NULL;
519 char *p;
521 if( opt.command_fd != -1 )
522 answer = do_get_from_fd ( keyword, 0, 0 );
523 #ifdef USE_SHM_COPROCESSING
524 else if( opt.shm_coprocess )
525 answer = do_shm_get( keyword, 0, 0 );
526 #endif
528 if (answer)
530 yes = answer_is_okay_cancel (answer, def_answer);
531 xfree (answer);
532 return yes;
535 for(;;)
537 p = tty_get( prompt );
538 trim_spaces(p); /* it is okay to do this here */
539 if (*p == '?' && !p[1])
541 xfree(p);
542 display_online_help (keyword);
544 else
546 tty_kill_prompt();
547 yes = answer_is_okay_cancel (p, def_answer);
548 xfree(p);
549 return yes;