Merged jnlib into common.
[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, 2010 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 /* Wrte an ERROR status line using a full gpg-error error value.  */
161 void
162 write_status_error (const char *where, gpg_error_t err)
163 {
164   if (!statusfp || !status_currently_allowed (STATUS_ERROR))
165     return;  /* Not enabled or allowed. */
166
167   fprintf (statusfp, "[GNUPG:] %s %s %u\n", 
168            get_status_string (STATUS_ERROR), where, err);
169   if (fflush (statusfp) && opt.exit_on_status_write_error)
170     g10_exit (0);
171 }
172
173
174 /* Same as above but only putputs the error code. */
175 void
176 write_status_errcode (const char *where, int errcode)
177 {
178   if (!statusfp || !status_currently_allowed (STATUS_ERROR))
179     return;  /* Not enabled or allowed. */
180
181   fprintf (statusfp, "[GNUPG:] %s %s %u\n", 
182            get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
183   if (fflush (statusfp) && opt.exit_on_status_write_error)
184     g10_exit (0);
185 }
186
187
188 /*
189  * Write a status line with a buffer using %XX escapes.  If WRAP is >
190  * 0 wrap the line after this length.  If STRING is not NULL it will
191  * be prepended to the buffer, no escaping is done for string.
192  * A wrap of -1 forces spaces not to be encoded as %20.
193  */
194 void
195 write_status_text_and_buffer ( int no, const char *string,
196                                const char *buffer, size_t len, int wrap )
197 {
198     const char *s, *text;
199     int esc, first;
200     int lower_limit = ' ';
201     size_t n, count, dowrap;
202
203     if( !statusfp || !status_currently_allowed (no) )
204         return;  /* Not enabled or allowed. */
205     
206     if (wrap == -1) {
207         lower_limit--;
208         wrap = 0;
209     }
210
211     text = get_status_string (no);
212     count = dowrap = first = 1;
213     do {
214         if (dowrap) {
215             fprintf (statusfp, "[GNUPG:] %s ", text );
216             count = dowrap = 0;
217             if (first && string) {
218                 fputs (string, statusfp);
219                 count += strlen (string);
220                 /* Make sure that there is space after the string.  */
221                 if (*string && string[strlen (string)-1] != ' ')
222                   {
223                     putc (' ', statusfp);
224                     count++;
225                   }
226             }
227             first = 0;
228         }
229         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
230             if ( *s == '%' || *(const byte*)s <= lower_limit 
231                            || *(const byte*)s == 127 ) 
232                 esc = 1;
233             if ( wrap && ++count > wrap ) {
234                 dowrap=1;
235                 break;
236             }
237         }
238         if (esc) {
239             s--; n++;
240         }
241         if (s != buffer) 
242             fwrite (buffer, s-buffer, 1, statusfp );
243         if ( esc ) {
244             fprintf (statusfp, "%%%02X", *(const byte*)s );
245             s++; n--;
246         }
247         buffer = s;
248         len = n;
249         if ( dowrap && len )
250             putc ( '\n', statusfp );
251     } while ( len );
252
253     putc ('\n',statusfp);
254     if ( fflush (statusfp) && opt.exit_on_status_write_error )
255       g10_exit (0);
256 }
257
258 void
259 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
260 {
261     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
262 }
263
264
265 /* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
266    used to retrieve the hash algorithms used for the message. */
267 void
268 write_status_begin_signing (gcry_md_hd_t md)
269 {
270   if (md)
271     {
272       char buf[100];
273       size_t buflen;
274       int i;
275       
276       /* We use a hard coded list of possible algorithms.  Using other
277          algorithms than specified by OpenPGP does not make sense
278          anyway.  We do this out of performance reasons: Walking all
279          the 110 allowed Ids is not a good idea given the way the
280          check is implemented in libgcrypt.  Recall that the only use
281          of this status code is to create the micalg algorithm for
282          PGP/MIME. */
283       buflen = 0;
284       for (i=1; i <= 11; i++)
285         if (i < 4 || i > 7)
286           if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
287             {
288               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
289                         "%sH%d", buflen? " ":"",i);
290               buflen += strlen (buf+buflen);
291             }
292       write_status_text ( STATUS_BEGIN_SIGNING, buf );
293     }
294   else
295     write_status ( STATUS_BEGIN_SIGNING );
296 }
297
298
299 static int
300 myread(int fd, void *buf, size_t count)
301 {
302     int rc;
303     do {
304         rc = read( fd, buf, count );
305     } while ( rc == -1 && errno == EINTR );
306     if ( !rc && count ) {
307         static int eof_emmited=0;
308         if ( eof_emmited < 3 ) {
309             *(char*)buf = CONTROL_D;
310             rc = 1;
311             eof_emmited++;
312         }
313         else { /* Ctrl-D not caught - do something reasonable */
314 #ifdef HAVE_DOSISH_SYSTEM
315             raise (SIGINT);  /* nothing to hangup under DOS */
316 #else
317             raise (SIGHUP); /* no more input data */
318 #endif
319         }
320     }    
321     return rc;
322 }
323
324
325
326 /* Request a string from the client over the command-fd.  If GETBOOL
327    is set the function returns a static string (do not free) if the
328    netered value was true or NULL if the entered value was false.  */
329 static char *
330 do_get_from_fd ( const char *keyword, int hidden, int getbool )
331 {
332   int i, len;
333   char *string;
334   
335   if (statusfp != stdout)
336     fflush (stdout);
337   
338   write_status_text (getbool? STATUS_GET_BOOL :
339                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
340
341   for (string = NULL, i = len = 200; ; i++ ) 
342     {
343       if (i >= len-1 ) 
344         {
345           char *save = string;
346           len += 100;
347           string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
348           if (save)
349             memcpy (string, save, i );
350           else
351             i = 0;
352         }
353       /* Fixme: why not use our read_line function here? */
354       if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
355         break;
356       else if ( string[i] == CONTROL_D ) 
357         {
358           /* Found ETX - Cancel the line and return a sole ETX.  */
359           string[0] = CONTROL_D;
360           i = 1;
361           break;
362         }
363     }
364   string[i] = 0;
365
366   write_status (STATUS_GOT_IT);
367
368   if (getbool)   /* Fixme: is this correct??? */
369     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
370
371   return string;
372 }
373
374
375
376 int
377 cpr_enabled()
378 {
379     if( opt.command_fd != -1 )
380         return 1;
381     return 0;
382 }
383
384 char *
385 cpr_get_no_help( 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     for(;;) {
392         p = tty_get( prompt );
393         return p;
394     }
395 }
396
397 char *
398 cpr_get( const char *keyword, const char *prompt )
399 {
400     char *p;
401
402     if( opt.command_fd != -1 )
403         return do_get_from_fd ( keyword, 0, 0 );
404     for(;;) {
405         p = tty_get( prompt );
406         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
407             xfree(p);
408             display_online_help( keyword );
409         }
410         else
411             return p;
412     }
413 }
414
415
416 char *
417 cpr_get_utf8( const char *keyword, const char *prompt )
418 {
419     char *p;
420     p = cpr_get( keyword, prompt );
421     if( p ) {
422         char *utf8 = native_to_utf8( p );
423         xfree( p );
424         p = utf8;
425     }
426     return p;
427 }
428
429 char *
430 cpr_get_hidden( const char *keyword, const char *prompt )
431 {
432     char *p;
433
434     if( opt.command_fd != -1 )
435         return do_get_from_fd ( keyword, 1, 0 );
436     for(;;) {
437         p = tty_get_hidden( prompt );
438         if( *p == '?' && !p[1] ) {
439             xfree(p);
440             display_online_help( keyword );
441         }
442         else
443             return p;
444     }
445 }
446
447 void
448 cpr_kill_prompt(void)
449 {
450     if( opt.command_fd != -1 )
451         return;
452     tty_kill_prompt();
453     return;
454 }
455
456 int
457 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
458 {
459     int yes;
460     char *p;
461
462     if( opt.command_fd != -1 )
463         return !!do_get_from_fd ( keyword, 0, 1 );
464     for(;;) {
465         p = tty_get( prompt );
466         trim_spaces(p); /* it is okay to do this here */
467         if( *p == '?' && !p[1] ) {
468             xfree(p);
469             display_online_help( keyword );
470         }
471         else {
472             tty_kill_prompt();
473             yes = answer_is_yes(p);
474             xfree(p);
475             return yes;
476         }
477     }
478 }
479
480 int
481 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
482 {
483     int yes;
484     char *p;
485
486     if( opt.command_fd != -1 )
487         return !!do_get_from_fd ( keyword, 0, 1 );
488     for(;;) {
489         p = tty_get( prompt );
490         trim_spaces(p); /* it is okay to do this here */
491         if( *p == '?' && !p[1] ) {
492             xfree(p);
493             display_online_help( keyword );
494         }
495         else {
496             tty_kill_prompt();
497             yes = answer_is_yes_no_quit(p);
498             xfree(p);
499             return yes;
500         }
501     }
502 }
503
504
505 int
506 cpr_get_answer_okay_cancel (const char *keyword,
507                             const char *prompt,
508                             int def_answer)
509 {
510   int yes;
511   char *answer = NULL;
512   char *p;
513
514   if( opt.command_fd != -1 )
515     answer = do_get_from_fd ( keyword, 0, 0 );
516
517   if (answer)
518     {
519       yes = answer_is_okay_cancel (answer, def_answer);
520       xfree (answer);
521       return yes;
522     }
523
524   for(;;)
525     {
526       p = tty_get( prompt );
527       trim_spaces(p); /* it is okay to do this here */
528       if (*p == '?' && !p[1])
529         {
530           xfree(p);
531           display_online_help (keyword);
532         }
533       else
534         {
535           tty_kill_prompt();
536           yes = answer_is_okay_cancel (p, def_answer);
537           xfree(p);
538           return yes;
539         }
540     }
541 }