Extensive changes that _should_ fix the socket buffering problems,
[u/mdw/putty] / scp.c
diff --git a/scp.c b/scp.c
index 871d01d..b00500c 100644 (file)
--- a/scp.c
+++ b/scp.c
@@ -53,6 +53,7 @@ static int preserve = 0;
 static int targetshouldbedirectory = 0;
 static int statistics = 1;
 static int portnumber = 0;
 static int targetshouldbedirectory = 0;
 static int statistics = 1;
 static int portnumber = 0;
+static int prev_stats_len = 0;
 static char *password = NULL;
 static int errs = 0;
 /* GUI Adaptation - Sept 2000 */
 static char *password = NULL;
 static int errs = 0;
 /* GUI Adaptation - Sept 2000 */
@@ -74,6 +75,12 @@ static void tell_user(FILE * stream, char *fmt, ...);
 static void gui_update_stats(char *name, unsigned long size,
                             int percentage, unsigned long elapsed);
 
 static void gui_update_stats(char *name, unsigned long size,
                             int percentage, unsigned long elapsed);
 
+/*
+ * The maximum amount of queued data we accept before we stop and
+ * wait for the server to process some.
+ */
+#define MAX_SCP_BUFSIZE 16384
+
 void logevent(char *string)
 {
 }
 void logevent(char *string)
 {
 }
@@ -93,6 +100,8 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
                         char *keystr, char *fingerprint)
 {
     int ret;
                         char *keystr, char *fingerprint)
 {
     int ret;
+    HANDLE hin;
+    DWORD savemode, i;
 
     static const char absentmsg[] =
        "The server's host key is not cached in the registry. You\n"
 
     static const char absentmsg[] =
        "The server's host key is not cached in the registry. You\n"
@@ -102,8 +111,11 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
        "%s\n"
        "If you trust this host, enter \"y\" to add the key to\n"
        "PuTTY's cache and carry on connecting.\n"
        "%s\n"
        "If you trust this host, enter \"y\" to add the key to\n"
        "PuTTY's cache and carry on connecting.\n"
-       "If you do not trust this host, enter \"n\" to abandon the\n"
-       "connection.\n" "Continue connecting? (y/n) ";
+       "If you want to carry on connecting just once, without\n"
+       "adding the key to the cache, enter \"n\".\n"
+       "If you do not trust this host, press Return to abandon the\n"
+       "connection.\n"
+       "Store key in cache? (y/n) ";
 
     static const char wrongmsg[] =
        "WARNING - POTENTIAL SECURITY BREACH!\n"
 
     static const char wrongmsg[] =
        "WARNING - POTENTIAL SECURITY BREACH!\n"
@@ -115,9 +127,9 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
        "The new key fingerprint is:\n"
        "%s\n"
        "If you were expecting this change and trust the new key,\n"
        "The new key fingerprint is:\n"
        "%s\n"
        "If you were expecting this change and trust the new key,\n"
-       "enter Yes to update PuTTY's cache and continue connecting.\n"
+       "enter \"y\" to update PuTTY's cache and continue connecting.\n"
        "If you want to carry on connecting but without updating\n"
        "If you want to carry on connecting but without updating\n"
-       "the cache, enter No.\n"
+       "the cache, enter \"n\".\n"
        "If you want to abandon the connection completely, press\n"
        "Return to cancel. Pressing Return is the ONLY guaranteed\n"
        "safe choice.\n"
        "If you want to abandon the connection completely, press\n"
        "Return to cancel. Pressing Return is the ONLY guaranteed\n"
        "safe choice.\n"
@@ -134,28 +146,29 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
 
     if (ret == 0)                     /* success - key matched OK */
        return;
 
     if (ret == 0)                     /* success - key matched OK */
        return;
+
     if (ret == 2) {                   /* key was different */
        fprintf(stderr, wrongmsg, fingerprint);
        fflush(stderr);
     if (ret == 2) {                   /* key was different */
        fprintf(stderr, wrongmsg, fingerprint);
        fflush(stderr);
-       if (fgets(line, sizeof(line), stdin) &&
-           line[0] != '\0' && line[0] != '\n') {
-           if (line[0] == 'y' || line[0] == 'Y')
-               store_host_key(host, port, keytype, keystr);
-       } else {
-           fprintf(stderr, abandoned);
-           fflush(stderr);
-           exit(0);
-       }
     }
     if (ret == 1) {                   /* key was absent */
        fprintf(stderr, absentmsg, fingerprint);
     }
     if (ret == 1) {                   /* key was absent */
        fprintf(stderr, absentmsg, fingerprint);
-       if (fgets(line, sizeof(line), stdin) &&
-           (line[0] == 'y' || line[0] == 'Y'))
+       fflush(stderr);
+    }
+
+    hin = GetStdHandle(STD_INPUT_HANDLE);
+    GetConsoleMode(hin, &savemode);
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
+                        ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
+    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
+    SetConsoleMode(hin, savemode);
+
+    if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
+       if (line[0] == 'y' || line[0] == 'Y')
            store_host_key(host, port, keytype, keystr);
            store_host_key(host, port, keytype, keystr);
-       else {
-           fprintf(stderr, abandoned);
-           exit(0);
-       }
+    } else {
+       fprintf(stderr, abandoned);
+       exit(0);
     }
 }
 
     }
 }
 
