2008-05-15 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / g10 / cpr.c
blob83f0ccb4d8e8ed2eb9998a38340a2e893ec71e27
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);
60 /* Return true if the status message NO may currently be issued. We
61 need this to avoid syncronisation problem while auto retrieving a
62 key. There it may happen that a status NODATA is issued for a non
63 available key and the user may falsely interpret this has a missing
64 signature. */
65 static int
66 status_currently_allowed (int no)
68 if (!glo_ctrl.in_auto_key_retrieve)
69 return 1; /* Yes. */
71 /* We allow some statis anyway, so that import statistics are
72 correct and to avoid problems if the retriebval subsystem will
73 prompt the user. */
74 switch (no)
76 case STATUS_GET_BOOL:
77 case STATUS_GET_LINE:
78 case STATUS_GET_HIDDEN:
79 case STATUS_GOT_IT:
80 case STATUS_IMPORTED:
81 case STATUS_IMPORT_OK:
82 case STATUS_IMPORT_CHECK:
83 case STATUS_IMPORT_RES:
84 return 1; /* Yes. */
85 default:
86 break;
88 return 0; /* No. */
92 void
93 set_status_fd ( int fd )
95 static int last_fd = -1;
97 if ( fd != -1 && last_fd == fd )
98 return;
100 if ( statusfp && statusfp != stdout && statusfp != stderr )
101 fclose (statusfp);
102 statusfp = NULL;
103 if ( fd == -1 )
104 return;
106 if( fd == 1 )
107 statusfp = stdout;
108 else if( fd == 2 )
109 statusfp = stderr;
110 else
111 statusfp = fdopen( fd, "w" );
112 if( !statusfp ) {
113 log_fatal("can't open fd %d for status output: %s\n",
114 fd, strerror(errno));
116 last_fd = fd;
118 gcry_set_progress_handler ( progress_cb, NULL );
122 is_status_enabled()
124 return !!statusfp;
127 void
128 write_status ( int no )
130 write_status_text( no, NULL );
133 void
134 write_status_text ( int no, const char *text)
136 if( !statusfp || !status_currently_allowed (no) )
137 return; /* Not enabled or allowed. */
139 fputs ( "[GNUPG:] ", statusfp );
140 fputs ( get_status_string (no), statusfp );
141 if( text ) {
142 putc ( ' ', statusfp );
143 for (; *text; text++) {
144 if (*text == '\n')
145 fputs ( "\\n", statusfp );
146 else if (*text == '\r')
147 fputs ( "\\r", statusfp );
148 else
149 putc ( *(const byte *)text, statusfp );
152 putc ('\n',statusfp);
153 if ( fflush (statusfp) && opt.exit_on_status_write_error )
154 g10_exit (0);
159 * Write a status line with a buffer using %XX escapes. If WRAP is >
160 * 0 wrap the line after this length. If STRING is not NULL it will
161 * be prepended to the buffer, no escaping is done for string.
162 * A wrap of -1 forces spaces not to be encoded as %20.
164 void
165 write_status_text_and_buffer ( int no, const char *string,
166 const char *buffer, size_t len, int wrap )
168 const char *s, *text;
169 int esc, first;
170 int lower_limit = ' ';
171 size_t n, count, dowrap;
173 if( !statusfp || !status_currently_allowed (no) )
174 return; /* Not enabled or allowed. */
176 if (wrap == -1) {
177 lower_limit--;
178 wrap = 0;
181 text = get_status_string (no);
182 count = dowrap = first = 1;
183 do {
184 if (dowrap) {
185 fprintf (statusfp, "[GNUPG:] %s ", text );
186 count = dowrap = 0;
187 if (first && string) {
188 fputs (string, statusfp);
189 count += strlen (string);
191 first = 0;
193 for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
194 if ( *s == '%' || *(const byte*)s <= lower_limit
195 || *(const byte*)s == 127 )
196 esc = 1;
197 if ( wrap && ++count > wrap ) {
198 dowrap=1;
199 break;
202 if (esc) {
203 s--; n++;
205 if (s != buffer)
206 fwrite (buffer, s-buffer, 1, statusfp );
207 if ( esc ) {
208 fprintf (statusfp, "%%%02X", *(const byte*)s );
209 s++; n--;
211 buffer = s;
212 len = n;
213 if ( dowrap && len )
214 putc ( '\n', statusfp );
215 } while ( len );
217 putc ('\n',statusfp);
218 if ( fflush (statusfp) && opt.exit_on_status_write_error )
219 g10_exit (0);
222 void
223 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
225 write_status_text_and_buffer (no, NULL, buffer, len, wrap);
229 /* Print the BEGIN_SIGNING status message. If MD is not NULL it is
230 used retrieve the hash algorithms used for the message. */
231 void
232 write_status_begin_signing (gcry_md_hd_t md)
234 if (md)
236 char buf[100];
237 size_t buflen;
238 int i;
240 /* We use a hard coded list of possible algorithms. Using other
241 algorithms than specified by OpenPGP does not make sense
242 anyway. We do this out of performance reasons: Walking all
243 the 110 allowed Ids is not a good idea given the way the
244 check is implemented in libgcrypt. Recall that the only use
245 of this status code is to create the micalg algorithm for
246 PGP/MIME. */
247 buflen = 0;
248 for (i=1; i <= 11; i++)
249 if (i < 4 || i > 7)
250 if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
252 snprintf (buf+buflen, DIM(buf) - buflen - 1,
253 "%sH%d", buflen? " ":"",i);
254 buflen += strlen (buf+buflen);
256 write_status_text ( STATUS_BEGIN_SIGNING, buf );
258 else
259 write_status ( STATUS_BEGIN_SIGNING );
263 static int
264 myread(int fd, void *buf, size_t count)
266 int rc;
267 do {
268 rc = read( fd, buf, count );
269 } while ( rc == -1 && errno == EINTR );
270 if ( !rc && count ) {
271 static int eof_emmited=0;
272 if ( eof_emmited < 3 ) {
273 *(char*)buf = CONTROL_D;
274 rc = 1;
275 eof_emmited++;
277 else { /* Ctrl-D not caught - do something reasonable */
278 #ifdef HAVE_DOSISH_SYSTEM
279 raise (SIGINT); /* nothing to hangup under DOS */
280 #else
281 raise (SIGHUP); /* no more input data */
282 #endif
285 return rc;
290 /* Request a string from the client over the command-fd. If GETBOOL
291 is set the function returns a static string (do not free) if the
292 netered value was true or NULL if the entered value was false. */
293 static char *
294 do_get_from_fd ( const char *keyword, int hidden, int getbool )
296 int i, len;
297 char *string;
299 if (statusfp != stdout)
300 fflush (stdout);
302 write_status_text (getbool? STATUS_GET_BOOL :
303 hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
305 for (string = NULL, i = len = 200; ; i++ )
307 if (i >= len-1 )
309 char *save = string;
310 len += 100;
311 string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
312 if (save)
313 memcpy (string, save, i );
314 else
315 i = 0;
317 /* Fixme: why not use our read_line function here? */
318 if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
319 break;
320 else if ( string[i] == CONTROL_D )
322 /* Found ETX - Cancel the line and return a sole ETX. */
323 string[0] = CONTROL_D;
324 i = 1;
325 break;
328 string[i] = 0;
330 write_status (STATUS_GOT_IT);
332 if (getbool) /* Fixme: is this correct??? */
333 return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
335 return string;
341 cpr_enabled()
343 if( opt.command_fd != -1 )
344 return 1;
345 #ifdef USE_SHM_COPROCESSING
346 if( opt.shm_coprocess )
347 return 1;
348 #endif
349 return 0;
352 char *
353 cpr_get_no_help( const char *keyword, const char *prompt )
355 char *p;
357 if( opt.command_fd != -1 )
358 return do_get_from_fd ( keyword, 0, 0 );
359 #ifdef USE_SHM_COPROCESSING
360 if( opt.shm_coprocess )
361 return do_shm_get( keyword, 0, 0 );
362 #endif
363 for(;;) {
364 p = tty_get( prompt );
365 return p;
369 char *
370 cpr_get( const char *keyword, const char *prompt )
372 char *p;
374 if( opt.command_fd != -1 )
375 return do_get_from_fd ( keyword, 0, 0 );
376 #ifdef USE_SHM_COPROCESSING
377 if( opt.shm_coprocess )
378 return do_shm_get( keyword, 0, 0 );
379 #endif
380 for(;;) {
381 p = tty_get( prompt );
382 if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
383 xfree(p);
384 display_online_help( keyword );
386 else
387 return p;
392 char *
393 cpr_get_utf8( const char *keyword, const char *prompt )
395 char *p;
396 p = cpr_get( keyword, prompt );
397 if( p ) {
398 char *utf8 = native_to_utf8( p );
399 xfree( p );
400 p = utf8;
402 return p;
405 char *
406 cpr_get_hidden( const char *keyword, const char *prompt )
408 char *p;
410 if( opt.command_fd != -1 )
411 return do_get_from_fd ( keyword, 1, 0 );
412 #ifdef USE_SHM_COPROCESSING
413 if( opt.shm_coprocess )
414 return do_shm_get( keyword, 1, 0 );
415 #endif
416 for(;;) {
417 p = tty_get_hidden( prompt );
418 if( *p == '?' && !p[1] ) {
419 xfree(p);
420 display_online_help( keyword );
422 else
423 return p;
427 void
428 cpr_kill_prompt(void)
430 if( opt.command_fd != -1 )
431 return;
432 #ifdef USE_SHM_COPROCESSING
433 if( opt.shm_coprocess )
434 return;
435 #endif
436 tty_kill_prompt();
437 return;
441 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
443 int yes;
444 char *p;
446 if( opt.command_fd != -1 )
447 return !!do_get_from_fd ( keyword, 0, 1 );
448 #ifdef USE_SHM_COPROCESSING
449 if( opt.shm_coprocess )
450 return !!do_shm_get( keyword, 0, 1 );
451 #endif
452 for(;;) {
453 p = tty_get( prompt );
454 trim_spaces(p); /* it is okay to do this here */
455 if( *p == '?' && !p[1] ) {
456 xfree(p);
457 display_online_help( keyword );
459 else {
460 tty_kill_prompt();
461 yes = answer_is_yes(p);
462 xfree(p);
463 return yes;
469 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
471 int yes;
472 char *p;
474 if( opt.command_fd != -1 )
475 return !!do_get_from_fd ( keyword, 0, 1 );
476 #ifdef USE_SHM_COPROCESSING
477 if( opt.shm_coprocess )
478 return !!do_shm_get( keyword, 0, 1 );
479 #endif
480 for(;;) {
481 p = tty_get( prompt );
482 trim_spaces(p); /* it is okay to do this here */
483 if( *p == '?' && !p[1] ) {
484 xfree(p);
485 display_online_help( keyword );
487 else {
488 tty_kill_prompt();
489 yes = answer_is_yes_no_quit(p);
490 xfree(p);
491 return yes;
498 cpr_get_answer_okay_cancel (const char *keyword,
499 const char *prompt,
500 int def_answer)
502 int yes;
503 char *answer = NULL;
504 char *p;
506 if( opt.command_fd != -1 )
507 answer = do_get_from_fd ( keyword, 0, 0 );
508 #ifdef USE_SHM_COPROCESSING
509 else if( opt.shm_coprocess )
510 answer = do_shm_get( keyword, 0, 0 );
511 #endif
513 if (answer)
515 yes = answer_is_okay_cancel (answer, def_answer);
516 xfree (answer);
517 return yes;
520 for(;;)
522 p = tty_get( prompt );
523 trim_spaces(p); /* it is okay to do this here */
524 if (*p == '?' && !p[1])
526 xfree(p);
527 display_online_help (keyword);
529 else
531 tty_kill_prompt();
532 yes = answer_is_okay_cancel (p, def_answer);
533 xfree(p);
534 return yes;