summaryrefslogtreecommitdiffstats
path: root/debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c')
-rw-r--r--debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c b/debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c
new file mode 100644
index 00000000..89bb5b6e
--- /dev/null
+++ b/debian/pinentry-tqt/pinentry-tqt-1.1.0/pinentry/pinentry-curses.c
@@ -0,0 +1,1194 @@
+/* pinentry-curses.c - A secure curses dialog for PIN entry, library version
+ * Copyright (C) 2002, 2015 g10 Code GmbH
+ *
+ * This file is part of PINENTRY.
+ *
+ * PINENTRY is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PINENTRY is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+#include <curses.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <iconv.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif /*HAVE_UTIME_H*/
+
+#include <memory.h>
+
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /*HAVE_WCHAR_H*/
+
+#include <assuan.h>
+
+#include "pinentry.h"
+
+#if GPG_ERROR_VERSION_NUMBER < 0x011900 /* 1.25 */
+# define GPG_ERR_WINDOW_TOO_SMALL 301
+# define GPG_ERR_MISSING_ENVVAR 303
+#endif
+
+
+/* FIXME: We should allow configuration of these button labels and in
+ any case use the default_ok, default_cancel values if available.
+ However, I have no clue about curses and localization. */
+#define STRING_OK "<OK>"
+#define STRING_NOTOK "<No>"
+#define STRING_CANCEL "<Cancel>"
+
+#define USE_COLORS (has_colors () && COLOR_PAIRS >= 2)
+static short pinentry_color[] = { -1, -1, COLOR_BLACK, COLOR_RED,
+ COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
+ COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
+static int init_screen;
+#ifndef HAVE_DOSISH_SYSTEM
+static int timed_out;
+#endif
+
+typedef enum
+ {
+ DIALOG_POS_NONE,
+ DIALOG_POS_PIN,
+ DIALOG_POS_OK,
+ DIALOG_POS_NOTOK,
+ DIALOG_POS_CANCEL
+ }
+dialog_pos_t;
+
+struct dialog
+{
+ dialog_pos_t pos;
+ int pin_y;
+ int pin_x;
+ /* Width of the PIN field. */
+ int pin_size;
+ /* Cursor location in PIN field. */
+ int pin_loc;
+ int pin_max;
+ /* Length of PIN. */
+ int pin_len;
+
+ int ok_y;
+ int ok_x;
+ char *ok;
+ int cancel_y;
+ int cancel_x;
+ char *cancel;
+ int notok_y;
+ int notok_x;
+ char *notok;
+
+ pinentry_t pinentry;
+};
+typedef struct dialog *dialog_t;
+
+
+#ifdef HAVE_NCURSESW
+typedef wchar_t CH;
+#define STRLEN(x) wcslen (x)
+#define ADDCH(x) addnwstr (&x, 1);
+#define CHWIDTH(x) wcwidth (x)
+#define NULLCH L'\0'
+#define NLCH L'\n'
+#define SPCH L' '
+#else
+typedef char CH;
+#define STRLEN(x) strlen (x)
+#define ADDCH(x) addch ((unsigned char) x)
+#define CHWIDTH(x) 1
+#define NULLCH '\0'
+#define NLCH '\n'
+#define SPCH ' '
+#endif
+
+/* Return the next line up to MAXLEN columns wide in START and LEN.
+ The first invocation should have 0 as *LEN. If the line ends with
+ a \n, it is a normal line that will be continued. If it is a '\0'
+ the end of the text is reached after this line. In all other cases
+ there is a forced line break. A full line is returned and will be
+ continued in the next line. */
+static void
+collect_line (int maxwidth, CH **start_p, int *len_p)
+{
+ int last_space = 0;
+ int len = *len_p;
+ int width = 0;
+ CH *end;
+
+ /* Skip to next line. */
+ *start_p += len;
+ /* Skip leading space. */
+ while (**start_p == SPCH)
+ (*start_p)++;
+
+ end = *start_p;
+ len = 0;
+
+ while (width < maxwidth - 1 && *end != NULLCH && *end != NLCH)
+ {
+ len++;
+ end++;
+ if (*end == SPCH)
+ last_space = len;
+ width += CHWIDTH (*end);
+ }
+
+ if (*end != NULLCH && *end != NLCH && last_space != 0)
+ {
+ /* We reached the end of the available space, but still have
+ characters to go in this line. We can break the line into
+ two parts at a space. */
+ len = last_space;
+ (*start_p)[len] = NLCH;
+ }
+ *len_p = len + 1;
+}
+
+#ifdef HAVE_NCURSESW
+static CH *
+utf8_to_local (char *lc_ctype, char *string)
+{
+ mbstate_t ps;
+ size_t len;
+ char *local;
+ const char *p;
+ wchar_t *wcs = NULL;
+ char *old_ctype = NULL;
+
+ local = pinentry_utf8_to_local (lc_ctype, string);
+ if (!local)
+ return NULL;
+
+ old_ctype = strdup (setlocale (LC_CTYPE, NULL));
+ setlocale (LC_CTYPE, lc_ctype? lc_ctype : "");
+
+ p = local;
+ memset (&ps, 0, sizeof(mbstate_t));
+ len = mbsrtowcs (NULL, &p, strlen (string), &ps);
+ if (len == (size_t)-1)
+ {
+ free (local);
+ goto leave;
+ }
+ wcs = calloc (len + 1, sizeof(wchar_t));
+ if (!wcs)
+ {
+ free (local);
+ goto leave;
+ }
+
+ p = local;
+ memset (&ps, 0, sizeof(mbstate_t));
+ mbsrtowcs (wcs, &p, len, &ps);
+
+ free (local);
+
+ leave:
+ if (old_ctype)
+ {
+ setlocale (LC_CTYPE, old_ctype);
+ free (old_ctype);
+ }
+
+ return wcs;
+}
+#else
+static CH *
+utf8_to_local (const char *lc_ctype, const char *string)
+{
+ return pinentry_utf8_to_local (lc_ctype, string);
+}
+#endif
+
+static int
+dialog_create (pinentry_t pinentry, dialog_t dialog)
+{
+ int err = 0;
+ int size_y;
+ int size_x;
+ int y;
+ int x;
+ int ypos;
+ int xpos;
+ int description_x = 0;
+ int error_x = 0;
+ CH *description = NULL;
+ CH *error = NULL;
+ CH *prompt = NULL;
+
+ dialog->pinentry = pinentry;
+
+#define COPY_OUT(what) \
+ do \
+ if (pinentry->what) \
+ { \
+ what = utf8_to_local (pinentry->lc_ctype, pinentry->what); \
+ if (!what) \
+ { \
+ err = 1; \
+ pinentry->specific_err = gpg_error (GPG_ERR_LOCALE_PROBLEM); \
+ pinentry->specific_err_loc = "dialog_create_copy"; \
+ goto out; \
+ } \
+ } \
+ while (0)
+
+ COPY_OUT (description);
+ COPY_OUT (error);
+ COPY_OUT (prompt);
+
+ /* There is no pinentry->default_notok. Map it to
+ pinentry->notok. */
+#define default_notok notok
+#define MAKE_BUTTON(which,default) \
+ do \
+ { \
+ char *new = NULL; \
+ if (pinentry->default_##which || pinentry->which) \
+ { \
+ int len; \
+ char *msg; \
+ int i, j; \
+ \
+ msg = pinentry->which; \
+ if (! msg) \
+ msg = pinentry->default_##which; \
+ len = strlen (msg); \
+ \
+ new = malloc (len + 3); \
+ if (!new) \
+ { \
+ err = 1; \
+ pinentry->specific_err = gpg_error_from_syserror (); \
+ pinentry->specific_err_loc = "dialog_create_mk_button"; \
+ goto out; \
+ } \
+ \
+ new[0] = '<'; \
+ for (i = 0, j = 1; i < len; i ++, j ++) \
+ { \
+ if (msg[i] == '_') \
+ { \
+ i ++; \
+ if (msg[i] == 0) \
+ /* _ at end of string. */ \
+ break; \
+ } \
+ new[j] = msg[i]; \
+ } \
+ \
+ new[j] = '>'; \
+ new[j + 1] = 0; \
+ } \
+ dialog->which = pinentry_utf8_to_local (pinentry->lc_ctype, \
+ new ? new : default); \
+ if (!dialog->which) \
+ { \
+ err = 1; \
+ pinentry->specific_err = gpg_error (GPG_ERR_LOCALE_PROBLEM); \
+ pinentry->specific_err_loc = "dialog_create_utf8conv"; \
+ goto out; \
+ } \
+ } \
+ while (0)
+
+ MAKE_BUTTON (ok, STRING_OK);
+ if (!pinentry->one_button)
+ MAKE_BUTTON (cancel, STRING_CANCEL);
+ else
+ dialog->cancel = NULL;
+ if (!pinentry->one_button && pinentry->notok)
+ MAKE_BUTTON (notok, STRING_NOTOK);
+ else
+ dialog->notok = NULL;
+
+ getmaxyx (stdscr, size_y, size_x);
+
+ /* Check if all required lines fit on the screen. */
+ y = 1; /* Top frame. */
+ if (description)
+ {
+ CH *start = description;
+ int len = 0;
+
+ do
+ {
+ collect_line (size_x - 4, &start, &len);
+ if (len > description_x)
+ description_x = len;
+ y++;
+ }
+ while (start[len - 1]);
+ y++;
+ }
+
+ if (pinentry->pin)
+ {
+ if (error)
+ {
+ CH *p = error;
+ int err_x = 0;
+
+ while (*p)
+ {
+ if (*(p++) == '\n')
+ {
+ if (err_x > error_x)
+ error_x = err_x;
+ y++;
+ err_x = 0;
+ }
+ else
+ err_x++;
+ }
+ if (err_x > error_x)
+ error_x = err_x;
+ y += 2; /* Error message. */
+ }
+ y += 2; /* Pin entry field. */
+ }
+ y += 2; /* OK/Cancel and bottom frame. */
+
+ if (y > size_y)
+ {
+ err = 1;
+ pinentry->specific_err = gpg_error (size_y < 0? GPG_ERR_MISSING_ENVVAR
+ /* */ : GPG_ERR_WINDOW_TOO_SMALL);
+ pinentry->specific_err_loc = "dialog_create";
+ goto out;
+ }
+
+ /* Check if all required columns fit on the screen. */
+ x = 0;
+ if (description)
+ {
+ int new_x = description_x;
+ if (new_x > size_x - 4)
+ new_x = size_x - 4;
+ if (new_x > x)
+ x = new_x;
+ }
+ if (pinentry->pin)
+ {
+#define MIN_PINENTRY_LENGTH 40
+ int new_x;
+
+ if (error)
+ {
+ new_x = error_x;
+ if (new_x > size_x - 4)
+ new_x = size_x - 4;
+ if (new_x > x)
+ x = new_x;
+ }
+
+ new_x = MIN_PINENTRY_LENGTH;
+ if (prompt)
+ {
+ new_x += STRLEN (prompt) + 1; /* One space after prompt. */
+ }
+ if (new_x > size_x - 4)
+ new_x = size_x - 4;
+ if (new_x > x)
+ x = new_x;
+ }
+ /* We position the buttons after the first, second and third fourth
+ of the width. Account for rounding. */
+ if (x < 3 * strlen (dialog->ok))
+ x = 3 * strlen (dialog->ok);
+ if (dialog->cancel)
+ if (x < 3 * strlen (dialog->cancel))
+ x = 3 * strlen (dialog->cancel);
+ if (dialog->notok)
+ if (x < 3 * strlen (dialog->notok))
+ x = 3 * strlen (dialog->notok);
+
+ /* Add the frame. */
+ x += 4;
+
+ if (x > size_x)
+ {
+ err = 1;
+ pinentry->specific_err = gpg_error (size_x < 0? GPG_ERR_MISSING_ENVVAR
+ /* */ : GPG_ERR_WINDOW_TOO_SMALL);
+ pinentry->specific_err_loc = "dialog_create";
+ goto out;
+ }
+
+ dialog->pos = DIALOG_POS_NONE;
+ dialog->pin_max = pinentry->pin_len;
+ dialog->pin_loc = 0;
+ dialog->pin_len = 0;
+ ypos = (size_y - y) / 2;
+ xpos = (size_x - x) / 2;
+ move (ypos, xpos);
+ addch (ACS_ULCORNER);
+ hline (0, x - 2);
+ move (ypos, xpos + x - 1);
+ addch (ACS_URCORNER);
+ move (ypos + 1, xpos + x - 1);
+ vline (0, y - 2);
+ move (ypos + y - 1, xpos);
+ addch (ACS_LLCORNER);
+ hline (0, x - 2);
+ move (ypos + y - 1, xpos + x - 1);
+ addch (ACS_LRCORNER);
+ ypos++;
+ if (description)
+ {
+ CH *start = description;
+ int len = 0;
+
+ do
+ {
+ int i;
+
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ addch (' ');
+ collect_line (size_x - 4, &start, &len);
+ for (i = 0; i < len - 1; i++)
+ {
+ ADDCH (start[i]);
+ }
+ if (start[len - 1] != NULLCH && start[len - 1] != NLCH)
+ ADDCH (start[len - 1]);
+ ypos++;
+ }
+ while (start[len - 1]);
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ ypos++;
+ }
+ if (pinentry->pin)
+ {
+ int i;
+
+ if (error)
+ {
+ CH *p = error;
+ i = 0;
+
+ while (*p)
+ {
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ addch (' ');
+ if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE)
+ {
+ attroff (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
+ attron (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0));
+ }
+ else
+ standout ();
+ for (;*p && *p != NLCH; p++)
+ if (i < x - 4)
+ {
+ i++;
+ ADDCH (*p);
+ }
+ if (USE_COLORS && pinentry->color_so != PINENTRY_COLOR_NONE)
+ {
+ attroff (COLOR_PAIR (2) | (pinentry->color_so_bright ? A_BOLD : 0));
+ attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
+ }
+ else
+ standend ();
+ if (*p == '\n')
+ p++;
+ i = 0;
+ ypos++;
+ }
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ ypos++;
+ }
+
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ addch (' ');
+
+ dialog->pin_y = ypos;
+ dialog->pin_x = xpos + 2;
+ dialog->pin_size = x - 4;
+ if (prompt)
+ {
+ CH *p = prompt;
+ i = STRLEN (prompt);
+ if (i > x - 4 - MIN_PINENTRY_LENGTH)
+ i = x - 4 - MIN_PINENTRY_LENGTH;
+ dialog->pin_x += i + 1;
+ dialog->pin_size -= i + 1;
+ while (i-- > 0)
+ {
+ ADDCH (*(p++));
+ }
+ addch (' ');
+ }
+ for (i = 0; i < dialog->pin_size; i++)
+ addch ('_');
+ ypos++;
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+ ypos++;
+ }
+ move (ypos, xpos);
+ addch (ACS_VLINE);
+
+ if (dialog->cancel || dialog->notok)
+ {
+ dialog->ok_y = ypos;
+ /* Calculating the left edge of the left button, rounding down. */
+ dialog->ok_x = xpos + 2 + ((x - 4) / 3 - strlen (dialog->ok)) / 2;
+ move (dialog->ok_y, dialog->ok_x);
+ addstr (dialog->ok);
+
+ if (! pinentry->pin && dialog->notok)
+ {
+ dialog->notok_y = ypos;
+ /* Calculating the left edge of the middle button, rounding up. */
+ dialog->notok_x = xpos + x / 2 - strlen (dialog->notok) / 2;
+ move (dialog->notok_y, dialog->notok_x);
+ addstr (dialog->notok);
+ }
+ if (dialog->cancel)
+ {
+ dialog->cancel_y = ypos;
+ /* Calculating the left edge of the right button, rounding up. */
+ dialog->cancel_x = xpos + x - 2 - ((x - 4) / 3 + strlen (dialog->cancel)) / 2;
+ move (dialog->cancel_y, dialog->cancel_x);
+ addstr (dialog->cancel);
+ }
+ }
+ else
+ {
+ dialog->ok_y = ypos;
+ /* Calculating the left edge of the OK button, rounding down. */
+ dialog->ok_x = xpos + x / 2 - strlen (dialog->ok) / 2;
+ move (dialog->ok_y, dialog->ok_x);
+ addstr (dialog->ok);
+ }
+
+ out:
+ if (description)
+ free (description);
+ if (error)
+ free (error);
+ if (prompt)
+ free (prompt);
+ return err;
+}
+
+
+static void
+set_cursor_state (int on)
+{
+ static int normal_state = -1;
+ static int on_last;
+
+ if (normal_state < 0 && !on)
+ {
+ normal_state = curs_set (0);
+ on_last = on;
+ }
+ else if (on != on_last)
+ {
+ curs_set (on ? normal_state : 0);
+ on_last = on;
+ }
+}
+
+static int
+dialog_switch_pos (dialog_t diag, dialog_pos_t new_pos)
+{
+ if (new_pos != diag->pos)
+ {
+ switch (diag->pos)
+ {
+ case DIALOG_POS_OK:
+ move (diag->ok_y, diag->ok_x);
+ addstr (diag->ok);
+ break;
+ case DIALOG_POS_NOTOK:
+ if (diag->notok)
+ {
+ move (diag->notok_y, diag->notok_x);
+ addstr (diag->notok);
+ }
+ break;
+ case DIALOG_POS_CANCEL:
+ if (diag->cancel)
+ {
+ move (diag->cancel_y, diag->cancel_x);
+ addstr (diag->cancel);
+ }
+ break;
+ default:
+ break;
+ }
+ diag->pos = new_pos;
+ switch (diag->pos)
+ {
+ case DIALOG_POS_PIN:
+ move (diag->pin_y, diag->pin_x + diag->pin_loc);
+ set_cursor_state (1);
+ break;
+ case DIALOG_POS_OK:
+ set_cursor_state (0);
+ move (diag->ok_y, diag->ok_x);
+ standout ();
+ addstr (diag->ok);
+ standend ();
+ move (diag->ok_y, diag->ok_x);
+ break;
+ case DIALOG_POS_NOTOK:
+ if (diag->notok)
+ {
+ set_cursor_state (0);
+ move (diag->notok_y, diag->notok_x);
+ standout ();
+ addstr (diag->notok);
+ standend ();
+ move (diag->notok_y, diag->notok_x);
+ }
+ break;
+ case DIALOG_POS_CANCEL:
+ if (diag->cancel)
+ {
+ set_cursor_state (0);
+ move (diag->cancel_y, diag->cancel_x);
+ standout ();
+ addstr (diag->cancel);
+ standend ();
+ move (diag->cancel_y, diag->cancel_x);
+ }
+ break;
+ case DIALOG_POS_NONE:
+ set_cursor_state (0);
+ break;
+ }
+ refresh ();
+ }
+ return 0;
+}
+
+/* XXX Assume that field width is at least > 5. */
+static void
+dialog_input (dialog_t diag, int alt, int chr)
+{
+ int old_loc = diag->pin_loc;
+ assert (diag->pinentry->pin);
+ assert (diag->pos == DIALOG_POS_PIN);
+
+ if (alt && chr == KEY_BACKSPACE)
+ /* Remap alt-backspace to control-W. */
+ chr = 'w' - 'a' + 1;
+
+ switch (chr)
+ {
+ case KEY_BACKSPACE:
+ /* control-h. */
+ case 'h' - 'a' + 1:
+ /* ASCII DEL. What Mac OS X apparently emits when the "delete"
+ (backspace) key is pressed. */
+ case 127:
+ if (diag->pin_len > 0)
+ {
+ diag->pin_len--;
+ diag->pin_loc--;
+ if (diag->pin_loc == 0 && diag->pin_len > 0)
+ {
+ diag->pin_loc = diag->pin_size - 5;
+ if (diag->pin_loc > diag->pin_len)
+ diag->pin_loc = diag->pin_len;
+ }
+ }
+ break;
+
+ case 'l' - 'a' + 1: /* control-l */
+ /* Refresh the screen. */
+ endwin ();
+ refresh ();
+ break;
+
+ case 'u' - 'a' + 1: /* control-u */
+ /* Erase the whole line. */
+ if (diag->pin_len > 0)
+ {
+ diag->pin_len = 0;
+ diag->pin_loc = 0;
+ }
+ break;
+
+ case 'w' - 'a' + 1: /* control-w. */
+ while (diag->pin_len > 0
+ && diag->pinentry->pin[diag->pin_len - 1] == ' ')
+ {
+ diag->pin_len --;
+ diag->pin_loc --;
+ if (diag->pin_loc < 0)
+ {
+ diag->pin_loc += diag->pin_size;
+ if (diag->pin_loc > diag->pin_len)
+ diag->pin_loc = diag->pin_len;
+ }
+ }
+ while (diag->pin_len > 0
+ && diag->pinentry->pin[diag->pin_len - 1] != ' ')
+ {
+ diag->pin_len --;
+ diag->pin_loc --;
+ if (diag->pin_loc < 0)
+ {
+ diag->pin_loc += diag->pin_size;
+ if (diag->pin_loc > diag->pin_len)
+ diag->pin_loc = diag->pin_len;
+ }
+ }
+
+ break;
+
+ default:
+ if (chr > 0 && chr < 256 && diag->pin_len < diag->pin_max)
+ {
+ /* Make sure there is enough room for this character and a
+ following NUL byte. */
+ if (! pinentry_setbufferlen (diag->pinentry, diag->pin_len + 2))
+ {
+ /* Bail. Here we use a simple approach. It would be
+ better to have a pinentry_bug function. */
+ assert (!"setbufferlen failed");
+ abort ();
+ }
+
+ diag->pinentry->pin[diag->pin_len] = (char) chr;
+ diag->pin_len++;
+ diag->pin_loc++;
+ if (diag->pin_loc == diag->pin_size && diag->pin_len < diag->pin_max)
+ {
+ diag->pin_loc = 5;
+ if (diag->pin_loc < diag->pin_size - (diag->pin_max + 1 - diag->pin_len))
+ diag->pin_loc = diag->pin_size - (diag->pin_max + 1 - diag->pin_len);
+ }
+ }
+ break;
+ }
+
+ if (old_loc < diag->pin_loc)
+ {
+ move (diag->pin_y, diag->pin_x + old_loc);
+ while (old_loc++ < diag->pin_loc)
+ addch ('*');
+ }
+ else if (old_loc > diag->pin_loc)
+ {
+ move (diag->pin_y, diag->pin_x + diag->pin_loc);
+ while (old_loc-- > diag->pin_loc)
+ addch ('_');
+ }
+ move (diag->pin_y, diag->pin_x + diag->pin_loc);
+}
+
+static int
+dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
+{
+ int confirm_mode = !pinentry->pin;
+ struct dialog diag;
+ FILE *ttyfi = NULL;
+ FILE *ttyfo = NULL;
+ SCREEN *screen = 0;
+ int done = 0;
+ char *pin_utf8;
+ int alt = 0;
+#ifndef HAVE_DOSISH_SYSTEM
+ int no_input = 1;
+#endif
+
+#ifdef HAVE_NCURSESW
+ char *old_ctype = NULL;
+
+ if (pinentry->lc_ctype)
+ {
+ old_ctype = strdup (setlocale (LC_CTYPE, NULL));
+ setlocale (LC_CTYPE, pinentry->lc_ctype);
+ }
+ else
+ setlocale (LC_CTYPE, "");
+#endif
+
+ /* Open the desired terminal if necessary. */
+ if (tty_name)
+ {
+ ttyfi = fopen (tty_name, "r");
+ if (!ttyfi)
+ {
+ pinentry->specific_err = gpg_error_from_syserror ();
+ pinentry->specific_err_loc = "open_tty_for_read";
+ return confirm_mode? 0 : -1;
+ }
+ ttyfo = fopen (tty_name, "w");
+ if (!ttyfo)
+ {
+ int err = errno;
+ fclose (ttyfi);
+ errno = err;
+ pinentry->specific_err = gpg_error_from_syserror ();
+ pinentry->specific_err_loc = "open_tty_for_write";
+ return confirm_mode? 0 : -1;
+ }
+ screen = newterm (tty_type, ttyfo, ttyfi);
+ set_term (screen);
+ }
+ else
+ {
+ if (!init_screen)
+ {
+ if (!(isatty(fileno(stdin)) && isatty(fileno(stdout))))
+ {
+ errno = ENOTTY;
+ pinentry->specific_err = gpg_error_from_syserror ();
+ pinentry->specific_err_loc = "isatty";
+ return confirm_mode? 0 : -1;
+ }
+ init_screen = 1;
+ initscr ();
+ }
+ else
+ clear ();
+ }
+
+ keypad (stdscr, TRUE); /* Enable keyboard mapping. */
+ nonl (); /* Tell curses not to do NL->CR/NL on output. */
+ cbreak (); /* Take input chars one at a time, no wait for \n. */
+ noecho (); /* Don't echo input - in color. */
+
+ if (pinentry->ttyalert)
+ {
+ if (! strcmp(pinentry->ttyalert, "beep"))
+ beep ();
+ else if (! strcmp(pinentry->ttyalert, "flash"))
+ flash ();
+ }
+
+ if (has_colors ())
+ {
+ start_color ();
+#ifdef NCURSES_VERSION
+ use_default_colors ();
+#endif
+
+ if (pinentry->color_so == PINENTRY_COLOR_DEFAULT)
+ {
+ pinentry->color_so = PINENTRY_COLOR_RED;
+ pinentry->color_so_bright = 1;
+ }
+ if (COLOR_PAIRS >= 2)
+ {
+ init_pair (1, pinentry_color[pinentry->color_fg],
+ pinentry_color[pinentry->color_bg]);
+ init_pair (2, pinentry_color[pinentry->color_so],
+ pinentry_color[pinentry->color_bg]);
+
+ bkgd (COLOR_PAIR (1));
+ attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
+ }
+ }
+ refresh ();
+
+ /* Create the dialog. */
+ if (dialog_create (pinentry, &diag))
+ {
+ /* Note: pinentry->specific_err has already been set. */
+ endwin ();
+ if (screen)
+ delscreen (screen);
+
+#ifdef HAVE_NCURSESW
+ if (old_ctype)
+ {
+ setlocale (LC_CTYPE, old_ctype);
+ free (old_ctype);
+ }
+#endif
+ if (ttyfi)
+ fclose (ttyfi);
+ if (ttyfo)
+ fclose (ttyfo);
+ return -2;
+ }
+ dialog_switch_pos (&diag, confirm_mode? DIALOG_POS_OK : DIALOG_POS_PIN);
+
+#ifndef HAVE_DOSISH_SYSTEM
+ wtimeout (stdscr, 70);
+#endif
+
+ do
+ {
+ int c;
+
+ c = wgetch (stdscr); /* Refresh, accept single keystroke of input. */
+#ifndef HAVE_DOSISH_SYSTEM
+ if (timed_out && no_input)
+ {
+ done = -2;
+ pinentry->specific_err = gpg_error (GPG_ERR_TIMEOUT);
+ break;
+ }
+#endif
+
+ switch (c)
+ {
+ case ERR:
+#ifndef HAVE_DOSISH_SYSTEM
+ continue;
+#else
+ done = -2;
+ break;
+#endif
+
+ case 27: /* Alt was pressed. */
+ alt = 1;
+ /* Get the next key press. */
+ continue;
+
+ case KEY_LEFT:
+ case KEY_UP:
+ switch (diag.pos)
+ {
+ case DIALOG_POS_OK:
+ if (!confirm_mode)
+ dialog_switch_pos (&diag, DIALOG_POS_PIN);
+ break;
+ case DIALOG_POS_NOTOK:
+ dialog_switch_pos (&diag, DIALOG_POS_OK);
+ break;
+ case DIALOG_POS_CANCEL:
+ if (diag.notok)
+ dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
+ else
+ dialog_switch_pos (&diag, DIALOG_POS_OK);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ switch (diag.pos)
+ {
+ case DIALOG_POS_PIN:
+ dialog_switch_pos (&diag, DIALOG_POS_OK);
+ break;
+ case DIALOG_POS_OK:
+ if (diag.notok)
+ dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
+ else
+ dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
+ break;
+ case DIALOG_POS_NOTOK:
+ dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '\t':
+ switch (diag.pos)
+ {
+ case DIALOG_POS_PIN:
+ dialog_switch_pos (&diag, DIALOG_POS_OK);
+ break;
+ case DIALOG_POS_OK:
+ if (diag.notok)
+ dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
+ else
+ dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
+ break;
+ case DIALOG_POS_NOTOK:
+ dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
+ break;
+ case DIALOG_POS_CANCEL:
+ if (confirm_mode)
+ dialog_switch_pos (&diag, DIALOG_POS_OK);
+ else
+ dialog_switch_pos (&diag, DIALOG_POS_PIN);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '\005':
+ done = -2;
+ break;
+
+ case '\r':
+ switch (diag.pos)
+ {
+ case DIALOG_POS_PIN:
+ case DIALOG_POS_OK:
+ done = 1;
+ break;
+ case DIALOG_POS_NOTOK:
+ done = -1;
+ break;
+ case DIALOG_POS_CANCEL:
+ done = -2;
+ break;
+ case DIALOG_POS_NONE:
+ break;
+ }
+ break;
+
+ default:
+ if (diag.pos == DIALOG_POS_PIN)
+ dialog_input (&diag, alt, c);
+ }
+#ifndef HAVE_DOSISH_SYSTEM
+ no_input = 0;
+#endif
+ if (c != -1)
+ alt = 0;
+ }
+ while (!done);
+
+ if (!confirm_mode)
+ {
+ /* NUL terminate the passphrase. dialog_run makes sure there is
+ enough space for the terminating NUL byte. */
+ diag.pinentry->pin[diag.pin_len] = 0;
+ }
+
+ set_cursor_state (1);
+ endwin ();
+ if (screen)
+ delscreen (screen);
+
+#ifdef HAVE_NCURSESW
+ if (old_ctype)
+ {
+ setlocale (LC_CTYPE, old_ctype);
+ free (old_ctype);
+ }
+#endif
+ if (ttyfi)
+ fclose (ttyfi);
+ if (ttyfo)
+ fclose (ttyfo);
+ /* XXX Factor out into dialog_release or something. */
+ free (diag.ok);
+ if (diag.cancel)
+ free (diag.cancel);
+ if (diag.notok)
+ free (diag.notok);
+
+ if (!confirm_mode)
+ {
+ pinentry->locale_err = 1;
+ pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
+ if (pin_utf8)
+ {
+ pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1);
+ if (pinentry->pin)
+ strcpy (pinentry->pin, pin_utf8);
+ secmem_free (pin_utf8);
+ pinentry->locale_err = 0;
+ }
+ }
+
+ if (done == -2)
+ pinentry->canceled = 1;
+
+ /* In confirm mode return cancel instead of error. */
+ if (confirm_mode)
+ return done < 0 ? 0 : 1;
+
+ return done < 0 ? -1 : diag.pin_len;
+}
+
+
+/* If a touch has been registered, touch that file. */
+static void
+do_touch_file (pinentry_t pinentry)
+{
+#ifdef HAVE_UTIME_H
+ struct stat st;
+ time_t tim;
+
+ if (!pinentry->touch_file || !*pinentry->touch_file)
+ return;
+
+ if (stat (pinentry->touch_file, &st))
+ return; /* Oops. */
+
+ /* Make sure that we actually update the mtime. */
+ while ( (tim = time (NULL)) == st.st_mtime )
+ sleep (1);
+
+ /* Update but ignore errors as we can't do anything in that case.
+ Printing error messages may even clubber the display further. */
+ utime (pinentry->touch_file, NULL);
+#endif /*HAVE_UTIME_H*/
+}
+
+#ifndef HAVE_DOSISH_SYSTEM
+static void
+catchsig (int sig)
+{
+ if (sig == SIGALRM)
+ timed_out = 1;
+}
+#endif
+
+int
+curses_cmd_handler (pinentry_t pinentry)
+{
+ int rc;
+
+#ifndef HAVE_DOSISH_SYSTEM
+ timed_out = 0;
+
+ if (pinentry->timeout)
+ {
+ struct sigaction sa;
+
+ memset (&sa, 0, sizeof(sa));
+ sa.sa_handler = catchsig;
+ sigaction (SIGALRM, &sa, NULL);
+ alarm (pinentry->timeout);
+ }
+#endif
+
+ rc = dialog_run (pinentry, pinentry->ttyname, pinentry->ttytype);
+ do_touch_file (pinentry);
+ return rc;
+}