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