1/* readpass.c: The opiereadpass() library function.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15        History:
16
17	Modified by cmetz for OPIE 2.31. Use usleep() to delay after setting
18		the terminal attributes; this might help certain buggy
19		systems.
20	Modified by cmetz for OPIE 2.3. Use TCSAFLUSH always.
21	Modified by cmetz for OPIE 2.22. Replaced echo w/ flags.
22               Really use FUNCTION.
23	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
24               Flush extraneous characters up to eol. Handle gobs of possible
25               erase and kill keys if on a terminal. To do so, use RAW
26               terminal I/O and handle echo ourselves. (should also help
27               DOS et al portability). Fixed include order. Re-did MSDOS
28	       and OS/2 includes. Set up VMIN and VTIME. Added some non-UNIX
29	       portability cruft. Limit backspacing and killing. In terminal
30               mode, eat random other control characters. Added eof handling.
31        Created at NRL for OPIE 2.2 from opiesubr.c. Change opiestrip_crlf to
32               opiestripcrlf. Don't strip to seven bits.
33*/
34#include "opie_cfg.h"
35
36#include <stdio.h>
37#include <string.h>
38#include <stdlib.h>	/* ANSI C standard library */
39
40#ifdef unix
41#include <fcntl.h>      /* POSIX file control function headers */
42#include <termios.h>    /* POSIX Terminal I/O functions */
43#if HAVE_UNISTD_H
44#include <unistd.h>     /* POSIX standard definitions */
45#endif /* HAVE_UNISTD_H */
46#include <signal.h>
47#include <setjmp.h>
48#endif /* unix */
49
50#ifdef __MSDOS__
51#include <dos.h>
52#endif /* __MSDOS__ */
53
54#ifdef __OS2__
55#define INCL_KBD
56#include <os2.h>
57#include <io.h>
58#endif /* __OS2__ */
59
60#include "opie.h"
61
62#define CONTROL(x) (x - 64)
63
64char *bsseq = "\b \b";
65
66#ifdef unix
67static jmp_buf jmpbuf;
68
69static VOIDRET catch FUNCTION((i), int i)
70{
71  longjmp(jmpbuf, 1);
72}
73#endif /* unix */
74
75char *opiereadpass FUNCTION((buf, len, flags), char *buf AND int len AND int flags)
76{
77#ifdef unix
78  struct termios attr, orig_attr;
79#endif /* unix */
80  char erase[5];
81  char kill[4];
82  char eof[4];
83
84  memset(erase, 0, sizeof(erase));
85  memset(kill, 0, sizeof(kill));
86  memset(eof, 0, sizeof(eof));
87
88  /* This section was heavily rewritten by rja following the model of code
89     samples circa page 151 of the POSIX Programmer's Guide by Donald Lewine,
90     ISBN 0-937175-73-0. That book is Copyright 1991 by O'Reilly &
91     Associates, Inc. All Rights Reserved. I recommend the book to anyone
92     trying to write portable software. rja */
93
94#ifdef unix
95  if (setjmp(jmpbuf))
96    goto error;
97
98  signal(SIGINT, catch);
99#endif /* unix */
100
101  /* Flush any pending output */
102  fflush(stderr);
103  fflush(stdout);
104
105#ifdef unix
106  /* Get original terminal attributes */
107  if (isatty(0)) {
108    if (tcgetattr(0, &orig_attr))
109      return NULL;
110
111    /* copy terminal settings into attr */
112    memcpy(&attr, &orig_attr, sizeof(struct termios));
113
114    attr.c_lflag &= ~(ECHO | ICANON);
115    attr.c_lflag |= ISIG;
116
117    attr.c_cc[VMIN] = 1;
118    attr.c_cc[VTIME] = 0;
119
120    erase[0] = CONTROL('H');
121    erase[1] = 127;
122
123#ifdef CERASE
124    {
125    char *e = erase;
126
127    while(*e)
128      if (*(e++) == CERASE)
129        break;
130
131    if (!*e)
132      *e = CERASE;
133    }
134#endif /* CERASE */
135#ifdef VERASE
136    {
137    char *e = erase;
138
139    while(*e)
140      if (*(e++) == attr.c_cc[VERASE])
141        break;
142
143    if (!*e)
144      *e = attr.c_cc[VERASE];
145    }
146#endif /* VERASE */
147
148    kill[0] = CONTROL('U');
149#ifdef CKILL
150    {
151    char *e = kill;
152
153    while(*e)
154      if (*(e++) == CKILL)
155        break;
156
157    if (!*e)
158      *e = CKILL;
159    }
160#endif /* CKILL */
161#ifdef VKILL
162    {
163    char *e = kill;
164
165    while(*e)
166      if (*(e++) == attr.c_cc[VKILL])
167        break;
168
169    if (!*e)
170      *e = attr.c_cc[VKILL];
171    }
172#endif /* VKILL */
173
174    eof[0] = CONTROL('D');
175#ifdef CEOF
176    {
177    char *e = eof;
178
179    while(*e)
180      if (*(e++) == CEOF)
181        break;
182
183    if (!*e)
184      *e = CEOF;
185    }
186#endif /* CEOF */
187#ifdef VEOF
188    {
189    char *e = eof;
190
191    while(*e)
192      if (*(e++) == attr.c_cc[VEOF])
193        break;
194
195    if (!*e)
196      *e = VEOF;
197    }
198#endif /* VEOF */
199
200#if HAVE_USLEEP
201    usleep(1);
202#endif /* HAVE_USLEEP */
203
204    if (tcsetattr(0, TCSAFLUSH, &attr))
205      goto error;
206
207#if HAVE_USLEEP
208    usleep(1);
209#endif /* HAVE_USLEEP */
210  }
211#else /* unix */
212  erase[0] = CONTROL('H');
213  erase[1] = 127;
214  kill[0] = CONTROL('U');
215  eof[0] = CONTROL('D');
216  eof[1] = CONTROL('Z');
217#endif /* unix */
218
219  {
220  char *c = buf, *end = buf + len, *e;
221#ifdef __OS2__
222  KBDKEYINFO keyInfo;
223#endif /* __OS2__ */
224
225loop:
226#ifdef unix
227  if (read(0, c, 1) != 1)
228    goto error;
229#endif /* unix */
230#ifdef MSDOS
231  *c = bdos(7, 0, 0);
232#endif /* MSDOS */
233#ifdef __OS2__
234  KbdCharIn(&keyInfo, 0, 0);
235  *c = keyInfo.chChar;
236#endif /* __OS2__ */
237
238  if ((*c == '\r') || (*c == '\n')) {
239    *c = 0;
240    goto restore;
241  }
242
243  e = eof;
244  while(*e)
245    if (*(e++) == *c)
246      goto error;
247
248  e = erase;
249  while(*e)
250    if (*(e++) == *c) {
251      if (c <= buf)
252	goto beep;
253
254      if (flags & 1)
255        write(1, bsseq, sizeof(bsseq) - 1);
256      c--;
257      goto loop;
258    }
259
260  e = kill;
261  while(*e)
262    if (*(e++) == *c) {
263      if (c <= buf)
264	goto beep;
265
266      if (flags & 1)
267        while(c-- > buf)
268          write(1, bsseq, sizeof(bsseq) - 1);
269
270      c = buf;
271      goto loop;
272    }
273
274  if (c < end) {
275    if (*c < 32)
276      goto beep;
277    if (flags & 1)
278      write(1, c, 1);
279    c++;
280  } else {
281  beep:
282    *c = CONTROL('G');
283    write(1, c, 1);
284  }
285
286  goto loop;
287  }
288
289restore:
290#ifdef unix
291  /* Restore previous tty modes */
292  if (isatty(0))
293    if (tcsetattr(0, TCSAFLUSH, &orig_attr))
294      return NULL;
295
296  signal(SIGINT, SIG_DFL);
297#endif /* unix */
298
299  /* After the secret key is taken from the keyboard, the line feed is
300     written to standard error instead of standard output.  That means that
301     anyone using the program from a terminal won't notice, but capturing
302     standard output will get the key words without a newline in front of
303     them. */
304  if (!(flags & 4)) {
305    fprintf(stderr, "\n");
306    fflush(stderr);
307  }
308
309  return buf;
310
311error:
312  *buf = 0;
313  buf = NULL;
314  goto restore;
315}
316