Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / windows / wincons.c
index 757f753..508be3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * console.c: various interactive-prompt routines shared between
+ * wincons.c - various interactive-prompt routines shared between
  * the Windows console PuTTY tools
  */
 
@@ -33,8 +33,21 @@ void cleanup_exit(int code)
     exit(code);
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+void set_busy_status(void *frontend, int status)
+{
+}
+
+void notify_remote_exit(void *frontend)
+{
+}
+
+void timer_change_notify(unsigned long next)
+{
+}
+
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     int ret;
     HANDLE hin;
@@ -99,12 +112,12 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
+       return 1;
 
     if (ret == 2) {                   /* key was different */
        if (console_batch_mode) {
            fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+            return 0;
        }
        fprintf(stderr, wrongmsg, keytype, fingerprint);
        fflush(stderr);
@@ -112,7 +125,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (ret == 1) {                   /* key was absent */
        if (console_batch_mode) {
            fprintf(stderr, absentmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+            return 0;
        }
        fprintf(stderr, absentmsg, keytype, fingerprint);
        fflush(stderr);
@@ -128,9 +141,10 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
        if (line[0] == 'y' || line[0] == 'Y')
            store_host_key(host, port, keytype, keystr);
+        return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+        return 0;
     }
 }
 
@@ -139,21 +153,21 @@ void update_specials_menu(void *frontend)
 }
 
 /*
- * Ask whether the selected cipher is acceptable (since it was
+ * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
- * cs: 0 = both ways, 1 = client->server, 2 = server->client
  */
-void askcipher(void *frontend, char *ciphername, int cs)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     HANDLE hin;
     DWORD savemode, i;
 
     static const char msg[] =
-       "The first %scipher supported by the server is\n"
+       "The first %s supported by the server is\n"
        "%s, which is below the configured warning threshold.\n"
        "Continue with connection? (y/n) ";
     static const char msg_batch[] =
-       "The first %scipher supported by the server is\n"
+       "The first %s supported by the server is\n"
        "%s, which is below the configured warning threshold.\n"
        "Connection abandoned.\n";
     static const char abandoned[] = "Connection abandoned.\n";
@@ -161,17 +175,11 @@ void askcipher(void *frontend, char *ciphername, int cs)
     char line[32];
 
     if (console_batch_mode) {
-       fprintf(stderr, msg_batch,
-               (cs == 0) ? "" :
-               (cs == 1) ? "client-to-server " : "server-to-client ",
-               ciphername);
-       cleanup_exit(1);
+       fprintf(stderr, msg_batch, algtype, algname);
+       return 0;
     }
 
-    fprintf(stderr, msg,
-           (cs == 0) ? "" :
-           (cs == 1) ? "client-to-server " : "server-to-client ",
-           ciphername);
+    fprintf(stderr, msg, algtype, algname);
     fflush(stderr);
 
     hin = GetStdHandle(STD_INPUT_HANDLE);
@@ -182,10 +190,10 @@ void askcipher(void *frontend, char *ciphername, int cs)
     SetConsoleMode(hin, savemode);
 
     if (line[0] == 'y' || line[0] == 'Y') {
-       return;
+       return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+       return 0;
     }
 }
 
@@ -193,7 +201,8 @@ void askcipher(void *frontend, char *ciphername, int cs)
  * Ask whether to wipe a session log file before writing to it.
  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
  */
-int askappend(void *frontend, Filename filename)
+int askappend(void *frontend, Filename *filename,
+             void (*callback)(void *ctx, int result), void *ctx)
 {
     HANDLE hin;
     DWORD savemode, i;
@@ -214,11 +223,11 @@ int askappend(void *frontend, Filename filename)
     char line[32];
 
     if (console_batch_mode) {
-       fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
+       fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
        fflush(stderr);
        return 0;
     }
-    fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);
+    fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
     fflush(stderr);
 
     hin = GetStdHandle(STD_INPUT_HANDLE);
@@ -249,7 +258,7 @@ int askappend(void *frontend, Filename filename)
 void old_keyfile_warning(void)
 {
     static const char message[] =
-       "You are loading an SSH 2 private key which has an\n"
+       "You are loading an SSH-2 private key which has an\n"
        "old version of the file format. This means your key\n"
        "file is not fully tamperproof. Future versions of\n"
        "PuTTY may stop supporting this private key format,\n"
@@ -262,6 +271,22 @@ void old_keyfile_warning(void)
     fputs(message, stderr);
 }
 