@@ -302,7 +315,7 @@ static unsigned char *outptr;              /* where to put the data */
 static unsigned outlen;                       /* how much data required */
 static unsigned char *pending = NULL;  /* any spare data */
 static unsigned pendlen = 0, pendsize = 0;     /* length and phys. size of buffer */
 static unsigned outlen;                       /* how much data required */
 static unsigned char *pending = NULL;  /* any spare data */
 static unsigned pendlen = 0, pendsize = 0;     /* length and phys. size of buffer */
-void from_backend(int is_stderr, char *data, int datalen)
+int from_backend(int is_stderr, char *data, int datalen)
 {
     unsigned char *p = (unsigned char *) data;
     unsigned len = (unsigned) datalen;
 {
     unsigned char *p = (unsigned char *) data;
     unsigned len = (unsigned) datalen;
@@ -313,7 +326,7 @@ void from_backend(int is_stderr, char *data, int datalen)
      */
     if (is_stderr) {
        fwrite(data, 1, len, stderr);
      */
     if (is_stderr) {
        fwrite(data, 1, len, stderr);
-       return;
+       return 0;
     }
 
     inbuf_head = 0;
     }
 
     inbuf_head = 0;
@@ -322,7 +335,7 @@ void from_backend(int is_stderr, char *data, int datalen)
      * If this is before the real session begins, just return.
      */
     if (!outptr)
      * If this is before the real session begins, just return.
      */
     if (!outptr)
-       return;
+       return 0;
 
     if (outlen > 0) {
        unsigned used = outlen;
 
     if (outlen > 0) {
        unsigned used = outlen;
@@ -346,6 +359,19 @@ void from_backend(int is_stderr, char *data, int datalen)
        memcpy(pending + pendlen, p, len);
        pendlen += len;
     }
        memcpy(pending + pendlen, p, len);
        pendlen += len;
     }
+
+    return 0;
+}
+static int scp_process_network_event(void)
+{
+    fd_set readfds;
+
+    FD_ZERO(&readfds);
+    FD_SET(scp_ssh_socket, &readfds);
+    if (select(1, &readfds, NULL, NULL, NULL) < 0)
+       return 0;                      /* doom */
+    select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
+    return 1;
 }
 static int ssh_scp_recv(unsigned char *buf, int len)
 {
 }
 static int ssh_scp_recv(unsigned char *buf, int len)
 {
@@ -375,13 +401,8 @@ static int ssh_scp_recv(unsigned char *buf, int len)
     }
 
     while (outlen > 0) {
     }
 
     while (outlen > 0) {
-       fd_set readfds;
-
-       FD_ZERO(&readfds);
-       FD_SET(scp_ssh_socket, &readfds);
-       if (select(1, &readfds, NULL, NULL, NULL) < 0)
+       if (!scp_process_network_event())
            return 0;                  /* doom */
            return 0;                  /* doom */
-       select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
     }
 
     return len;
     }
 
     return len;
@@ -560,6 +581,7 @@ static void print_stats(char *name, unsigned long size, unsigned long done,
     unsigned long eta;
     char etastr[10];
     int pct;
     unsigned long eta;
     char etastr[10];
     int pct;
+    int len;
 
     /* GUI Adaptation - Sept 2000 */
     if (gui_mode)
 
     /* GUI Adaptation - Sept 2000 */
     if (gui_mode)
@@ -580,8 +602,11 @@ static void print_stats(char *name, unsigned long size, unsigned long done,
 
        pct = (int) (100.0 * (float) done / size);
 
 
        pct = (int) (100.0 * (float) done / size);
 
-       printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
-              name, done / 1024, ratebs / 1024.0, etastr, pct);
+       len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
+                    name, done / 1024, ratebs / 1024.0, etastr, pct);
+       if (len < prev_stats_len)
+           printf("%*s", prev_stats_len - len, "");
+       prev_stats_len = len;
 
        if (done == size)
            printf("\n");
 
        if (done == size)
            printf("\n");
@@ -748,6 +773,8 @@ static void source(char *src)
     for (i = 0; i < size; i += 4096) {
        char transbuf[4096];
        DWORD j, k = 4096;
     for (i = 0; i < size; i += 4096) {
        char transbuf[4096];
        DWORD j, k = 4096;
+       int bufsize;
+
        if (i + k > size)
            k = size - i;
        if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
        if (i + k > size)
            k = size - i;
        if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
@@ -755,7 +782,7 @@ static void source(char *src)
                printf("\n");
            bump("%s: Read error", src);
        }
                printf("\n");
            bump("%s: Read error", src);
        }
-       back->send(transbuf, k);
+       bufsize = back->send(transbuf, k);
        if (statistics) {
            stat_bytes += k;
            if (time(NULL) != stat_lasttime || i + k == size) {
        if (statistics) {
            stat_bytes += k;
            if (time(NULL) != stat_lasttime || i + k == size) {
@@ -764,6 +791,18 @@ static void source(char *src)
                            stat_starttime, stat_lasttime);
            }
        }
                            stat_starttime, stat_lasttime);
            }
        }
+
+       /*
+        * If the network transfer is backing up - that is, the
+        * remote site is not accepting data as fast as we can
+        * produce it - then we must loop on network events until
+        * we have space in the buffer again.
+        */
+       while (bufsize > MAX_SCP_BUFSIZE) {
+           if (!scp_process_network_event())
+               bump("%s: Network error occurred", src);
+           bufsize = back->sendbuffer();
+       }
     }
     CloseHandle(f);
 
     }
     CloseHandle(f);