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