* certpath.c (gpgsm_validate_path): Ask the agent to add the
[gnupg.git] / util / ttyio.c
1 /* ttyio.c -  tty i/O functions
2  *      Copyright (C) 1998, 2000 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #ifdef HAVE_TCGETATTR
28   #include <termios.h>
29 #else
30   #ifdef HAVE_TERMIO_H
31     /* simulate termios with termio */
32     #include <termio.h>
33     #define termios termio
34     #define tcsetattr ioctl
35     #define TCSAFLUSH TCSETAF
36     #define tcgetattr(A,B) ioctl(A,TCGETA,B)
37     #define HAVE_TCGETATTR
38   #endif
39 #endif
40 #ifdef __MINGW32__ /* use the odd Win32 functions */
41   #include <windows.h>
42   #ifdef HAVE_TCGETATTR
43      #error mingw32 and termios
44   #endif
45 #endif
46 #include <errno.h>
47 #include <ctype.h>
48 #include <gcrypt.h>
49 #include "util.h"
50 #include "memory.h"
51 #include "ttyio.h"
52
53 #define CONTROL_D ('D' - 'A' + 1)
54
55
56 #ifdef __MINGW32__ /* use the odd Win32 functions */
57 static struct {
58     HANDLE in, out;
59 } con;
60 #define DEF_INPMODE  (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT    \
61                                         |ENABLE_PROCESSED_INPUT )
62 #define HID_INPMODE  (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
63 #define DEF_OUTMODE  (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
64
65 #else /* yeah, we have a real OS */
66 static FILE *ttyfp = NULL;
67 #endif
68
69 static int initialized;
70 static int last_prompt_len;
71 static int batchmode;
72 static int no_terminal;
73
74 #ifdef HAVE_TCGETATTR
75 static struct termios termsave;
76 static int restore_termios;
77 #endif
78
79 #ifdef HAVE_TCGETATTR
80 static void
81 cleanup(void)
82 {
83     if( restore_termios ) {
84         restore_termios = 0; /* do it prios in case it is interrupted again */
85         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
86             log_error("tcsetattr() failed: %s\n", strerror(errno) );
87     }
88 }
89 #endif
90
91 static void
92 init_ttyfp(void)
93 {
94     if( initialized )
95         return;
96
97   #if defined(__MINGW32__)
98     {
99         SECURITY_ATTRIBUTES sa;
100
101         memset(&sa, 0, sizeof(sa));
102         sa.nLength = sizeof(sa);
103         sa.bInheritHandle = TRUE;
104         con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
105                                FILE_SHARE_READ|FILE_SHARE_WRITE,
106                                &sa, OPEN_EXISTING, 0, 0 );
107         if( con.out == INVALID_HANDLE_VALUE )
108             log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
109         memset(&sa, 0, sizeof(sa));
110         sa.nLength = sizeof(sa);
111         sa.bInheritHandle = TRUE;
112         con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
113                                FILE_SHARE_READ|FILE_SHARE_WRITE,
114                                &sa, OPEN_EXISTING, 0, 0 );
115         if( con.in == INVALID_HANDLE_VALUE )
116             log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
117     }
118     SetConsoleMode(con.in, DEF_INPMODE );
119     SetConsoleMode(con.out, DEF_OUTMODE );
120
121   #elif defined(__EMX__)
122     ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
123   #else
124     ttyfp = batchmode? stderr : fopen("/dev/tty", "r+");
125     if( !ttyfp ) {
126         log_error("cannot open /dev/tty: %s\n", strerror(errno) );
127         exit(2);
128     }
129   #endif
130   #ifdef HAVE_TCGETATTR
131     atexit( cleanup );
132   #endif
133     initialized = 1;
134 }
135
136 int
137 tty_batchmode( int onoff )
138 {
139     int old = batchmode;
140     if( onoff != -1 )
141         batchmode = onoff;
142     return old;
143 }
144
145 int
146 tty_no_terminal(int onoff)
147 {
148     int old = no_terminal;
149     no_terminal = onoff ? 1 : 0;
150     return old;
151 }
152
153 void
154 tty_printf( const char *fmt, ... )
155 {
156     va_list arg_ptr;
157
158     if (no_terminal)
159         return;
160
161     if( !initialized )
162         init_ttyfp();
163
164     va_start( arg_ptr, fmt ) ;
165   #ifdef __MINGW32__
166     { static char *buf;
167       static size_t bufsize;
168         int n;
169         DWORD nwritten;
170
171       #if 0 /* the dox say, that there is a snprintf, but I didn't found
172              * it, so we use a static buffer for now */
173         do {
174             if( n == -1 || !buf ) {
175                 gcry_free(buf);
176                 bufsize += 200;
177                 /* better check the new size; (we use M$ functions) */
178                 if( bufsize > 50000 )
179                     log_bug("vsnprintf probably failed\n");
180                 buf = gcry_xmalloc( bufsize );
181             }
182             n = _vsnprintf(buf, bufsize-1, fmt, arg_ptr);
183         } while( n == -1 );
184       #else
185         if( !buf ) {
186             bufsize += 1000;
187             buf = gcry_xmalloc( bufsize );
188         }
189         n = vsprintf(buf, fmt, arg_ptr);
190         if( n == -1 )
191             log_bug("vsprintf() failed\n");
192       #endif
193
194         if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
195             log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
196         if( n != nwritten )
197             log_fatal("WriteConsole failed: %d != %ld\n", n, nwritten );
198         last_prompt_len += n;
199     }
200   #else
201     last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
202     fflush(ttyfp);
203   #endif
204     va_end(arg_ptr);
205 }
206
207
208 /****************
209  * Print a string, but filter all control characters out.
210  */
211 void
212 tty_print_string( byte *p, size_t n )
213 {
214     if (no_terminal)
215         return;
216
217     if( !initialized )
218         init_ttyfp();
219
220   #ifdef __MINGW32__
221     /* not so effective, change it if you want */
222     for( ; n; n--, p++ )
223         if( iscntrl( *p ) ) {
224             if( *p == '\n' )
225                 tty_printf("\\n");
226             else if( !*p )
227                 tty_printf("\\0");
228             else
229                 tty_printf("\\x%02x", *p);
230         }
231         else
232             tty_printf("%c", *p);
233   #else
234     for( ; n; n--, p++ )
235         if( iscntrl( *p ) ) {
236             putc('\\', ttyfp);
237             if( *p == '\n' )
238                 putc('n', ttyfp);
239             else if( !*p )
240                 putc('0', ttyfp);
241             else
242                 fprintf(ttyfp, "x%02x", *p );
243         }
244         else
245             putc(*p, ttyfp);
246   #endif
247 }
248
249 void
250 tty_print_utf8_string2( byte *p, size_t n, size_t max_n )
251 {
252     size_t i;
253     char *buf;
254
255     if (no_terminal)
256         return;
257
258     /* we can handle plain ascii simpler, so check for it first */
259     for(i=0; i < n; i++ ) {
260         if( p[i] & 0x80 )
261             break;
262     }
263     if( i < n ) {
264         buf = utf8_to_native( p, n );
265         if( strlen( buf ) > max_n ) {
266             buf[max_n] = 0;
267         }
268         /*(utf8 conversion already does the control character quoting)*/
269         tty_printf("%s", buf );
270         gcry_free( buf );
271     }
272     else {
273         if( n > max_n ) {
274             n = max_n;
275         }
276         tty_print_string( p, n );
277     }
278 }
279
280
281 void
282 tty_print_utf8_string( byte *p, size_t n )
283 {
284     tty_print_utf8_string2( p, n, n );
285 }
286
287
288
289
290 static char *
291 do_get( const char *prompt, int hidden )
292 {
293     char *buf;
294     byte cbuf[1];
295     int c, n, i;
296
297     if( batchmode ) {
298         log_error("Sorry, we are in batchmode - can't get input\n");
299         exit(2);
300     }
301
302     if (no_terminal) {
303         log_error("Sorry, no terminal at all requested - can't get input\n");
304         exit(2);
305     }
306
307     if( !initialized )
308         init_ttyfp();
309
310     last_prompt_len = 0;
311     tty_printf( prompt );
312     buf = gcry_xmalloc(n=50);
313     i = 0;
314
315   #ifdef __MINGW32__ /* windoze version */
316     if( hidden )
317         SetConsoleMode(con.in, HID_INPMODE );
318
319     for(;;) {
320         DWORD nread;
321
322         if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
323             log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
324         if( !nread )
325             continue;
326         if( *cbuf == '\n' )
327             break;
328
329         if( !hidden )
330             last_prompt_len++;
331         c = *cbuf;
332         if( c == '\t' )
333             c = ' ';
334         else if( c > 0xa0 )
335             ; /* we don't allow 0xa0, as this is a protected blank which may
336                * confuse the user */
337         else if( iscntrl(c) )
338             continue;
339         if( !(i < n-1) ) {
340             n += 50;
341             buf = gcry_xrealloc( buf, n );
342         }
343         buf[i++] = c;
344     }
345
346     if( hidden )
347         SetConsoleMode(con.in, DEF_INPMODE );
348
349   #else /* unix version */
350     if( hidden ) {
351       #ifdef HAVE_TCGETATTR
352         struct termios term;
353
354         if( tcgetattr(fileno(ttyfp), &termsave) )
355             log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
356         restore_termios = 1;
357         term = termsave;
358         term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
359         if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
360             log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
361       #endif
362     }
363
364     /* fixme: How can we avoid that the \n is echoed w/o disabling
365      * canonical mode - w/o this kill_prompt can't work */
366     while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
367         if( !hidden )
368             last_prompt_len++;
369         c = *cbuf;
370         if( c == CONTROL_D )
371             log_info("control d found\n");
372         if( c == '\t' )
373             c = ' ';
374         else if( c > 0xa0 )
375             ; /* we don't allow 0xa0, as this is a protected blank which may
376                * confuse the user */
377         else if( iscntrl(c) )
378             continue;
379         if( !(i < n-1) ) {
380             n += 50;
381             buf = gcry_xrealloc( buf, n );
382         }
383         buf[i++] = c;
384     }
385     if( *cbuf != '\n' ) {
386         buf[0] = CONTROL_D;
387         i = 1;
388     }
389
390
391     if( hidden ) {
392       #ifdef HAVE_TCGETATTR
393         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
394             log_error("tcsetattr() failed: %s\n", strerror(errno) );
395         restore_termios = 0;
396       #endif
397     }
398   #endif /* end unix version */
399     buf[i] = 0;
400     return buf;
401 }
402
403
404 char *
405 tty_get( const char *prompt )
406 {
407     return do_get( prompt, 0 );
408 }
409
410 char *
411 tty_get_hidden( const char *prompt )
412 {
413     return do_get( prompt, 1 );
414 }
415
416
417 void
418 tty_kill_prompt()
419 {
420     if ( no_terminal )
421         return;
422
423     if( !initialized )
424         init_ttyfp();
425
426     if( batchmode )
427         last_prompt_len = 0;
428     if( !last_prompt_len )
429         return;
430   #ifdef __MINGW32__
431     tty_printf("\r%*s\r", last_prompt_len, "");
432   #else
433     {
434         int i;
435         putc('\r', ttyfp);
436         for(i=0; i < last_prompt_len; i ++ )
437             putc(' ', ttyfp);
438         putc('\r', ttyfp);
439         fflush(ttyfp);
440     }
441   #endif
442     last_prompt_len = 0;
443 }
444
445
446 int
447 tty_get_answer_is_yes( const char *prompt )
448 {
449     int yes;
450     char *p = tty_get( prompt );
451     tty_kill_prompt();
452     yes = answer_is_yes(p);
453     gcry_free(p);
454     return yes;
455 }
456