fix small memory leak in pinentry-curses
[pinentry.git] / pinentry / pinentry-curses.c
1 /* pinentry-curses.c - A secure curses dialog for PIN entry, library version
2    Copyright (C) 2002 g10 Code GmbH
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, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <assert.h>
25 #include <curses.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <locale.h>
31 #include <iconv.h>
32 #include <langinfo.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #ifdef HAVE_UTIME_H
40 #include <utime.h>
41 #endif /*HAVE_UTIME_H*/
42
43 #include <memory.h>
44
45 #ifdef HAVE_WCHAR_H
46 #include <wchar.h>
47 #endif /*HAVE_WCHAR_H*/
48
49 #include "pinentry.h"
50
51 /* FIXME: We should allow configuration of these button labels and in
52    any case use the default_ok, default_cancel values if available.
53    However, I have no clue about curses and localization.  */
54 #define STRING_OK "<OK>"
55 #define STRING_NOTOK "<No>"
56 #define STRING_CANCEL "<Cancel>"
57
58 #define USE_COLORS              (has_colors () && COLOR_PAIRS >= 2)
59 static short pinentry_color[] = { -1, -1, COLOR_BLACK, COLOR_RED,
60                                   COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
61                                   COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
62 static int init_screen;
63 #ifndef HAVE_DOSISH_SYSTEM
64 static int timed_out;
65 #endif
66
67 typedef enum
68   {
69     DIALOG_POS_NONE,
70     DIALOG_POS_PIN,
71     DIALOG_POS_OK,
72     DIALOG_POS_NOTOK,
73     DIALOG_POS_CANCEL
74   }
75 dialog_pos_t;
76
77 struct dialog
78 {
79   dialog_pos_t pos;
80   int pin_y;
81   int pin_x;
82   /* Width of the PIN field.  */
83   int pin_size;
84   /* Cursor location in PIN field.  */
85   int pin_loc;
86   char *pin;
87   int pin_max;
88   /* Length of PIN.  */
89   int pin_len;
90
91   int ok_y;
92   int ok_x;
93   char *ok;
94   int cancel_y;
95   int cancel_x;
96   char *cancel;
97   int notok_y;
98   int notok_x;
99   char *notok;
100 };
101 typedef struct dialog *dialog_t;
102
103 \f
104 #ifdef HAVE_NCURSESW
105 typedef wchar_t CH;
106 #define STRLEN(x) wcslen (x)
107 #define ADDCH(x) addnwstr (&x, 1);
108 #define CHWIDTH(x) wcwidth (x)
109 #define NULLCH L'\0'
110 #define NLCH L'\n'
111 #define SPCH L' '
112 #else
113 typedef char CH;
114 #define STRLEN(x) strlen (x)
115 #define ADDCH(x) addch ((unsigned char) x)
116 #define CHWIDTH(x) 1
117 #define NULLCH '\0'
118 #define NLCH '\n'
119 #define SPCH ' '
120 #endif
121
122 /* Return the next line up to MAXLEN columns wide in START and LEN.
123    The first invocation should have 0 as *LEN.  If the line ends with
124    a \n, it is a normal line that will be continued.  If it is a '\0'
125    the end of the text is reached after this line.  In all other cases
126    there is a forced line break.  A full line is returned and will be
127    continued in the next line.  */
128 static void
129 collect_line (int maxwidth, CH **start_p, int *len_p)
130 {
131   int last_space = 0;
132   int len = *len_p;
133   int width = 0;
134   CH *end;
135
136   /* Skip to next line.  */
137   *start_p += len;
138   /* Skip leading space.  */
139   while (**start_p == SPCH)
140     (*start_p)++;
141
142   end = *start_p;
143   len = 0;
144
145   while (width < maxwidth - 1 && *end != NULLCH && *end != NLCH)
146     {
147       len++;
148       end++;
149       if (*end == SPCH)
150         last_space = len;
151       width += CHWIDTH (*end);
152     }
153
154   if (*end != NULLCH && *end != NLCH && last_space != 0)
155     {
156       /* We reached the end of the available space, but still have
157          characters to go in this line.  We can break the line into
158          two parts at a space.  */
159       len = last_space;
160       (*start_p)[len] = NLCH;
161     }
162   *len_p = len + 1;
163 }
164
165 #ifdef HAVE_NCURSESW
166 static CH *
167 utf8_to_local (char *lc_ctype, char *string)
168 {
169   mbstate_t ps;
170   size_t len;
171   char *local;
172   const char *p;
173   wchar_t *wcs = NULL;
174   char *old_ctype = NULL;
175
176   local = pinentry_utf8_to_local (lc_ctype, string);
177   if (!local)
178     return NULL;
179
180   old_ctype = strdup (setlocale (LC_CTYPE, NULL));
181   setlocale (LC_CTYPE, lc_ctype? lc_ctype : "");
182
183   p = local;
184   memset (&ps, 0, sizeof(mbstate_t));
185   len = mbsrtowcs (NULL, &p, strlen (string), &ps);
186   if (len == (size_t)-1)
187     {
188       free (local);
189       goto leave;
190     }
191   wcs = calloc (len + 1, sizeof(wchar_t));
192   if (!wcs)
193     {
194       free (local);
195       goto leave;
196     }
197
198   p = local;
199   memset (&ps, 0, sizeof(mbstate_t));
200   mbsrtowcs (wcs, &p, len, &ps);
201
202   free (local);
203   
204  leave:
205   if (old_ctype)
206     {
207       setlocale (LC_CTYPE, old_ctype);
208       free (old_ctype);
209     }
210
211   return wcs;
212 }
213 #else
214 static CH *
215 utf8_to_local (const char *lc_ctype, const char *string)
216 {
217   return pinentry_utf8_to_local (lc_ctype, string);
218 }
219 #endif
220
221 static int
222 dialog_create (pinentry_t pinentry, dialog_t dialog)
223 {
224   int err = 0;
225   int size_y;
226   int size_x;
227   int y;
228   int x;
229   int ypos;
230   int xpos;
231   int description_x = 0;
232   int error_x = 0;
233   CH *description = NULL;
234   CH *error = NULL;
235   CH *prompt = NULL;
236
237 #define COPY_OUT(what)                                                  \
238   do                                                                    \
239     if (pinentry->what)                                                 \
240       {                                                                 \
241         what = utf8_to_local (pinentry->lc_ctype, pinentry->what);      \
242         if (!what)                                                      \
243           {                                                             \
244             err = 1;                                                    \
245             goto out;                                                   \
246           }                                                             \
247       }                                                                 \
248   while (0)
249
250   COPY_OUT (description);
251   COPY_OUT (error);
252   COPY_OUT (prompt);
253
254 #define MAKE_BUTTON(which,default)                                      \
255   do                                                                    \
256     {                                                                   \
257       char *new = NULL;                                                 \
258       if (pinentry->which)                                              \
259         {                                                               \
260           int len = strlen (pinentry->which);                           \
261           new = malloc (len + 3);                                       \
262           if (!new)                                                     \
263             {                                                           \
264               err = 1;                                                  \
265               goto out;                                                 \
266             }                                                           \
267           new[0] = '<';                                                 \
268           memcpy (&new[1], pinentry->which, len);                       \
269           new[len + 1] = '>';                                           \
270           new[len + 2] = '\0';                                          \
271         }                                                               \
272       dialog->which = pinentry_utf8_to_local (pinentry->lc_ctype,       \
273                                               new ? new : default);     \
274       if (!dialog->which)                                               \
275         {                                                               \
276           err = 1;                                                      \
277           goto out;                                                     \
278         }                                                               \
279     }                                                                   \
280   while (0)
281
282   MAKE_BUTTON (ok, STRING_OK);
283   if (!pinentry->one_button)
284     MAKE_BUTTON (cancel, STRING_CANCEL);
285   else
286     dialog->cancel = NULL;
287   if (!pinentry->one_button && pinentry->notok)
288     MAKE_BUTTON (notok, STRING_NOTOK);
289   else
290     dialog->notok = NULL;
291
292   getmaxyx (stdscr, size_y, size_x);
293
294   /* Check if all required lines fit on the screen.  */
295   y = 1;                /* Top frame.  */
296   if (description)
297     {
298       CH *start = description;
299       int len = 0;
300
301       do
302         {
303           collect_line (size_x - 4, &start, &len);
304           if (len > description_x)
305             description_x = len;
306           y++;
307         }
308       while (start[len - 1]);
309       y++;
310     }
311
312   if (pinentry->pin)
313     {
314       if (error)
315         {
316           CH *p = error;
317           int err_x = 0;
318
319           while (*p)
320             {
321               if (*(p++) == '\n')
322                 {
323                   if (err_x > error_x)
324                     error_x = err_x;
325                   y++;
326                   err_x = 0;
327                 }
328               else
329                 err_x++;
330             }
331           if (err_x > error_x)
332             error_x = err_x;
333           y += 2;       /* Error message.  */
334         }
335       y += 2;           /* Pin entry field.  */
336     }
337   y += 2;               /* OK/Cancel and bottom frame.  */
338
339   if (y > size_y)
340     {
341       err = 1;
342       goto out;
343     }
344
345   /* Check if all required columns fit on the screen.  */
346   x = 0;
347   if (description)
348     {
349       int new_x = description_x;
350       if (new_x > size_x - 4)
351         new_x = size_x - 4;
352       if (new_x > x)
353         x = new_x;
354     }
355   if (pinentry->pin)
356     {
357 #define MIN_PINENTRY_LENGTH 40
358       int new_x;
359
360       if (error)
361         {
362           new_x = error_x;
363           if (new_x > size_x - 4)
364             new_x = size_x - 4;
365           if (new_x > x)
366             x = new_x;
367         }
368
369       new_x = MIN_PINENTRY_LENGTH;
370       if (prompt)
371         {
372           new_x += STRLEN (prompt) + 1; /* One space after prompt.  */
373         }
374       if (new_x > size_x - 4)
375         new_x = size_x - 4;
376       if (new_x > x)
377         x = new_x;
378     }
379   /* We position the buttons after the first, second and third fourth
380      of the width.  Account for rounding.  */
381   if (x < 3 * strlen (dialog->ok))
382     x = 3 * strlen (dialog->ok);
383   if (dialog->cancel)
384     if (x < 3 * strlen (dialog->cancel))
385       x = 3 * strlen (dialog->cancel);
386   if (dialog->notok)
387     if (x < 3 * strlen (dialog->notok))
388       x = 3 * strlen (dialog->notok);
389
390   /* Add the frame.  */
391   x += 4;
392
393   if (x > size_x)
394     {
395       err = 1;
396       goto out;
397     }
398
399   dialog->pos = DIALOG_POS_NONE;
400   dialog->pin = pinentry->pin;
401   dialog->pin_max = pinentry->pin_len;
402   dialog->pin_loc = 0;
403   dialog->pin_len = 0;
404   ypos = (size_y - y) / 2;
405   xpos = (size_x - x) / 2;
406   move (ypos, xpos);
407   addch (ACS_ULCORNER);
408   hline (0, x - 2);
409   move (ypos, xpos + x - 1);
410   addch (ACS_URCORNER);
411   move (ypos + 1, xpos + x - 1);
412   vline (0, y - 2);
413   move (ypos + y - 1, xpos);
414   addch (ACS_LLCORNER);
415   hline (0, x - 2);
416   move (ypos + y - 1, xpos + x - 1);
417   addch (ACS_LRCORNER);
418   ypos++;
419   if (description)
420     {
421       CH *start = description;
422       int len = 0;
423
424       do
425         {
426           int i;
427
428           move (ypos, xpos);
429           addch (ACS_VLINE);
430           addch (' ');
431           collect_line (size_x - 4, &start, &len);
432           for (i = 0; i < len - 1; i++)
433             {
434               ADDCH (start[i]);
435             }
436           if (start[len - 1] != NULLCH && start[len - 1] != NLCH)
437             ADDCH (start[len - 1]);
438           ypos++;
439         }
440       while (start[len - 1]);
441       move (ypos, xpos);
442       addch (ACS_VLINE);
443       ypos++;
444     }
445   if (pinentry->pin)
446     {
447       int i;
448
449       if (error)
450         {
451           CH *p = error;
452           i = 0;
453
454           while (*p)
455             {
456               move (ypos, xpos);
457               addch (ACS_VLINE);
458               addch (' ');
459               if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE)
460                 {
461                   attroff (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
462                   attron (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0));
463                 }
464               else
465                 standout ();
466               for (;*p && *p != NLCH; p++)
467                 if (i < x - 4)
468                   {
469                     i++;
470                     ADDCH (*p);
471                   }
472               if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE)
473                 {
474                   attroff (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0));
475                   attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
476                 }
477               else
478                 standend ();
479               if (*p == '\n')
480                 p++;
481               i = 0;
482               ypos++;
483             }
484           move (ypos, xpos);
485           addch (ACS_VLINE);
486           ypos++;
487         }
488
489       move (ypos, xpos);
490       addch (ACS_VLINE);
491       addch (' ');
492
493       dialog->pin_y = ypos;
494       dialog->pin_x = xpos + 2;
495       dialog->pin_size = x - 4;
496       if (prompt)
497         {
498           CH *p = prompt;
499           i = STRLEN (prompt);
500           if (i > x - 4 - MIN_PINENTRY_LENGTH)
501             i = x - 4 - MIN_PINENTRY_LENGTH;
502           dialog->pin_x += i + 1;
503           dialog->pin_size -= i + 1;
504           while (i-- > 0)
505             {
506               ADDCH (*(p++));
507             }
508           addch (' ');
509         }
510       for (i = 0; i < dialog->pin_size; i++)
511         addch ('_');
512       ypos++;
513       move (ypos, xpos);
514       addch (ACS_VLINE);
515       ypos++;
516     }
517   move (ypos, xpos);
518   addch (ACS_VLINE);
519
520   if (dialog->cancel || dialog->notok)
521     {
522       dialog->ok_y = ypos;
523       /* Calculating the left edge of the left button, rounding down.  */
524       dialog->ok_x = xpos + 2 + ((x - 4) / 3 - strlen (dialog->ok)) / 2;
525       move (dialog->ok_y, dialog->ok_x);
526       addstr (dialog->ok);
527
528       if (dialog->notok)
529         {
530           dialog->notok_y = ypos;
531           /* Calculating the left edge of the middle button, rounding up.  */
532           dialog->notok_x = xpos + x / 2 - strlen (dialog->notok) / 2;
533           move (dialog->notok_y, dialog->notok_x);
534           addstr (dialog->notok);
535         }
536       if (dialog->cancel)
537         {
538           dialog->cancel_y = ypos;
539           /* Calculating the left edge of the right button, rounding up.  */
540           dialog->cancel_x = xpos + x - 2 - ((x - 4) / 3 + strlen (dialog->cancel)) / 2;
541           move (dialog->cancel_y, dialog->cancel_x);
542           addstr (dialog->cancel);
543         }
544     }
545   else
546     {
547       dialog->ok_y = ypos;
548       /* Calculating the left edge of the OK button, rounding down.  */
549       dialog->ok_x = xpos + x / 2 - strlen (dialog->ok) / 2;
550       move (dialog->ok_y, dialog->ok_x);
551       addstr (dialog->ok);
552     }
553
554  out:
555   if (description)
556     free (description);
557   if (error)
558     free (error);
559   if (prompt)
560     free (prompt);
561   return err;
562 }
563
564 \f
565 static void
566 set_cursor_state (int on)
567 {
568   static int normal_state = -1;
569   static int on_last;
570
571   if (normal_state < 0 && !on)
572     {
573       normal_state = curs_set (0);
574       on_last = on;
575     }
576   else if (on != on_last)
577     {
578       curs_set (on ? normal_state : 0);
579       on_last = on;
580     }
581 }
582
583 static int
584 dialog_switch_pos (dialog_t diag, dialog_pos_t new_pos)
585 {
586   if (new_pos != diag->pos)
587     {
588       switch (diag->pos)
589         {
590         case DIALOG_POS_OK:
591           move (diag->ok_y, diag->ok_x);
592           addstr (diag->ok);
593           break;
594         case DIALOG_POS_NOTOK:
595           if (diag->notok)
596             {
597               move (diag->notok_y, diag->notok_x);
598               addstr (diag->notok);
599             }
600           break;
601         case DIALOG_POS_CANCEL:
602           if (diag->cancel)
603             {
604               move (diag->cancel_y, diag->cancel_x);
605               addstr (diag->cancel);
606             }
607           break;
608         default:
609           break;
610         }
611       diag->pos = new_pos;
612       switch (diag->pos)
613         {
614         case DIALOG_POS_PIN:
615           move (diag->pin_y, diag->pin_x + diag->pin_loc);
616           set_cursor_state (1);
617           break;
618         case DIALOG_POS_OK:
619           set_cursor_state (0);
620           move (diag->ok_y, diag->ok_x);
621           standout ();
622           addstr (diag->ok);
623           standend ();
624           move (diag->ok_y, diag->ok_x);
625           break;
626         case DIALOG_POS_NOTOK:
627           if (diag->notok)
628             {
629               set_cursor_state (0);
630               move (diag->notok_y, diag->notok_x);
631               standout ();
632               addstr (diag->notok);
633               standend ();
634               move (diag->notok_y, diag->notok_x);
635             }
636           break;
637         case DIALOG_POS_CANCEL:
638           if (diag->cancel)
639             {
640               set_cursor_state (0);
641               move (diag->cancel_y, diag->cancel_x);
642               standout ();
643               addstr (diag->cancel);
644               standend ();
645               move (diag->cancel_y, diag->cancel_x);
646             }
647           break;
648         case DIALOG_POS_NONE:
649           set_cursor_state (0);
650           break;
651         }
652       refresh ();
653     }
654   return 0;
655 }
656
657 /* XXX Assume that field width is at least > 5.  */
658 static void
659 dialog_input (dialog_t diag, int chr)
660 {
661   int old_loc = diag->pin_loc;
662   assert (diag->pin);
663   assert (diag->pos == DIALOG_POS_PIN);
664
665   switch (chr)
666     {
667     case KEY_BACKSPACE:
668       if (diag->pin_len > 0)
669         {
670           diag->pin_len--;
671           diag->pin_loc--;
672           if (diag->pin_loc == 0 && diag->pin_len > 0)
673             {
674               diag->pin_loc = diag->pin_size - 5;
675               if (diag->pin_loc > diag->pin_len)
676                 diag->pin_loc = diag->pin_len;
677             }
678         }
679       break;
680
681     default:
682       if (chr > 0 && chr < 256 && diag->pin_len < diag->pin_max)
683         {
684           diag->pin[diag->pin_len] = (char) chr;
685           diag->pin_len++;
686           diag->pin_loc++;
687           if (diag->pin_loc == diag->pin_size && diag->pin_len < diag->pin_max)
688             {
689               diag->pin_loc = 5;
690               if (diag->pin_loc < diag->pin_size - (diag->pin_max + 1 - diag->pin_len))
691                 diag->pin_loc = diag->pin_size - (diag->pin_max + 1 - diag->pin_len);
692             }
693         }
694       break;
695     }
696
697   if (old_loc < diag->pin_loc)
698     {
699       move (diag->pin_y, diag->pin_x + old_loc);
700       while (old_loc++ < diag->pin_loc)
701         addch ('*');
702     }
703   else if (old_loc > diag->pin_loc)
704     {
705       move (diag->pin_y, diag->pin_x + diag->pin_loc);
706       while (old_loc-- > diag->pin_loc)
707         addch ('_');
708     }
709   move (diag->pin_y, diag->pin_x + diag->pin_loc);
710 }
711
712 static int
713 dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
714 {
715   struct dialog diag;
716   FILE *ttyfi = NULL;
717   FILE *ttyfo = NULL;
718   SCREEN *screen = 0;
719   int done = 0;
720   char *pin_utf8;
721 #ifndef HAVE_DOSISH_SYSTEM
722   int no_input = 1;
723 #endif
724 #ifdef HAVE_NCURSESW
725   char *old_ctype = NULL;
726
727   if (pinentry->lc_ctype)
728     {
729       old_ctype = strdup (setlocale (LC_CTYPE, NULL));
730       setlocale (LC_CTYPE, pinentry->lc_ctype);
731     }
732   else
733     setlocale (LC_CTYPE, "");
734 #endif
735
736   /* Open the desired terminal if necessary.  */
737   if (tty_name)
738     {
739       ttyfi = fopen (tty_name, "r");
740       if (!ttyfi)
741         return -1;
742       ttyfo = fopen (tty_name, "w");
743       if (!ttyfo)
744         {
745           int err = errno;
746           fclose (ttyfi);
747           errno = err;
748           return -1;
749         }
750       screen = newterm (tty_type, ttyfo, ttyfi);
751       set_term (screen);
752     }
753   else
754     {
755       if (!init_screen)
756         {
757           if (!(isatty(fileno(stdin)) && isatty(fileno(stdout))))
758             {
759               errno = ENOTTY;
760               return -1;
761             }
762           init_screen = 1;
763           initscr ();
764         }
765       else
766         clear ();
767     }
768
769   keypad (stdscr, TRUE); /* Enable keyboard mapping.  */
770   nonl ();              /* Tell curses not to do NL->CR/NL on output.  */
771   cbreak ();            /* Take input chars one at a time, no wait for \n.  */
772   noecho ();            /* Don't echo input - in color.  */
773
774   if (has_colors ())
775     {
776       start_color ();
777 #ifdef NCURSES_VERSION
778       use_default_colors ();
779 #endif
780
781       if (pinentry->color_so == PINENTRY_COLOR_DEFAULT)
782         {
783           pinentry->color_so = PINENTRY_COLOR_RED;
784           pinentry->color_so_bright = 1;
785         }
786       if (COLOR_PAIRS >= 2)
787         {
788           init_pair (1, pinentry_color[pinentry->color_fg],
789                      pinentry_color[pinentry->color_bg]);
790           init_pair (2, pinentry_color[pinentry->color_so],
791                      pinentry_color[pinentry->color_bg]);
792
793           bkgd (COLOR_PAIR (1));
794           attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
795         }
796     }
797   refresh ();
798
799   /* XXX */
800   if (dialog_create (pinentry, &diag))
801     return -2;
802   dialog_switch_pos (&diag, diag.pin ? DIALOG_POS_PIN : DIALOG_POS_OK);
803
804 #ifndef HAVE_DOSISH_SYSTEM
805   wtimeout (stdscr, 70);
806 #endif
807
808   do
809     {
810       int c;
811
812       c = wgetch (stdscr);     /* Refresh, accept single keystroke of input.  */
813 #ifndef HAVE_DOSISH_SYSTEM
814       if (timed_out && no_input)
815         {
816           done = -2;
817           break;
818         }
819 #endif
820
821       switch (c)
822         {
823 #ifndef HAVE_DOSISH_SYSTEM
824         case ERR:
825           continue;
826 #endif
827         case KEY_LEFT:
828         case KEY_UP:
829           switch (diag.pos)
830             {
831             case DIALOG_POS_OK:
832               if (diag.pin)
833                 dialog_switch_pos (&diag, DIALOG_POS_PIN);
834               break;
835             case DIALOG_POS_NOTOK:
836               dialog_switch_pos (&diag, DIALOG_POS_OK);
837               break;
838             case DIALOG_POS_CANCEL:
839               if (diag.notok)
840                 dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
841               else
842                 dialog_switch_pos (&diag, DIALOG_POS_OK);
843               break;
844             default:
845               break;
846             }
847           break;
848
849         case KEY_RIGHT:
850         case KEY_DOWN:
851           switch (diag.pos)
852             {
853             case DIALOG_POS_PIN:
854               dialog_switch_pos (&diag, DIALOG_POS_OK);
855               break;
856             case DIALOG_POS_OK:
857               if (diag.notok)
858                 dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
859               else
860                 dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
861               break;
862             case DIALOG_POS_NOTOK:
863               dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
864               break;
865             default:
866               break;
867             }
868           break;
869
870         case '\t':
871           switch (diag.pos)
872             {
873             case DIALOG_POS_PIN:
874               dialog_switch_pos (&diag, DIALOG_POS_OK);
875               break;
876             case DIALOG_POS_OK:
877               if (diag.notok)
878                 dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
879               else
880                 dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
881               break;
882             case DIALOG_POS_NOTOK:
883               dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
884               break;
885             case DIALOG_POS_CANCEL:
886               if (diag.pin)
887                 dialog_switch_pos (&diag, DIALOG_POS_PIN);
888               else
889                 dialog_switch_pos (&diag, DIALOG_POS_OK);
890               break;
891             default:
892               break;
893             }
894           break;
895
896         case '\005':
897           done = -2;
898           break;
899
900         case '\r':
901           switch (diag.pos)
902             {
903             case DIALOG_POS_PIN:
904             case DIALOG_POS_OK:
905               done = 1;
906               break;
907             case DIALOG_POS_NOTOK:
908               done = -1;
909               break;
910             case DIALOG_POS_CANCEL:
911               done = -2;
912               break;
913             case DIALOG_POS_NONE:
914               break;
915             }
916           break;
917
918         default:
919           if (diag.pos == DIALOG_POS_PIN)
920             dialog_input (&diag, c);
921         }
922 #ifndef HAVE_DOSISH_SYSTEM
923       no_input = 0;
924 #endif
925     }
926   while (!done);
927
928   set_cursor_state (1);
929   endwin ();
930   if (screen)
931     delscreen (screen);
932
933 #ifdef HAVE_NCURSESW
934   if (old_ctype)
935     {
936       setlocale (LC_CTYPE, old_ctype);
937       free (old_ctype);
938     }
939 #endif
940   if (ttyfi)
941     fclose (ttyfi);
942   if (ttyfo)
943     fclose (ttyfo);
944   /* XXX Factor out into dialog_release or something.  */
945   free (diag.ok);
946   if (diag.cancel)
947     free (diag.cancel);
948   if (diag.notok)
949     free (diag.notok);
950
951   if (pinentry->pin)
952     {
953       pinentry->locale_err = 1;
954       pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
955       if (pin_utf8)
956         {
957           pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1);
958           if (pinentry->pin)
959             strcpy (pinentry->pin, pin_utf8);
960           secmem_free (pin_utf8);
961           pinentry->locale_err = 0;
962         }
963     }
964
965   if (done == -2)
966     pinentry->canceled = 1;
967
968   return diag.pin ? (done < 0 ? -1 : diag.pin_len) : (done < 0 ? 0 : 1);
969 }
970
971
972 /* If a touch has been registered, touch that file.  */
973 static void
974 do_touch_file (pinentry_t pinentry)
975 {
976 #ifdef HAVE_UTIME_H
977   struct stat st;
978   time_t tim;
979
980   if (!pinentry->touch_file || !*pinentry->touch_file)
981     return;
982
983   if (stat (pinentry->touch_file, &st))
984     return; /* Oops.  */
985
986   /* Make sure that we actually update the mtime.  */
987   while ( (tim = time (NULL)) == st.st_mtime )
988     sleep (1);
989
990   /* Update but ignore errors as we can't do anything in that case.
991      Printing error messages may even clubber the display further. */
992   utime (pinentry->touch_file, NULL);
993 #endif /*HAVE_UTIME_H*/
994 }
995
996 #ifndef HAVE_DOSISH_SYSTEM
997 static void
998 catchsig (int sig)
999 {
1000   if (sig == SIGALRM)
1001     timed_out = 1;
1002 }
1003 #endif
1004
1005 int
1006 curses_cmd_handler (pinentry_t pinentry)
1007 {
1008   int rc;
1009
1010 #ifndef HAVE_DOSISH_SYSTEM
1011   timed_out = 0;
1012
1013   if (pinentry->timeout)
1014     {
1015       struct sigaction sa;
1016
1017       memset (&sa, 0, sizeof(sa));
1018       sa.sa_handler = catchsig;
1019       sigaction (SIGALRM, &sa, NULL);
1020       alarm (pinentry->timeout);
1021     }
1022 #endif
1023
1024   rc = dialog_run (pinentry, pinentry->ttyname, pinentry->ttytype);
1025   do_touch_file (pinentry);
1026   return rc;
1027 }