Get rid of getopt_long and improve --help output.
[pinentry.git] / tty / pinentry-tty.c
1 /* pinentry-curses.c - A secure curses dialog for PIN entry, library version
2    Copyright (C) 2014 Serge Voilokov
3
4    This file is part of PINENTRY.
5
6    PINENTRY is free software; you can redistribute it and/or modify it
7    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    PINENTRY is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <signal.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <termios.h>
32 #ifdef HAVE_UTIME_H
33 #include <utime.h>
34 #endif /*HAVE_UTIME_H*/
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include "pinentry.h"
39
40 #ifndef HAVE_DOSISH_SYSTEM
41 static int timed_out;
42 #endif
43
44 static struct termios n_term;
45 static struct termios o_term;
46
47 static int
48 cbreak (int fd)
49 {
50   if ((tcgetattr(fd, &o_term)) == -1)
51     return -1;
52   n_term = o_term;
53   n_term.c_lflag = n_term.c_lflag & ~(ECHO|ICANON);
54   n_term.c_cc[VMIN] = 1;
55   n_term.c_cc[VTIME]= 0;
56   if ((tcsetattr(fd, TCSAFLUSH, &n_term)) == -1)
57     return -1;
58   return 1;
59 }
60
61 static int
62 read_password (pinentry_t pinentry, const char *tty_name, const char *tty_type)
63 {
64   FILE *ttyfi = stdin;
65   FILE *ttyfo = stdout;
66   int count;
67
68   if (tty_name)
69     {
70       ttyfi = fopen (tty_name, "r");
71       if (!ttyfi)
72         return -1;
73
74       ttyfo = fopen (tty_name, "w");
75       if (!ttyfo)
76         {
77           int err = errno;
78           fclose (ttyfi);
79           errno = err;
80           return -1;
81         }
82     }
83
84   if (cbreak (fileno (ttyfi)) == -1)
85     {
86       int err = errno;
87       if (tty_name)
88         {
89           fclose (ttyfi);
90           fclose (ttyfo);
91         }
92       fprintf (stderr, "cbreak failure, exiting\n");
93       errno = err;
94       return -1;
95     }
96
97   fprintf (ttyfo, "%s\n%s:\n",
98            pinentry->description? pinentry->description:"",
99            pinentry->prompt? pinentry->prompt:"PIN? ");
100   fflush (ttyfo);
101
102   memset (pinentry->pin, 0, pinentry->pin_len);
103
104   count = 0;
105   while (count+1 < pinentry->pin_len)
106     {
107       char c = fgetc (ttyfi);
108       if (c == '\n')
109         break;
110
111       fflush (ttyfo);
112       pinentry->pin[count++] = c;
113     }
114
115   tcsetattr (fileno(ttyfi), TCSANOW, &o_term);
116   if (tty_name)
117     {
118       fclose (ttyfi);
119       fclose (ttyfo);
120     }
121   return strlen (pinentry->pin);
122 }
123
124
125 /* If a touch has been registered, touch that file.  */
126 static void
127 do_touch_file(pinentry_t pinentry)
128 {
129 #ifdef HAVE_UTIME_H
130   struct stat st;
131   time_t tim;
132
133   if (!pinentry->touch_file || !*pinentry->touch_file)
134     return;
135
136   if (stat(pinentry->touch_file, &st))
137     return; /* Oops.  */
138
139   /* Make sure that we actually update the mtime.  */
140   while ((tim = time(NULL)) == st.st_mtime)
141     sleep(1);
142
143   /* Update but ignore errors as we can't do anything in that case.
144      Printing error messages may even clubber the display further. */
145   utime (pinentry->touch_file, NULL);
146 #endif /*HAVE_UTIME_H*/
147 }
148
149 #ifndef HAVE_DOSISH_SYSTEM
150 static void
151 catchsig(int sig)
152 {
153   if (sig == SIGALRM)
154     timed_out = 1;
155 }
156 #endif
157
158 int
159 tty_cmd_handler(pinentry_t pinentry)
160 {
161   int rc;
162
163 #ifndef HAVE_DOSISH_SYSTEM
164   timed_out = 0;
165
166   if (pinentry->timeout)
167     {
168       struct sigaction sa;
169
170       memset(&sa, 0, sizeof(sa));
171       sa.sa_handler = catchsig;
172       sigaction(SIGALRM, &sa, NULL);
173       alarm(pinentry->timeout);
174     }
175 #endif
176
177   rc = read_password (pinentry, pinentry->ttyname, pinentry->ttytype);
178   do_touch_file (pinentry);
179   return rc;
180 }
181
182
183 pinentry_cmd_handler_t pinentry_cmd_handler = tty_cmd_handler;
184
185
186 int
187 main (int argc, char *argv[])
188 {
189   pinentry_init ("pinentry-tty");
190
191   /* Consumes all arguments.  */
192   pinentry_parse_opts(argc, argv);
193
194   if (pinentry_loop ())
195     return 1;
196
197   return 0;
198 }