Add rmd160.c.
[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             }
206             first = 0;
207         }
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;
215             }
216         }
217         if (esc) {
218             s--; n++;
219         }
220         if (s != buffer) 
221             fwrite (buffer, s-buffer, 1, statusfp );
222         if ( esc ) {
223             fprintf (statusfp, "%%%02X", *(const byte*)s );
224             s++; n--;
225         }
226         buffer = s;
227         len = n;
228         if ( dowrap && len )
229             putc ( '\n', statusfp );
230     } while ( len );
231
232     putc ('\n',statusfp);
233     if ( fflush (statusfp) && opt.exit_on_status_write_error )
234       g10_exit (0);
235 }
236
237 void
238 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
239 {
240     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
241 }
242
243
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)
248 {
249   if (md)
250     {
251       char buf[100];
252       size_t buflen;
253       int i;
254       
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) )
266             {
267               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
268                         "%sH%d", buflen? " ":"",i);
269               buflen += strlen (buf+buflen);
270             }
271       write_status_text ( STATUS_BEGIN_SIGNING, buf );
272     }
273   else
274     write_status ( STATUS_BEGIN_SIGNING );
275 }
276
277
278 static int
279 myread(int fd, void *buf, size_t count)
280 {
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++;
291         }
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
298         }
299     }    
300     return rc;
301 }
302
303
304
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 )
310 {
311   int i, len;
312   char *string;
313   
314   if (statusfp != stdout)
315     fflush (stdout);
316   
317   write_status_text (getbool? STATUS_GET_BOOL :
318                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
319
320   for (string = NULL, i = len = 200; ; i++ ) 
321     {
322       if (i >= len-1 ) 
323         {
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;
331         }
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 ) 
336         {
337           /* Found ETX - Cancel the line and return a sole ETX.  */
338           string[0] = CONTROL_D;
339           i = 1;
340           break;
341         }
342     }
343   string[i] = 0;
344
345   write_status (STATUS_GOT_IT);
346
347   if (getbool)   /* Fixme: is this correct??? */
348     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
349
350   return string;
351 }
352
353
354
355 int
356 cpr_enabled()
357 {
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;
365 }
366
367 char *
368 cpr_get_no_help( const char *keyword, const char *prompt )
369 {
370     char *p;
371
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;
381     }
382 }
383
384 char *
385 cpr_get( const char *keyword, const char *prompt )
386 {
387     char *p;
388
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 );
400         }
401         else
402             return p;
403     }
404 }
405
406
407 char *
408 cpr_get_utf8( const char *keyword, const char *prompt )
409 {
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;
416     }
417     return p;
418 }
419
420 char *
421 cpr_get_hidden( const char *keyword, const char *prompt )
422 {
423     char *p;
424
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 );
436         }
437         else
438             return p;
439     }
440 }
441
442 void
443 cpr_kill_prompt(void)
444 {
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;
453 }
454
455 int
456 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
457 {
458     int yes;
459     char *p;
460
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 );
473         }
474         else {
475             tty_kill_prompt();
476             yes = answer_is_yes(p);
477             xfree(p);
478             return yes;
479         }
480     }
481 }
482
483 int
484 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
485 {
486     int yes;
487     char *p;
488
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 );
501         }
502         else {
503             tty_kill_prompt();
504             yes = answer_is_yes_no_quit(p);
505             xfree(p);
506             return yes;
507         }
508     }
509 }
510
511
512 int
513 cpr_get_answer_okay_cancel (const char *keyword,
514                             const char *prompt,
515                             int def_answer)
516 {
517   int yes;
518   char *answer = NULL;
519   char *p;
520
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
527
528   if (answer)
529     {
530       yes = answer_is_okay_cancel (answer, def_answer);
531       xfree (answer);
532       return yes;
533     }
534
535   for(;;)
536     {
537       p = tty_get( prompt );
538       trim_spaces(p); /* it is okay to do this here */
539       if (*p == '?' && !p[1])
540         {
541           xfree(p);
542           display_online_help (keyword);
543         }
544       else
545         {
546           tty_kill_prompt();
547           yes = answer_is_okay_cancel (p, def_answer);
548           xfree(p);
549           return yes;
550         }
551     }
552 }