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