Update estream.
[gnupg.git] / g10 / cpr.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 3 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, see <http://www.gnu.org/licenses/>.
19  */
20
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>
28
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 */
37
38 #define CONTROL_D ('D' - 'A' + 1)
39
40
41
42 static FILE *statusfp;
43
44
45 static void
46 progress_cb (void *ctx, const char *what, int printchar,
47              int current, int total)
48 {
49   char buf[50];
50
51   (void)ctx;
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
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)
69 {
70   if (!glo_ctrl.in_auto_key_retrieve)
71     return 1; /* Yes. */
72
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)
77     {
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;
89     }
90   return 0; /* No. */
91 }
92
93
94 void
95 set_status_fd ( int fd )
96 {
97     static int last_fd = -1;
98
99     if ( fd != -1 && last_fd == fd )
100         return;
101
102     if ( statusfp && statusfp != stdout && statusfp != stderr )
103         fclose (statusfp);
104     statusfp = NULL;
105     if ( fd == -1 ) 
106         return;
107
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));
117     }
118     last_fd = fd;
119
120     gcry_set_progress_handler ( progress_cb, NULL );
121 }
122
123 int
124 is_status_enabled()
125 {
126     return !!statusfp;
127 }
128
129 void
130 write_status ( int no )
131 {
132     write_status_text( no, NULL );
133 }
134
135 void
136 write_status_text ( int no, const char *text)
137 {
138     if( !statusfp || !status_currently_allowed (no) )
139         return;  /* Not enabled or allowed. */
140
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 );
152         }
153     }
154     putc ('\n',statusfp);
155     if ( fflush (statusfp) && opt.exit_on_status_write_error )
156       g10_exit (0);
157 }
158
159
160 void
161 write_status_error (const char *where, int errcode)
162 {
163   if (!statusfp || !status_currently_allowed (STATUS_ERROR))
164     return;  /* Not enabled or allowed. */
165
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);
170 }
171
172
173 /*
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.
178  */
179 void
180 write_status_text_and_buffer ( int no, const char *string,
181                                const char *buffer, size_t len, int wrap )
182 {
183     const char *s, *text;
184     int esc, first;
185     int lower_limit = ' ';
186     size_t n, count, dowrap;
187
188     if( !statusfp || !status_currently_allowed (no) )
189         return;  /* Not enabled or allowed. */
190     
191     if (wrap == -1) {
192         lower_limit--;
193         wrap = 0;
194     }
195
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);
205                 /* Make sure that there is space after the string.  */
206                 if (*string && string[strlen (string)-1] != ' ')
207                   {
208                     putc (' ', statusfp);
209                     count++;
210                   }
211             }
212             first = 0;
213         }
214         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
215             if ( *s == '%' || *(const byte*)s <= lower_limit 
216                            || *(const byte*)s == 127 ) 
217                 esc = 1;
218             if ( wrap && ++count > wrap ) {
219                 dowrap=1;
220                 break;
221             }
222         }
223         if (esc) {
224             s--; n++;
225         }
226         if (s != buffer) 
227             fwrite (buffer, s-buffer, 1, statusfp );
228         if ( esc ) {
229             fprintf (statusfp, "%%%02X", *(const byte*)s );
230             s++; n--;
231         }
232         buffer = s;
233         len = n;
234         if ( dowrap && len )
235             putc ( '\n', statusfp );
236     } while ( len );
237
238     putc ('\n',statusfp);
239     if ( fflush (statusfp) && opt.exit_on_status_write_error )
240       g10_exit (0);
241 }
242
243 void
244 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
245 {
246     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
247 }
248
249
250 /* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
251    used to retrieve the hash algorithms used for the message. */
252 void
253 write_status_begin_signing (gcry_md_hd_t md)
254 {
255   if (md)
256     {
257       char buf[100];
258       size_t buflen;
259       int i;
260       
261       /* We use a hard coded list of possible algorithms.  Using other
262          algorithms than specified by OpenPGP does not make sense
263          anyway.  We do this out of performance reasons: Walking all
264          the 110 allowed Ids is not a good idea given the way the
265          check is implemented in libgcrypt.  Recall that the only use
266          of this status code is to create the micalg algorithm for
267          PGP/MIME. */
268       buflen = 0;
269       for (i=1; i <= 11; i++)
270         if (i < 4 || i > 7)
271           if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
272             {
273               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
274                         "%sH%d", buflen? " ":"",i);
275               buflen += strlen (buf+buflen);
276             }
277       write_status_text ( STATUS_BEGIN_SIGNING, buf );
278     }
279   else
280     write_status ( STATUS_BEGIN_SIGNING );
281 }
282
283
284 static int
285 myread(int fd, void *buf, size_t count)
286 {
287     int rc;
288     do {
289         rc = read( fd, buf, count );
290     } while ( rc == -1 && errno == EINTR );
291     if ( !rc && count ) {
292         static int eof_emmited=0;
293         if ( eof_emmited < 3 ) {
294             *(char*)buf = CONTROL_D;
295             rc = 1;
296             eof_emmited++;
297         }
298         else { /* Ctrl-D not caught - do something reasonable */
299 #ifdef HAVE_DOSISH_SYSTEM
300             raise (SIGINT);  /* nothing to hangup under DOS */
301 #else
302             raise (SIGHUP); /* no more input data */
303 #endif
304         }
305     }    
306     return rc;
307 }
308
309
310
311 /* Request a string from the client over the command-fd.  If GETBOOL
312    is set the function returns a static string (do not free) if the
313    netered value was true or NULL if the entered value was false.  */
314 static char *
315 do_get_from_fd ( const char *keyword, int hidden, int getbool )
316 {
317   int i, len;
318   char *string;
319   
320   if (statusfp != stdout)
321     fflush (stdout);
322   
323   write_status_text (getbool? STATUS_GET_BOOL :
324                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
325
326   for (string = NULL, i = len = 200; ; i++ ) 
327     {
328       if (i >= len-1 ) 
329         {
330           char *save = string;
331           len += 100;
332           string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
333           if (save)
334             memcpy (string, save, i );
335           else
336             i = 0;
337         }
338       /* Fixme: why not use our read_line function here? */
339       if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
340         break;
341       else if ( string[i] == CONTROL_D ) 
342         {
343           /* Found ETX - Cancel the line and return a sole ETX.  */
344           string[0] = CONTROL_D;
345           i = 1;
346           break;
347         }
348     }
349   string[i] = 0;
350
351   write_status (STATUS_GOT_IT);
352
353   if (getbool)   /* Fixme: is this correct??? */
354     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
355
356   return string;
357 }
358
359
360
361 int
362 cpr_enabled()
363 {
364     if( opt.command_fd != -1 )
365         return 1;
366     return 0;
367 }
368
369 char *
370 cpr_get_no_help( const char *keyword, const char *prompt )
371 {
372     char *p;
373
374     if( opt.command_fd != -1 )
375         return do_get_from_fd ( keyword, 0, 0 );
376     for(;;) {
377         p = tty_get( prompt );
378         return p;
379     }
380 }
381
382 char *
383 cpr_get( const char *keyword, const char *prompt )
384 {
385     char *p;
386
387     if( opt.command_fd != -1 )
388         return do_get_from_fd ( keyword, 0, 0 );
389     for(;;) {
390         p = tty_get( prompt );
391         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
392             xfree(p);
393             display_online_help( keyword );
394         }
395         else
396             return p;
397     }
398 }
399
400
401 char *
402 cpr_get_utf8( const char *keyword, const char *prompt )
403 {
404     char *p;
405     p = cpr_get( keyword, prompt );
406     if( p ) {
407         char *utf8 = native_to_utf8( p );
408         xfree( p );
409         p = utf8;
410     }
411     return p;
412 }
413
414 char *
415 cpr_get_hidden( const char *keyword, const char *prompt )
416 {
417     char *p;
418
419     if( opt.command_fd != -1 )
420         return do_get_from_fd ( keyword, 1, 0 );
421     for(;;) {
422         p = tty_get_hidden( prompt );
423         if( *p == '?' && !p[1] ) {
424             xfree(p);
425             display_online_help( keyword );
426         }
427         else
428             return p;
429     }
430 }
431
432 void
433 cpr_kill_prompt(void)
434 {
435     if( opt.command_fd != -1 )
436         return;
437     tty_kill_prompt();
438     return;
439 }
440
441 int
442 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
443 {
444     int yes;
445     char *p;
446
447     if( opt.command_fd != -1 )
448         return !!do_get_from_fd ( keyword, 0, 1 );
449     for(;;) {
450         p = tty_get( prompt );
451         trim_spaces(p); /* it is okay to do this here */
452         if( *p == '?' && !p[1] ) {
453             xfree(p);
454             display_online_help( keyword );
455         }
456         else {
457             tty_kill_prompt();
458             yes = answer_is_yes(p);
459             xfree(p);
460             return yes;
461         }
462     }
463 }
464
465 int
466 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
467 {
468     int yes;
469     char *p;
470
471     if( opt.command_fd != -1 )
472         return !!do_get_from_fd ( keyword, 0, 1 );
473     for(;;) {
474         p = tty_get( prompt );
475         trim_spaces(p); /* it is okay to do this here */
476         if( *p == '?' && !p[1] ) {
477             xfree(p);
478             display_online_help( keyword );
479         }
480         else {
481             tty_kill_prompt();
482             yes = answer_is_yes_no_quit(p);
483             xfree(p);
484             return yes;
485         }
486     }
487 }
488
489
490 int
491 cpr_get_answer_okay_cancel (const char *keyword,
492                             const char *prompt,
493                             int def_answer)
494 {
495   int yes;
496   char *answer = NULL;
497   char *p;
498
499   if( opt.command_fd != -1 )
500     answer = do_get_from_fd ( keyword, 0, 0 );
501
502   if (answer)
503     {
504       yes = answer_is_okay_cancel (answer, def_answer);
505       xfree (answer);
506       return yes;
507     }
508
509   for(;;)
510     {
511       p = tty_get( prompt );
512       trim_spaces(p); /* it is okay to do this here */
513       if (*p == '?' && !p[1])
514         {
515           xfree(p);
516           display_online_help (keyword);
517         }
518       else
519         {
520           tty_kill_prompt();
521           yes = answer_is_okay_cancel (p, def_answer);
522           xfree(p);
523           return yes;
524         }
525     }
526 }