diff options
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.c | 1194 |
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; +} |