./autogen.sh --build-w32ce does now succeed.
[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 #ifdef HAVE_SIGNAL_H
28 # include <signal.h>
29 #endif
30
31 #include "gpg.h"
32 #include "util.h"
33 #include "status.h"
34 #include "ttyio.h"
35 #include "options.h"
36 #include "main.h"
37 #include "i18n.h"
38 #include "cipher.h" /* for progress functions */
39
40 #define CONTROL_D ('D' - 'A' + 1)
41
42
43
44 static FILE *statusfp;
45
46
47 static void
48 progress_cb (void *ctx, const char *what, int printchar,
49              int current, int total)
50 {
51   char buf[50];
52
53   (void)ctx;
54
55   if ( printchar == '\n' && !strcmp (what, "primegen") )
56     snprintf (buf, sizeof buf -1, "%.20s X 100 100", what );
57   else
58     snprintf (buf, sizeof buf -1, "%.20s %c %d %d",
59               what, printchar=='\n'?'X':printchar, current, total );
60   write_status_text (STATUS_PROGRESS, buf);
61 }
62
63
64 /* Return true if the status message NO may currently be issued.  We
65    need this to avoid syncronisation problem while auto retrieving a
66    key.  There it may happen that a status NODATA is issued for a non
67    available key and the user may falsely interpret this has a missing
68    signature. */
69 static int
70 status_currently_allowed (int no)
71 {
72   if (!glo_ctrl.in_auto_key_retrieve)
73     return 1; /* Yes. */
74
75   /* We allow some statis anyway, so that import statistics are
76      correct and to avoid problems if the retriebval subsystem will
77      prompt the user. */
78   switch (no)
79     {
80     case STATUS_GET_BOOL:        
81     case STATUS_GET_LINE:        
82     case STATUS_GET_HIDDEN:      
83     case STATUS_GOT_IT:  
84     case STATUS_IMPORTED:
85     case STATUS_IMPORT_OK:      
86     case STATUS_IMPORT_CHECK:  
87     case STATUS_IMPORT_RES:
88       return 1; /* Yes. */
89     default:
90       break;
91     }
92   return 0; /* No. */
93 }
94
95
96 void
97 set_status_fd ( int fd )
98 {
99     static int last_fd = -1;
100
101     if ( fd != -1 && last_fd == fd )
102         return;
103
104     if ( statusfp && statusfp != stdout && statusfp != stderr )
105         fclose (statusfp);
106     statusfp = NULL;
107     if ( fd == -1 ) 
108         return;
109
110     if( fd == 1 )
111         statusfp = stdout;
112     else if( fd == 2 )
113         statusfp = stderr;
114     else
115         statusfp = fdopen( fd, "w" );
116     if( !statusfp ) {
117         log_fatal("can't open fd %d for status output: %s\n",
118                   fd, strerror(errno));
119     }
120     last_fd = fd;
121
122     gcry_set_progress_handler ( progress_cb, NULL );
123 }
124
125 int
126 is_status_enabled()
127 {
128     return !!statusfp;
129 }
130
131 void
132 write_status ( int no )
133 {
134     write_status_text( no, NULL );
135 }
136
137 void
138 write_status_text ( int no, const char *text)
139 {
140     if( !statusfp || !status_currently_allowed (no) )
141         return;  /* Not enabled or allowed. */
142
143     fputs ( "[GNUPG:] ", statusfp );
144     fputs ( get_status_string (no), statusfp );
145     if( text ) {
146         putc ( ' ', statusfp );
147         for (; *text; text++) {
148             if (*text == '\n')
149                 fputs ( "\\n", statusfp );
150             else if (*text == '\r')
151                 fputs ( "\\r", statusfp );
152             else 
153                 putc ( *(const byte *)text,  statusfp );
154         }
155     }
156     putc ('\n',statusfp);
157     if ( fflush (statusfp) && opt.exit_on_status_write_error )
158       g10_exit (0);
159 }
160
161
162 /* Wrte an ERROR status line using a full gpg-error error value.  */
163 void
164 write_status_error (const char *where, gpg_error_t err)
165 {
166   if (!statusfp || !status_currently_allowed (STATUS_ERROR))
167     return;  /* Not enabled or allowed. */
168
169   fprintf (statusfp, "[GNUPG:] %s %s %u\n", 
170            get_status_string (STATUS_ERROR), where, err);
171   if (fflush (statusfp) && opt.exit_on_status_write_error)
172     g10_exit (0);
173 }
174
175
176 /* Same as above but only putputs the error code. */
177 void
178 write_status_errcode (const char *where, int errcode)
179 {
180   if (!statusfp || !status_currently_allowed (STATUS_ERROR))
181     return;  /* Not enabled or allowed. */
182
183   fprintf (statusfp, "[GNUPG:] %s %s %u\n", 
184            get_status_string (STATUS_ERROR), where, gpg_err_code (errcode));
185   if (fflush (statusfp) && opt.exit_on_status_write_error)
186     g10_exit (0);
187 }
188
189
190 /*
191  * Write a status line with a buffer using %XX escapes.  If WRAP is >
192  * 0 wrap the line after this length.  If STRING is not NULL it will
193  * be prepended to the buffer, no escaping is done for string.
194  * A wrap of -1 forces spaces not to be encoded as %20.
195  */
196 void
197 write_status_text_and_buffer ( int no, const char *string,
198                                const char *buffer, size_t len, int wrap )
199 {
200     const char *s, *text;
201     int esc, first;
202     int lower_limit = ' ';
203     size_t n, count, dowrap;
204
205     if( !statusfp || !status_currently_allowed (no) )
206         return;  /* Not enabled or allowed. */
207     
208     if (wrap == -1) {
209         lower_limit--;
210         wrap = 0;
211     }
212
213     text = get_status_string (no);
214     count = dowrap = first = 1;
215     do {
216         if (dowrap) {
217             fprintf (statusfp, "[GNUPG:] %s ", text );
218             count = dowrap = 0;
219             if (first && string) {
220                 fputs (string, statusfp);
221                 count += strlen (string);
222                 /* Make sure that there is space after the string.  */
223                 if (*string && string[strlen (string)-1] != ' ')
224                   {
225                     putc (' ', statusfp);
226                     count++;
227                   }
228             }
229             first = 0;
230         }
231         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
232             if ( *s == '%' || *(const byte*)s <= lower_limit 
233                            || *(const byte*)s == 127 ) 
234                 esc = 1;
235             if ( wrap && ++count > wrap ) {
236                 dowrap=1;
237                 break;
238             }
239         }
240         if (esc) {
241             s--; n++;
242         }
243         if (s != buffer) 
244             fwrite (buffer, s-buffer, 1, statusfp );
245         if ( esc ) {
246             fprintf (statusfp, "%%%02X", *(const byte*)s );
247             s++; n--;
248         }
249         buffer = s;
250         len = n;
251         if ( dowrap && len )
252             putc ( '\n', statusfp );
253     } while ( len );
254
255     putc ('\n',statusfp);
256     if ( fflush (statusfp) && opt.exit_on_status_write_error )
257       g10_exit (0);
258 }
259
260 void
261 write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
262 {
263     write_status_text_and_buffer (no, NULL, buffer, len, wrap);
264 }
265
266
267 /* Print the BEGIN_SIGNING status message.  If MD is not NULL it is
268    used to retrieve the hash algorithms used for the message. */
269 void
270 write_status_begin_signing (gcry_md_hd_t md)
271 {
272   if (md)
273     {
274       char buf[100];
275       size_t buflen;
276       int i;
277       
278       /* We use a hard coded list of possible algorithms.  Using other
279          algorithms than specified by OpenPGP does not make sense
280          anyway.  We do this out of performance reasons: Walking all
281          the 110 allowed Ids is not a good idea given the way the
282          check is implemented in libgcrypt.  Recall that the only use
283          of this status code is to create the micalg algorithm for
284          PGP/MIME. */
285       buflen = 0;
286       for (i=1; i <= 11; i++)
287         if (i < 4 || i > 7)
288           if ( gcry_md_is_enabled (md, i) && buflen < DIM(buf) )
289             {
290               snprintf (buf+buflen, DIM(buf) - buflen - 1, 
291                         "%sH%d", buflen? " ":"",i);
292               buflen += strlen (buf+buflen);
293             }
294       write_status_text ( STATUS_BEGIN_SIGNING, buf );
295     }
296   else
297     write_status ( STATUS_BEGIN_SIGNING );
298 }
299
300
301 static int
302 myread(int fd, void *buf, size_t count)
303 {
304     int rc;
305     do {
306         rc = read( fd, buf, count );
307     } while ( rc == -1 && errno == EINTR );
308     if ( !rc && count ) {
309         static int eof_emmited=0;
310         if ( eof_emmited < 3 ) {
311             *(char*)buf = CONTROL_D;
312             rc = 1;
313             eof_emmited++;
314         }
315         else { /* Ctrl-D not caught - do something reasonable */
316 #ifdef HAVE_DOSISH_SYSTEM
317 #ifndef HAVE_W32CE_SYSTEM
318             raise (SIGINT);  /* nothing to hangup under DOS */
319 #endif
320 #else
321             raise (SIGHUP); /* no more input data */
322 #endif
323         }
324     }    
325     return rc;
326 }
327
328
329
330 /* Request a string from the client over the command-fd.  If GETBOOL
331    is set the function returns a static string (do not free) if the
332    netered value was true or NULL if the entered value was false.  */
333 static char *
334 do_get_from_fd ( const char *keyword, int hidden, int getbool )
335 {
336   int i, len;
337   char *string;
338   
339   if (statusfp != stdout)
340     fflush (stdout);
341   
342   write_status_text (getbool? STATUS_GET_BOOL :
343                      hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword);
344
345   for (string = NULL, i = len = 200; ; i++ ) 
346     {
347       if (i >= len-1 ) 
348         {
349           char *save = string;
350           len += 100;
351           string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
352           if (save)
353             memcpy (string, save, i );
354           else
355             i = 0;
356         }
357       /* Fixme: why not use our read_line function here? */
358       if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n'  )
359         break;
360       else if ( string[i] == CONTROL_D ) 
361         {
362           /* Found ETX - Cancel the line and return a sole ETX.  */
363           string[0] = CONTROL_D;
364           i = 1;
365           break;
366         }
367     }
368   string[i] = 0;
369
370   write_status (STATUS_GOT_IT);
371
372   if (getbool)   /* Fixme: is this correct??? */
373     return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
374
375   return string;
376 }
377
378
379
380 int
381 cpr_enabled()
382 {
383     if( opt.command_fd != -1 )
384         return 1;
385     return 0;
386 }
387
388 char *
389 cpr_get_no_help( const char *keyword, const char *prompt )
390 {
391     char *p;
392
393     if( opt.command_fd != -1 )
394         return do_get_from_fd ( keyword, 0, 0 );
395     for(;;) {
396         p = tty_get( prompt );
397         return p;
398     }
399 }
400
401 char *
402 cpr_get( const char *keyword, const char *prompt )
403 {
404     char *p;
405
406     if( opt.command_fd != -1 )
407         return do_get_from_fd ( keyword, 0, 0 );
408     for(;;) {
409         p = tty_get( prompt );
410         if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
411             xfree(p);
412             display_online_help( keyword );
413         }
414         else
415             return p;
416     }
417 }
418
419
420 char *
421 cpr_get_utf8( const char *keyword, const char *prompt )
422 {
423     char *p;
424     p = cpr_get( keyword, prompt );
425     if( p ) {
426         char *utf8 = native_to_utf8( p );
427         xfree( p );
428         p = utf8;
429     }
430     return p;
431 }
432
433 char *
434 cpr_get_hidden( const char *keyword, const char *prompt )
435 {
436     char *p;
437
438     if( opt.command_fd != -1 )
439         return do_get_from_fd ( keyword, 1, 0 );
440     for(;;) {
441         p = tty_get_hidden( prompt );
442         if( *p == '?' && !p[1] ) {
443             xfree(p);
444             display_online_help( keyword );
445         }
446         else
447             return p;
448     }
449 }
450
451 void
452 cpr_kill_prompt(void)
453 {
454     if( opt.command_fd != -1 )
455         return;
456     tty_kill_prompt();
457     return;
458 }
459
460 int
461 cpr_get_answer_is_yes( const char *keyword, const char *prompt )
462 {
463     int yes;
464     char *p;
465
466     if( opt.command_fd != -1 )
467         return !!do_get_from_fd ( keyword, 0, 1 );
468     for(;;) {
469         p = tty_get( prompt );
470         trim_spaces(p); /* it is okay to do this here */
471         if( *p == '?' && !p[1] ) {
472             xfree(p);
473             display_online_help( keyword );
474         }
475         else {
476             tty_kill_prompt();
477             yes = answer_is_yes(p);
478             xfree(p);
479             return yes;
480         }
481     }
482 }
483
484 int
485 cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
486 {
487     int yes;
488     char *p;
489
490     if( opt.command_fd != -1 )
491         return !!do_get_from_fd ( keyword, 0, 1 );
492     for(;;) {
493         p = tty_get( prompt );
494         trim_spaces(p); /* it is okay to do this here */
495         if( *p == '?' && !p[1] ) {
496             xfree(p);
497             display_online_help( keyword );
498         }
499         else {
500             tty_kill_prompt();
501             yes = answer_is_yes_no_quit(p);
502             xfree(p);
503             return yes;
504         }
505     }
506 }
507
508
509 int
510 cpr_get_answer_okay_cancel (const char *keyword,
511                             const char *prompt,
512                             int def_answer)
513 {
514   int yes;
515   char *answer = NULL;
516   char *p;
517
518   if( opt.command_fd != -1 )
519     answer = do_get_from_fd ( keyword, 0, 0 );
520
521   if (answer)
522     {
523       yes = answer_is_okay_cancel (answer, def_answer);
524       xfree (answer);
525       return yes;
526     }
527
528   for(;;)
529     {
530       p = tty_get( prompt );
531       trim_spaces(p); /* it is okay to do this here */
532       if (*p == '?' && !p[1])
533         {
534           xfree(p);
535           display_online_help (keyword);
536         }
537       else
538         {
539           tty_kill_prompt();
540           yes = answer_is_okay_cancel (p, def_answer);
541           xfree(p);
542           return yes;
543         }
544     }
545 }