Bump to 2.4.3
[platform/upstream/gpg2.git] / common / ttyio.c
1 /* ttyio.c -  tty i/O functions
2  * Copyright (C) 1997-2019 Werner Koch
3  * Copyright (C) 1998-2020 Free Software Foundation, Inc.
4  * Copyright (C) 2015-2020 g10 Code GmbH
5  *
6  * This file is part of GnuPG.
7  *
8  * This file is free software; you can redistribute it and/or modify
9  * it under the terms of either
10  *
11  *   - the GNU Lesser General Public License as published by the Free
12  *     Software Foundation; either version 3 of the License, or (at
13  *     your option) any later version.
14  *
15  * or
16  *
17  *   - the GNU General Public License as published by the Free
18  *     Software Foundation; either version 2 of the License, or (at
19  *     your option) any later version.
20  *
21  * or both in parallel, as here.
22  *
23  * This file is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, see <https://www.gnu.org/licenses/>.
30  * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
31  */
32
33 #include <config.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <unistd.h>
39
40 #ifdef HAVE_TCGETATTR
41 # include <termios.h>
42 #else
43 # ifdef HAVE_TERMIO_H
44 /* simulate termios with termio */
45 #  include <termio.h>
46 #  define termios termio
47 #  define tcsetattr ioctl
48 #  define TCSAFLUSH TCSETAF
49 #  define tcgetattr(A,B) ioctl(A,TCGETA,B)
50 #  define HAVE_TCGETATTR
51 # endif
52 #endif
53 #ifdef HAVE_W32_SYSTEM
54 # ifdef HAVE_WINSOCK2_H
55 #  include <winsock2.h>
56 # endif
57 # include <windows.h>
58 # ifdef HAVE_TCGETATTR
59 #  error mingw32 and termios
60 # endif
61 #endif
62 #include <errno.h>
63 #include <ctype.h>
64
65 #include "util.h"
66 #include "ttyio.h"
67 #include "i18n.h"
68 #include "common-defs.h"
69
70 #define CONTROL_D ('D' - 'A' + 1)
71
72
73 #ifdef HAVE_W32_SYSTEM
74 static struct {
75     HANDLE in, out;
76 } con;
77 #define DEF_INPMODE  (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT    \
78                                        |ENABLE_PROCESSED_INPUT )
79 #define HID_INPMODE  (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
80 #define DEF_OUTMODE  (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
81
82 #else /* Unix */
83 static FILE *ttyfp = NULL;
84 #endif /* Unix */
85
86 static int initialized;
87 static int last_prompt_len;
88 static int batchmode;
89 static int no_terminal;
90
91 #ifdef HAVE_TCGETATTR
92     static struct termios termsave;
93     static int restore_termios;
94 #endif
95
96 /* Hooks set by gpgrlhelp.c if required. */
97 static void (*my_rl_set_completer) (rl_completion_func_t *);
98 static void (*my_rl_inhibit_completion) (int);
99 static void (*my_rl_cleanup_after_signal) (void);
100 static void (*my_rl_init_stream) (FILE *);
101 static char *(*my_rl_readline) (const char*);
102 static void (*my_rl_add_history) (const char*);
103 static int (*my_rl_rw_history)(const char *, int, int);
104
105 /* This is a wrapper around ttyname so that we can use it even when
106    the standard streams are redirected.  It figures the name out the
107    first time and returns it in a statically allocated buffer. */
108 const char *
109 tty_get_ttyname (void)
110 {
111   static char *name;
112
113   /* On a GNU system ctermid() always return /dev/tty, so this does
114      not make much sense - however if it is ever changed we do the
115      Right Thing now. */
116 #ifdef HAVE_CTERMID
117   static int got_name;
118
119   if (!got_name)
120     {
121       const char *s;
122       /* Note that despite our checks for these macros the function is
123          not necessarily thread save.  We mainly do this for
124          portability reasons, in case L_ctermid is not defined. */
125 # if defined(_POSIX_THREAD_SAFE_FUNCTIONS) || defined(_POSIX_TRHEADS)
126       char buffer[L_ctermid];
127       s = ctermid (buffer);
128 # else
129       s = ctermid (NULL);
130 # endif
131       if (s)
132         name = strdup (s);
133       got_name = 1;
134     }
135 #endif /*HAVE_CTERMID*/
136   /* Assume the standard tty on memory error or when there is no
137      ctermid. */
138   return name? name : "/dev/tty";
139 }
140
141
142
143 #ifdef HAVE_TCGETATTR
144 static void
145 cleanup(void)
146 {
147   if (restore_termios)
148     {
149       restore_termios = 0; /* do it prior in case it is interrupted again */
150       if (tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave))
151         log_error ("tcsetattr() failed: %s\n", strerror (errno));
152     }
153 }
154 #endif /*HAVE_TCGETATTR*/
155
156
157 static void
158 init_ttyfp(void)
159 {
160   if (initialized)
161     return;
162
163 #ifdef HAVE_W32_SYSTEM
164   {
165     SECURITY_ATTRIBUTES sa;
166
167     memset (&sa, 0, sizeof(sa));
168     sa.nLength = sizeof(sa);
169     sa.bInheritHandle = TRUE;
170     con.out = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE,
171                            FILE_SHARE_READ|FILE_SHARE_WRITE,
172                            &sa, OPEN_EXISTING, 0, 0 );
173     if (con.out == INVALID_HANDLE_VALUE)
174       log_fatal ("open(CONOUT$) failed: %s\n", w32_strerror (-1));
175
176     memset (&sa, 0, sizeof(sa));
177     sa.nLength = sizeof(sa);
178     sa.bInheritHandle = TRUE;
179     con.in = CreateFileA ("CONIN$", GENERIC_READ|GENERIC_WRITE,
180                           FILE_SHARE_READ|FILE_SHARE_WRITE,
181                           &sa, OPEN_EXISTING, 0, 0 );
182     if (con.in == INVALID_HANDLE_VALUE)
183       log_fatal ("open(CONIN$) failed: %s\n", w32_strerror (-1));
184   }
185   SetConsoleMode (con.in, DEF_INPMODE);
186   SetConsoleMode (con.out, DEF_OUTMODE);
187
188 #else /* Unix */
189   ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+");
190   if (!ttyfp)
191     {
192       log_error ("cannot open '%s': %s\n", tty_get_ttyname (), strerror(errno));
193       exit (2);
194     }
195   if (my_rl_init_stream)
196     my_rl_init_stream (ttyfp);
197 #endif /* Unix */
198
199 #ifdef HAVE_TCGETATTR
200   atexit (cleanup);
201 #endif
202
203   initialized = 1;
204 }
205
206
207 int
208 tty_batchmode( int onoff )
209 {
210   int old = batchmode;
211   if (onoff != -1)
212     batchmode = onoff;
213   return old;
214 }
215
216 int
217 tty_no_terminal(int onoff)
218 {
219   int old = no_terminal;
220   no_terminal = onoff ? 1 : 0;
221   return old;
222 }
223
224
225 #ifdef HAVE_W32_SYSTEM
226 /* Write the UTF-8 encoded STRING to the console.  */
227 static void
228 w32_write_console (const char *string)
229 {
230   wchar_t *wstring;
231   DWORD n, nwritten;
232
233   wstring = utf8_to_wchar (string);
234   if (!wstring)
235     log_fatal ("w32_write_console failed: %s", strerror (errno));
236   n = wcslen (wstring);
237
238   if (!WriteConsoleW (con.out, wstring, n, &nwritten, NULL))
239     {
240       static int shown;
241       if (!shown)
242         {
243           shown = 1;
244           log_info ("WriteConsole failed: %s", w32_strerror (-1));
245           log_info ("Please configure a suitable font for the console\n");
246         }
247       n = strlen (string);
248       if (!WriteConsoleA (con.out, string, n , &nwritten, NULL))
249         log_fatal ("WriteConsole fallback failed: %s", w32_strerror (-1));
250     }
251   else
252     {
253       if (n != nwritten)
254         log_fatal ("WriteConsole failed: %lu != %lu\n",
255                    (unsigned long)n, (unsigned long)nwritten);
256     }
257   last_prompt_len += n;
258   xfree (wstring);
259 }
260 #endif /*HAVE_W32_SYSTEM*/
261
262
263 void
264 tty_printf (const char *fmt, ... )
265 {
266   va_list arg_ptr;
267
268   if (no_terminal)
269     return;
270
271   if (!initialized)
272     init_ttyfp ();
273
274   va_start (arg_ptr, fmt);
275
276 #ifdef HAVE_W32_SYSTEM
277   {
278     char *buf = NULL;
279
280     vasprintf(&buf, fmt, arg_ptr);
281     if (!buf)
282       log_bug ("vasprintf() failed\n");
283     w32_write_console (buf);
284     xfree (buf);
285   }
286 #else /* Unix */
287   last_prompt_len += vfprintf (ttyfp, fmt, arg_ptr) ;
288   fflush (ttyfp);
289 #endif /* Unix */
290   va_end(arg_ptr);
291 }
292
293
294 /* Same as tty_printf but if FP is not NULL, behave like a regular
295    fprintf. */
296 void
297 tty_fprintf (estream_t fp, const char *fmt, ... )
298 {
299   va_list arg_ptr;
300
301   if (fp)
302     {
303       va_start (arg_ptr, fmt) ;
304       es_vfprintf (fp, fmt, arg_ptr );
305       va_end (arg_ptr);
306       return;
307     }
308
309   if (no_terminal)
310     return;
311
312   if (!initialized)
313     init_ttyfp ();
314
315   va_start (arg_ptr, fmt);
316
317 #ifdef HAVE_W32_SYSTEM
318   {
319     char *buf = NULL;
320
321     vasprintf (&buf, fmt, arg_ptr);
322     if (!buf)
323       log_bug ("vasprintf() failed\n");
324     w32_write_console (buf);
325     xfree (buf);
326   }
327 #else /* Unix */
328   last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
329   fflush(ttyfp);
330 #endif /* Unix */
331
332   va_end(arg_ptr);
333 }
334
335
336 /* Print a string, but filter all control characters out.  If FP is
337  * not NULL print to that stream instead to the tty.  */
338 static void
339 do_print_string (estream_t fp, const byte *p, size_t n )
340 {
341   if (no_terminal && !fp)
342     return;
343
344   if (!initialized && !fp)
345     init_ttyfp();
346
347   if (fp)
348     {
349       print_utf8_buffer (fp, p, n);
350       return;
351     }
352
353 #ifdef HAVE_W32_SYSTEM
354   /* Not so effective, change it if you want */
355   for (; n; n--, p++)
356     {
357       if (iscntrl (*p))
358         {
359           if( *p == '\n' )
360             tty_printf ("\\n");
361           else if( !*p )
362             tty_printf ("\\0");
363           else
364             tty_printf ("\\x%02x", *p);
365         }
366       else
367         tty_printf ("%c", *p);
368     }
369 #else /* Unix */
370   for (; n; n--, p++)
371     {
372       if (iscntrl (*p))
373         {
374           putc ('\\', ttyfp);
375           if ( *p == '\n' )
376             putc ('n', ttyfp);
377           else if ( !*p )
378             putc ('0', ttyfp);
379           else
380             fprintf (ttyfp, "x%02x", *p );
381         }
382       else
383         putc (*p, ttyfp);
384     }
385 #endif /* Unix */
386 }
387
388
389 void
390 tty_print_utf8_string2 (estream_t fp, const byte *p, size_t n, size_t max_n)
391 {
392   size_t i;
393   char *buf;
394
395   if (no_terminal && !fp)
396     return;
397
398   /* We can handle plain ascii simpler, so check for it first. */
399   for(i=0; i < n; i++ )
400     {
401       if (p[i] & 0x80)
402         break;
403     }
404   if (i < n)
405     {
406       buf = utf8_to_native ((const char *)p, n, 0);
407       if (max_n && (strlen (buf) > max_n))
408         buf[max_n] = 0;
409       /* (utf8_to_native already did  the control character quoting) */
410       tty_fprintf (fp, "%s", buf);
411       xfree (buf);
412     }
413   else
414     {
415       if (max_n && (n > max_n))
416         n = max_n;
417       do_print_string (fp, p, n );
418     }
419 }
420
421
422 void
423 tty_print_utf8_string (const byte *p, size_t n)
424 {
425   tty_print_utf8_string2 (NULL, p, n, 0);
426 }
427
428
429 /* Read a string from the tty using PROMPT.  If HIDDEN is set the
430  * input is not echoed.  */
431 static char *
432 do_get (const char *prompt, int hidden)
433 {
434   char *buf;
435   int  n;  /* Allocated size of BUF.  */
436   int  i;  /* Number of bytes in BUF. */
437   int  c;
438 #ifdef HAVE_W32_SYSTEM
439   char *utf8buf;
440   int errcount = 0;
441 #else
442   byte cbuf[1];
443 #endif
444
445   if (batchmode)
446     {
447       log_error (_("Sorry, we are in batchmode - can't get input\n"));
448       exit (2);
449     }
450
451   if (no_terminal)
452     {
453       log_error (_("Sorry, no terminal at all requested - can't get input\n"));
454       exit (2);
455     }
456
457   if (!initialized)
458     init_ttyfp ();
459
460   last_prompt_len = 0;
461   tty_printf ("%s", prompt);
462   buf = xmalloc ((n=50));
463   i = 0;
464
465 #ifdef HAVE_W32_SYSTEM
466   if (hidden)
467     SetConsoleMode(con.in, HID_INPMODE );
468
469   utf8buf = NULL;
470   for (;;)
471     {
472       DWORD nread;
473       wchar_t wbuf[2];
474       const unsigned char *s;
475
476       if (!ReadConsoleW (con.in, wbuf, 1, &nread, NULL))
477         log_fatal ("ReadConsole failed: %s", w32_strerror (-1));
478       if (!nread)
479         continue;
480
481       wbuf[1] = 0;
482       xfree (utf8buf);
483       utf8buf = wchar_to_utf8 (wbuf);
484       if (!utf8buf)
485         {
486           log_info ("wchar_to_utf8 failed: %s\n", strerror (errno));
487           if (++errcount > 10)
488             log_fatal (_("too many errors; giving up\n"));
489           continue;
490         }
491       if (*utf8buf == '\n')
492         {
493           if (utf8buf[1])
494             {
495               log_info ("ReadConsole returned more than requested"
496                         " (0x0a,0x%02x)\n", utf8buf[1]);
497               if (++errcount > 10)
498                 log_fatal (_("too many errors; giving up\n"));
499             }
500           break;
501         }
502       if (!hidden)
503         last_prompt_len++;
504
505       for (s=utf8buf; *s; s++)
506         {
507           c = *s;
508           if (c == '\t')
509             c = ' ';  /* Map tab to a space.  */
510           else if ((c >= 0 && c <= 0x1f) || c == 0x7f)
511             continue; /* Remove control characters.  */
512           if (!(i < n-1))
513             {
514               n += 50;
515               buf = xrealloc (buf, n);
516             }
517           buf[i++] = c;
518         }
519     }
520   xfree (utf8buf);
521
522   if (hidden)
523     SetConsoleMode(con.in, DEF_INPMODE );
524
525 #else /* Unix */
526
527   if (hidden)
528     {
529 #ifdef HAVE_TCGETATTR
530       struct termios term;
531
532       if (tcgetattr(fileno(ttyfp), &termsave))
533         log_fatal ("tcgetattr() failed: %s\n", strerror(errno));
534       restore_termios = 1;
535       term = termsave;
536       term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
537       if (tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
538         log_fatal("tcsetattr() failed: %s\n", strerror(errno));
539 #endif /*HAVE_TCGETATTR*/
540     }
541
542   /* fixme: How can we avoid that the \n is echoed w/o disabling
543    * canonical mode - w/o this kill_prompt can't work */
544   while (read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n')
545     {
546       if (!hidden)
547         last_prompt_len++;
548       c = *cbuf;
549       if (c == CONTROL_D)
550         log_info (_("Control-D detected\n"));
551
552       if (c == '\t') /* Map tab to a space.  */
553         c = ' ';
554       else if ( (c >= 0 && c <= 0x1f) || c == 0x7f)
555         continue; /* Skip all other ASCII control characters.  */
556       if (!(i < n-1))
557         {
558           n += 50;
559           buf = xrealloc (buf, n);
560         }
561       buf[i++] = c;
562     }
563   if (*cbuf != '\n')
564     {
565       buf[0] = CONTROL_D;
566       i = 1;
567     }
568
569   if (hidden)
570     {
571 #ifdef HAVE_TCGETATTR
572       if (tcsetattr (fileno(ttyfp), TCSAFLUSH, &termsave))
573         log_error ("tcsetattr() failed: %s\n", strerror(errno));
574       restore_termios = 0;
575 #endif /*HAVE_TCGETATTR*/
576     }
577 #endif /* Unix */
578
579   buf[i] = 0;
580   return buf;
581 }
582
583
584
585 /* Note: This function never returns NULL. */
586 char *
587 tty_get( const char *prompt )
588 {
589   if (!batchmode && !no_terminal && my_rl_readline && my_rl_add_history)
590     {
591       char *line;
592       char *buf;
593
594       if (!initialized)
595         init_ttyfp();
596
597       last_prompt_len = 0;
598
599       line = my_rl_readline (prompt?prompt:"");
600
601       /* We need to copy it to memory controlled by our malloc
602          implementations; further we need to convert an EOF to our
603          convention. */
604       buf = xmalloc(line? strlen(line)+1:2);
605       if (line)
606         {
607           strcpy (buf, line);
608           trim_spaces (buf);
609           if (strlen (buf) > 2 )
610             my_rl_add_history (line); /* Note that we test BUF but add LINE. */
611           free (line);
612         }
613       else
614         {
615           buf[0] = CONTROL_D;
616           buf[1] = 0;
617         }
618       return buf;
619     }
620   else
621     return do_get ( prompt, 0 );
622 }
623
624
625 /* Variable argument version of tty_get.  The prompt is actually a
626  * format string with arguments.  */
627 char *
628 tty_getf (const char *promptfmt, ... )
629 {
630   va_list arg_ptr;
631   char *prompt;
632   char *answer;
633
634   va_start (arg_ptr, promptfmt);
635   if (gpgrt_vasprintf (&prompt, promptfmt, arg_ptr) < 0)
636     log_fatal ("estream_vasprintf failed: %s\n", strerror (errno));
637   va_end (arg_ptr);
638   answer = tty_get (prompt);
639   xfree (prompt);
640   return answer;
641 }
642
643
644 char *
645 tty_get_hidden( const char *prompt )
646 {
647   return do_get (prompt, 1);
648 }
649
650
651 void
652 tty_kill_prompt (void)
653 {
654   if (no_terminal)
655     return;
656
657   if (!initialized)
658     init_ttyfp ();
659
660   if (batchmode)
661     last_prompt_len = 0;
662   if (!last_prompt_len)
663     return;
664 #ifdef HAVE_W32_SYSTEM
665   tty_printf ("\r%*s\r", last_prompt_len, "");
666 #else /* Unix */
667   {
668     int i;
669     putc ('\r', ttyfp);
670     for (i=0; i < last_prompt_len; i ++ )
671       putc (' ', ttyfp);
672     putc ('\r', ttyfp);
673     fflush (ttyfp);
674   }
675 #endif /* Unix */
676   last_prompt_len = 0;
677 }
678
679
680 int
681 tty_get_answer_is_yes( const char *prompt )
682 {
683   int yes;
684   char *p;
685
686   p = tty_get (prompt);
687   tty_kill_prompt ();
688   yes = answer_is_yes (p);
689   xfree (p);
690
691   return yes;
692 }
693
694
695 /* Called by gnupg_rl_initialize to setup the readline support. */
696 void
697 tty_private_set_rl_hooks (void (*init_stream) (FILE *),
698                           void (*set_completer) (rl_completion_func_t*),
699                           void (*inhibit_completion) (int),
700                           void (*cleanup_after_signal) (void),
701                           char *(*readline_fun) (const char*),
702                           void (*add_history_fun) (const char*),
703                           int (*rw_history_fun)(const char *, int, int))
704 {
705   my_rl_init_stream = init_stream;
706   my_rl_set_completer = set_completer;
707   my_rl_inhibit_completion = inhibit_completion;
708   my_rl_cleanup_after_signal = cleanup_after_signal;
709   my_rl_readline = readline_fun;
710   my_rl_add_history = add_history_fun;
711   my_rl_rw_history = rw_history_fun;
712 }
713
714
715 /* Read the history from FILENAME or limit the size of the history.
716  * If FILENAME is NULL and NLINES is zero the current history is
717  * cleared.  Returns 0 on success or -1 on error and sets ERRNO.  No
718  * error is return if readline support is not available.  */
719 int
720 tty_read_history (const char *filename, int nlines)
721 {
722   int rc;
723
724   if (!my_rl_rw_history)
725     return 0;
726
727   rc = my_rl_rw_history (filename, 0, nlines);
728   if (rc && gpg_err_code_from_syserror () == GPG_ERR_ENOENT)
729     rc = 0;
730
731   return rc;
732 }
733
734
735 /* Write the current history to the file FILENAME.  Returns 0 on
736  * success or -1 on error and sets ERRNO.  No error is return if
737  * readline support is not available.  */
738 int
739 tty_write_history (const char *filename)
740 {
741   if (!my_rl_rw_history)
742     return 0;
743
744   return my_rl_rw_history (filename, 1, 0);
745 }
746
747
748 #ifdef HAVE_LIBREADLINE
749 void
750 tty_enable_completion (rl_completion_func_t *completer)
751 {
752   if (no_terminal || !my_rl_set_completer )
753     return;
754
755   if (!initialized)
756     init_ttyfp();
757
758   my_rl_set_completer (completer);
759 }
760
761 void
762 tty_disable_completion (void)
763 {
764   if (no_terminal || !my_rl_inhibit_completion)
765     return;
766
767   if (!initialized)
768     init_ttyfp();
769
770   my_rl_inhibit_completion (1);
771 }
772 #endif /* HAVE_LIBREADLINE */
773
774 void
775 tty_cleanup_after_signal (void)
776 {
777 #ifdef HAVE_TCGETATTR
778   cleanup ();
779 #endif
780 }
781
782 void
783 tty_cleanup_rl_after_signal (void)
784 {
785   if (my_rl_cleanup_after_signal)
786     my_rl_cleanup_after_signal ();
787 }