+/*
+ * Display the fingerprints of the PGP Master Keys to the user.
+ */
+void pgp_fingerprints(void)
+{
+    fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
+         "be used to establish a trust path from this executable to another\n"
+         "one. See the manual for more information.\n"
+         "(Note: these fingerprints have nothing to do with SSH!)\n"
+         "\n"
+         "PuTTY Master Key (RSA), 1024-bit:\n"
+         "  " PGP_RSA_MASTER_KEY_FP "\n"
+         "PuTTY Master Key (DSA), 1024-bit:\n"
+         "  " PGP_DSA_MASTER_KEY_FP "\n", stdout);
+}
+
 void console_provide_logctx(void *logctx)
 {
     console_logctx = logctx;
@@ -269,52 +294,129 @@ void console_provide_logctx(void *logctx)
 
 void logevent(void *frontend, const char *string)
 {
-    if (console_logctx)
-       log_eventlog(console_logctx, string);
+    log_eventlog(console_logctx, string);
 }
 
-int console_get_line(const char *prompt, char *str,
-                           int maxlen, int is_pw)
+static void console_data_untrusted(HANDLE hout, const char *data, int len)
+{
+    DWORD dummy;
+    /* FIXME: control-character filtering */
+    WriteFile(hout, data, len, &dummy, NULL);
+}
+
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
 {
     HANDLE hin, hout;
-    DWORD savemode, newmode, i;
+    size_t curr_prompt;
 
-    if (console_batch_mode) {
-       if (maxlen > 0)
-           str[0] = '\0';
-       return 0;
-    } else {
+    /*
+     * Zero all the results, in case we abort half-way through.
+     */
+    {
+       int i;
+       for (i = 0; i < (int)p->n_prompts; i++)
+            prompt_set_result(p->prompts[i], "");
+    }
+
+    /*
+     * The prompts_t might contain a message to be displayed but no
+     * actual prompt. More usually, though, it will contain
+     * questions that the user needs to answer, in which case we
+     * need to ensure that we're able to get the answers.
+     */
+    if (p->n_prompts) {
+       if (console_batch_mode)
+           return 0;
        hin = GetStdHandle(STD_INPUT_HANDLE);
+       if (hin == INVALID_HANDLE_VALUE) {
+           fprintf(stderr, "Cannot get standard input handle\n");
+           cleanup_exit(1);
+       }
+    }
+
+    /*
+     * And if we have anything to print, we need standard output.
+     */
+    if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) {
        hout = GetStdHandle(STD_OUTPUT_HANDLE);
-       if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
-           fprintf(stderr, "Cannot get standard input/output handles\n");
+       if (hout == INVALID_HANDLE_VALUE) {
+           fprintf(stderr, "Cannot get standard output handle\n");
            cleanup_exit(1);
        }
+    }
+
+    /*
+     * Preamble.
+     */
+    /* We only print the `name' caption if we have to... */
+    if (p->name_reqd && p->name) {
+       size_t l = strlen(p->name);
+       console_data_untrusted(hout, p->name, l);
+       if (p->name[l-1] != '\n')
+           console_data_untrusted(hout, "\n", 1);
+    }
+    /* ...but we always print any `instruction'. */
+    if (p->instruction) {
+       size_t l = strlen(p->instruction);
+       console_data_untrusted(hout, p->instruction, l);
+       if (p->instruction[l-1] != '\n')
+           console_data_untrusted(hout, "\n", 1);
+    }
+
+    for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
+
+       DWORD savemode, newmode;
+        int len;
+       prompt_t *pr = p->prompts[curr_prompt];
 
        GetConsoleMode(hin, &savemode);
        newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
-       if (is_pw)
+       if (!pr->echo)
            newmode &= ~ENABLE_ECHO_INPUT;
        else
            newmode |= ENABLE_ECHO_INPUT;
        SetConsoleMode(hin, newmode);
 
-       WriteFile(hout, prompt, strlen(prompt), &i, NULL);
-       ReadFile(hin, str, maxlen - 1, &i, NULL);
+       console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));
+
+        len = 0;
+        while (1) {
+            DWORD ret = 0;
+            BOOL r;
+
+            prompt_ensure_result_size(pr, len * 5 / 4 + 512);
+
+            r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1,
+                         &ret, NULL);
+
+            if (!r || ret == 0) {
+                len = -1;
+                break;
+            }
+            len += ret;
+            if (pr->result[len - 1] == '\n') {
+                len--;
+                if (pr->result[len - 1] == '\r')
+                    len--;
+                break;
+            }
+        }
 
        SetConsoleMode(hin, savemode);
 
-       if ((int) i > maxlen)
-           i = maxlen - 1;
-       else
-           i = i - 2;
-       str[i] = '\0';
+       if (!pr->echo) {
+           DWORD dummy;
+           WriteFile(hout, "\r\n", 2, &dummy, NULL);
+       }
 
-       if (is_pw)
-           WriteFile(hout, "\r\n", 2, &i, NULL);
+        if (len < 0) {
+            return 0;                  /* failure due to read error */
+        }
 
-       return 1;
+       pr->result[len] = '\0';
     }
+
+    return 1; /* success */
 }
 
 void frontend_keypress(void *handle)