#define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
#define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
+static int list = 0;
static int verbose = 0;
static int recursive = 0;
static int preserve = 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 void tell_char(FILE * stream, char c);
static void tell_str(FILE * stream, char *str);
static void tell_user(FILE * stream, char *fmt, ...);
-static void send_char_msg(unsigned int msg_id, char c);
-static void send_str_msg(unsigned int msg_id, char *str);
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)
{
}
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"
"%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"
"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"
- "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 (ret == 0) /* success - key matched OK */
return;
+
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 (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);
- else {
- fprintf(stderr, abandoned);
- exit(0);
- }
+ } else {
+ fprintf(stderr, abandoned);
+ exit(0);
+ }
+}
+
+/*
+ * Ask whether the selected cipher is acceptable (since it was
+ * below the configured 'warn' threshold).
+ * cs: 0 = both ways, 1 = client->server, 2 = server->client
+ */
+void askcipher(char *ciphername, int cs)
+{
+ HANDLE hin;
+ DWORD savemode, i;
+
+ static const char msg[] =
+ "The first %scipher supported by the server is\n"
+ "%s, which is below the configured warning threshold.\n"
+ "Continue with connection? (y/n) ";
+ static const char abandoned[] = "Connection abandoned.\n";
+
+ char line[32];
+
+ fprintf(stderr, msg,
+ (cs == 0) ? "" :
+ (cs == 1) ? "client-to-server " :
+ "server-to-client ",
+ ciphername);
+ 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] == 'y' || line[0] == 'Y') {
+ return;
+ } else {
+ fprintf(stderr, abandoned);
+ exit(0);
}
}
char str[0x100]; /* Make the size big enough */
va_list ap;
va_start(ap, fmt);
- strcpy(str, "Fatal:");
+ strcpy(str, "Fatal: ");
vsprintf(str + strlen(str), fmt, ap);
va_end(ap);
strcat(str, "\n");
tell_str(stderr, str);
+ errs++;
+
+ if (gui_mode) {
+ unsigned int msg_id = WM_RET_ERR_CNT;
+ if (list)
+ msg_id = WM_LS_RET_ERR_CNT;
+ while (!PostMessage
+ ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
+ 0 /*lParam */ ))SleepEx(1000, TRUE);
+ }
exit(1);
}
char str[0x100]; /* Make the size big enough */
va_list ap;
va_start(ap, fmt);
- strcpy(str, "Fatal:");
+ strcpy(str, "Fatal: ");
vsprintf(str + strlen(str), fmt, ap);
va_end(ap);
strcat(str, "\n");
tell_str(stderr, str);
+ errs++;
+
+ if (gui_mode) {
+ unsigned int msg_id = WM_RET_ERR_CNT;
+ if (list)
+ msg_id = WM_LS_RET_ERR_CNT;
+ while (!PostMessage
+ ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
+ 0 /*lParam */ ))SleepEx(1000, TRUE);
+ }
exit(1);
}
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;
*/
if (is_stderr) {
fwrite(data, 1, len, stderr);
- return;
+ return 0;
}
inbuf_head = 0;
* If this is before the real session begins, just return.
*/
if (!outptr)
- return;
+ return 0;
if (outlen > 0) {
unsigned used = outlen;
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)
{
}
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 */
- select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
}
return len;
char str[0x100]; /* Make the size big enough */
va_list ap;
va_start(ap, fmt);
- strcpy(str, "Fatal:");
+ strcpy(str, "Fatal: ");
vsprintf(str + strlen(str), fmt, ap);
va_end(ap);
strcat(str, "\n");
tell_str(stderr, str);
+ errs++;
if (back != NULL && back->socket() != NULL) {
char ch;
back->special(TS_EOF);
ssh_scp_recv(&ch, 1);
}
+
+ if (gui_mode) {
+ unsigned int msg_id = WM_RET_ERR_CNT;
+ if (list)
+ msg_id = WM_LS_RET_ERR_CNT;
+ while (!PostMessage
+ ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
+ 0 /*lParam */ ))SleepEx(1000, TRUE);
+ }
+
exit(1);
}
unsigned long eta;
char etastr[10];
int pct;
+ int len;
/* GUI Adaptation - Sept 2000 */
if (gui_mode)
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");
}
/*
+ * Return a pointer to the portion of str that comes after the last
+ * slash or backslash.
+ */
+static char *stripslashes(char *str)
+{
+ char *p;
+
+ p = strrchr(str, '/');
+ if (p) str = p+1;
+
+ p = strrchr(str, '\\');
+ if (p) str = p+1;
+
+ return str;
+}
+
+/*
* Wait for a response from the other side.
* Return 0 if ok, -1 if error.
*/
}
}
-/*
+/* ----------------------------------------------------------------------
+ * Helper routines that contain the actual SCP protocol elements,
+ * so they can be switched to use SFTP.
+ */
+
+int scp_send_errmsg(char *str)
+{
+ back->send("\001", 1); /* scp protocol error prefix */
+ back->send(str, strlen(str));
+ return 0; /* can't fail */
+}
+
+int scp_send_filetimes(unsigned long mtime, unsigned long atime)
+{
+ char buf[80];
+ sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
+ back->send(buf, strlen(buf));
+ return response();
+}
+
+int scp_send_filename(char *name, unsigned long size, int modes)
+{
+ char buf[40];
+ sprintf(buf, "C%04o %lu ", modes, size);
+ back->send(buf, strlen(buf));
+ back->send(name, strlen(name));
+ back->send("\n", 1);
+ return response();
+}
+
+int scp_send_filedata(char *data, int len)
+{
+ int bufsize = back->send(data, len);
+
+ /*
+ * 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())
+ return 1;
+ bufsize = back->sendbuffer();
+ }
+
+ return 0;
+}
+
+int scp_send_finish(void)
+{
+ back->send("", 1);
+ return response();
+}
+
+int scp_send_dirname(char *name, int modes)
+{
+ char buf[40];
+ sprintf(buf, "D%04o 0 ", modes);
+ back->send(buf, strlen(buf));
+ back->send(name, strlen(name));
+ back->send("\n", 1);
+ return response();
+}
+
+int scp_send_enddir(void)
+{
+ back->send("E\n", 2);
+ return response();
+}
+
+int scp_sink_init(void)
+{
+ back->send("", 1);
+ return 0;
+}
+
+#define SCP_SINK_FILE 1
+#define SCP_SINK_DIR 2
+#define SCP_SINK_ENDDIR 3
+struct scp_sink_action {
+ int action; /* FILE, DIR, ENDDIR */
+ char *buf; /* will need freeing after use */
+ char *name; /* filename or dirname (not ENDDIR) */
+ int mode; /* access mode (not ENDDIR) */
+ unsigned long size; /* file size (not ENDDIR) */
+ int settime; /* 1 if atime and mtime are filled */
+ unsigned long atime, mtime; /* access times for the file */
+};
+
+int scp_get_sink_action(struct scp_sink_action *act)
+{
+ int done = 0;
+ int i, bufsize;
+ int action;
+ char ch;
+
+ act->settime = 0;
+ act->buf = NULL;
+ bufsize = 0;
+
+ while (!done) {
+ if (ssh_scp_recv(&ch, 1) <= 0)
+ return 1;
+ if (ch == '\n')
+ bump("Protocol error: Unexpected newline");
+ i = 0;
+ action = ch;
+ do {
+ if (ssh_scp_recv(&ch, 1) <= 0)
+ bump("Lost connection");
+ if (i >= bufsize) {
+ bufsize = i + 128;
+ act->buf = srealloc(act->buf, bufsize);
+ }
+ act->buf[i++] = ch;
+ } while (ch != '\n');
+ act->buf[i - 1] = '\0';
+ switch (action) {
+ case '\01': /* error */
+ tell_user(stderr, "%s\n", act->buf);
+ errs++;
+ continue; /* go round again */
+ case '\02': /* fatal error */
+ bump("%s", act->buf);
+ case 'E':
+ back->send("", 1);
+ act->action = SCP_SINK_ENDDIR;
+ return 0;
+ case 'T':
+ if (sscanf(act->buf, "%ld %*d %ld %*d",
+ &act->mtime, &act->atime) == 2) {
+ act->settime = 1;
+ back->send("", 1);
+ continue; /* go round again */
+ }
+ bump("Protocol error: Illegal time format");
+ case 'C':
+ case 'D':
+ act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
+ break;
+ default:
+ bump("Protocol error: Expected control record");
+ }
+ /*
+ * We will go round this loop only once, unless we hit
+ * `continue' above.
+ */
+ done = 1;
+ }
+
+ /*
+ * If we get here, we must have seen SCP_SINK_FILE or
+ * SCP_SINK_DIR.
+ */
+ if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
+ bump("Protocol error: Illegal file descriptor format");
+ act->name = act->buf + i;
+ return 0;
+}
+
+int scp_accept_filexfer(void)
+{
+ back->send("", 1);
+ return 0; /* can't fail */
+}
+
+int scp_recv_filedata(char *data, int len)
+{
+ return ssh_scp_recv(data, len);
+}
+
+int scp_finish_filerecv(void)
+{
+ back->send("", 1);
+ return response();
+}
+
+/* ----------------------------------------------------------------------
* Send an error message to the other side and to the screen.
* Increment error counter.
*/
strcpy(str, "scp: ");
vsprintf(str + strlen(str), fmt, ap);
strcat(str, "\n");
- back->send("\001", 1); /* scp protocol error prefix */
- back->send(str, strlen(str));
+ scp_send_errmsg(str);
tell_user(stderr, "%s", str);
va_end(ap);
}
*/
static void source(char *src)
{
- char buf[2048];
unsigned long size;
char *last;
HANDLE f;
GetFileTime(f, NULL, &actime, &wrtime);
TIME_WIN_TO_POSIX(actime, atime);
TIME_WIN_TO_POSIX(wrtime, mtime);
- sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
- back->send(buf, strlen(buf));
- if (response())
+ if (scp_send_filetimes(mtime, atime))
return;
}
size = GetFileSize(f, NULL);
- sprintf(buf, "C0644 %lu %s\n", size, last);
if (verbose)
- tell_user(stderr, "Sending file modes: %s", buf);
- back->send(buf, strlen(buf));
- if (response())
+ tell_user(stderr, "Sending file %s, size=%lu", last, size);
+ if (scp_send_filename(last, size, 0644))
return;
- if (statistics) {
- stat_bytes = 0;
- stat_starttime = time(NULL);
- stat_lasttime = 0;
- }
+ stat_bytes = 0;
+ stat_starttime = time(NULL);
+ stat_lasttime = 0;
for (i = 0; i < size; i += 4096) {
char transbuf[4096];
DWORD j, k = 4096;
+
if (i + k > size)
k = size - i;
if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
printf("\n");
bump("%s: Read error", src);
}
- back->send(transbuf, k);
+ if (scp_send_filedata(transbuf, k))
+ bump("%s: Network error occurred", src);
+
if (statistics) {
stat_bytes += k;
if (time(NULL) != stat_lasttime || i + k == size) {
stat_starttime, stat_lasttime);
}
}
+
}
CloseHandle(f);
- back->send("", 1);
- (void) response();
+ (void) scp_send_finish();
}
/*
*/
static void rsource(char *src)
{
- char buf[2048];
- char *last;
+ char *last, *findfile;
HANDLE dir;
WIN32_FIND_DATA fdat;
int ok;
/* maybe send filetime */
- sprintf(buf, "D0755 0 %s\n", last);
if (verbose)
- tell_user(stderr, "Entering directory: %s", buf);
- back->send(buf, strlen(buf));
- if (response())
+ tell_user(stderr, "Entering directory: %s", last);
+ if (scp_send_dirname(last, 0755))
return;
- sprintf(buf, "%s/*", src);
- dir = FindFirstFile(buf, &fdat);
+ findfile = dupcat(src, "/*", NULL);
+ dir = FindFirstFile(findfile, &fdat);
ok = (dir != INVALID_HANDLE_VALUE);
while (ok) {
if (strcmp(fdat.cFileName, ".") == 0 ||
strcmp(fdat.cFileName, "..") == 0) {
- } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
- run_err("%s/%s: Name too long", src, fdat.cFileName);
+ /* ignore . and .. */
} else {
- sprintf(buf, "%s/%s", src, fdat.cFileName);
- source(buf);
+ char *foundfile = dupcat(src, "/", fdat.cFileName);
+ source(foundfile);
+ sfree(foundfile);
}
ok = FindNextFile(dir, &fdat);
}
FindClose(dir);
+ sfree(findfile);
- sprintf(buf, "E\n");
- back->send(buf, strlen(buf));
- (void) response();
+ (void) scp_send_enddir();
}
/*
- * Execute the sink part of the SCP protocol.
+ * Execute the sink part of the SCP protocol.
*/
static void sink(char *targ, char *src)
{
- char buf[2048];
- char namebuf[2048];
+ char *destfname;
char ch;
int targisdir = 0;
int settime;
int exists;
DWORD attr;
HANDLE f;
- unsigned long mtime, atime;
- unsigned int mode;
- unsigned long size, i;
+ unsigned long received;
int wrerror = 0;
unsigned long stat_bytes;
time_t stat_starttime, stat_lasttime;
if (targetshouldbedirectory && !targisdir)
bump("%s: Not a directory", targ);
- back->send("", 1);
+ scp_sink_init();
while (1) {
- settime = 0;
- gottime:
- if (ssh_scp_recv(&ch, 1) <= 0)
+ struct scp_sink_action act;
+ if (scp_get_sink_action(&act))
return;
- if (ch == '\n')
- bump("Protocol error: Unexpected newline");
- i = 0;
- buf[i++] = ch;
- do {
- if (ssh_scp_recv(&ch, 1) <= 0)
- bump("Lost connection");
- buf[i++] = ch;
- } while (i < sizeof(buf) && ch != '\n');
- buf[i - 1] = '\0';
- switch (buf[0]) {
- case '\01': /* error */
- tell_user(stderr, "%s\n", buf + 1);
- errs++;
- continue;
- case '\02': /* fatal error */
- bump("%s", buf + 1);
- case 'E':
- back->send("", 1);
+
+ if (act.action == SCP_SINK_ENDDIR)
return;
- case 'T':
- if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
- settime = 1;
- back->send("", 1);
- goto gottime;
- }
- bump("Protocol error: Illegal time format");
- case 'C':
- case 'D':
- break;
- default:
- bump("Protocol error: Expected control record");
- }
- if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
- bump("Protocol error: Illegal file descriptor format");
- /* Security fix: ensure the file ends up where we asked for it. */
if (targisdir) {
- char t[2048];
- char *p;
- strcpy(t, targ);
+ /*
+ * Prevent the remote side from maliciously writing to
+ * files outside the target area by sending a filename
+ * containing `../'. In fact, it shouldn't be sending
+ * filenames with any slashes in at all; so we'll find
+ * the last slash or backslash in the filename and use
+ * only the part after that. (And warn!)
+ *
+ * In addition, we also ensure here that if we're
+ * copying a single file and the target is a directory
+ * (common usage: `pscp host:filename .') the remote
+ * can't send us a _different_ file name. We can
+ * distinguish this case because `src' will be non-NULL
+ * and the last component of that will fail to match
+ * (the last component of) the name sent.
+ */
+ char *striptarget, *stripsrc;
+
+ striptarget = stripslashes(act.name);
+ if (striptarget != act.name) {
+ tell_user(stderr, "warning: remote host sent a compound"
+ " pathname - possibly malicious! (ignored)");
+ }
+
+ /*
+ * Also check to see if the target filename is '.' or
+ * '..', or indeed '...' and so on because Windows
+ * appears to interpret those like '..'.
+ */
+ if (striptarget[strspn(striptarget, ".")] == '\0') {
+ bump("security violation: remote host attempted to write to"
+ " a '.' or '..' path!");
+ }
+
+ if (src) {
+ stripsrc = stripslashes(src);
+ if (strcmp(striptarget, stripsrc)) {
+ tell_user(stderr, "warning: remote host attempted to"
+ " write to a different filename: disallowing");
+ }
+ /* Override the name the server provided with our own. */
+ striptarget = stripsrc;
+ }
+
if (targ[0] != '\0')
- strcat(t, "/");
- p = namebuf + strlen(namebuf);
- while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
- p--;
- strcat(t, p);
- strcpy(namebuf, t);
+ destfname = dupcat(targ, "\\", striptarget, NULL);
+ else
+ destfname = dupstr(striptarget);
} else {
- strcpy(namebuf, targ);
+ /*
+ * In this branch of the if, the target area is a
+ * single file with an explicitly specified name in any
+ * case, so there's no danger.
+ */
+ destfname = dupstr(targ);
}
- attr = GetFileAttributes(namebuf);
+ attr = GetFileAttributes(destfname);
exists = (attr != (DWORD) - 1);
- if (buf[0] == 'D') {
+ if (act.action == SCP_SINK_DIR) {
if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
- run_err("%s: Not a directory", namebuf);
+ run_err("%s: Not a directory", destfname);
continue;
}
if (!exists) {
- if (!CreateDirectory(namebuf, NULL)) {
- run_err("%s: Cannot create directory", namebuf);
+ if (!CreateDirectory(destfname, NULL)) {
+ run_err("%s: Cannot create directory", destfname);
continue;
}
}
- sink(namebuf, NULL);
+ sink(destfname, NULL);
/* can we set the timestamp for directories ? */
continue;
}
- f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
+ f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (f == INVALID_HANDLE_VALUE) {
- run_err("%s: Cannot create file", namebuf);
+ run_err("%s: Cannot create file", destfname);
continue;
}
- back->send("", 1);
+ if (scp_accept_filexfer())
+ return;
- if (statistics) {
- stat_bytes = 0;
- stat_starttime = time(NULL);
- stat_lasttime = 0;
- if ((stat_name = strrchr(namebuf, '/')) == NULL)
- stat_name = namebuf;
- else
- stat_name++;
- if (strrchr(stat_name, '\\') != NULL)
- stat_name = strrchr(stat_name, '\\') + 1;
- }
+ stat_bytes = 0;
+ stat_starttime = time(NULL);
+ stat_lasttime = 0;
+ stat_name = stripslashes(destfname);
- for (i = 0; i < size; i += 4096) {
+ received = 0;
+ while (received < act.size) {
char transbuf[4096];
- DWORD j, k = 4096;
- if (i + k > size)
- k = size - i;
- if (ssh_scp_recv(transbuf, k) == 0)
+ DWORD blksize, read, written;
+ blksize = 4096;
+ if (blksize > act.size - received)
+ blksize = act.size - received;
+ read = scp_recv_filedata(transbuf, blksize);
+ if (read <= 0)
bump("Lost connection");
if (wrerror)
continue;
- if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
+ if (!WriteFile(f, transbuf, read, &written, NULL) ||
+ written != read) {
wrerror = 1;
+ /* FIXME: in sftp we can actually abort the transfer */
if (statistics)
printf("\r%-25.25s | %50s\n",
stat_name,
continue;
}
if (statistics) {
- stat_bytes += k;
- if (time(NULL) > stat_lasttime || i + k == size) {
+ stat_bytes += read;
+ if (time(NULL) > stat_lasttime ||
+ received + read == act.size) {
stat_lasttime = time(NULL);
- print_stats(stat_name, size, stat_bytes,
+ print_stats(stat_name, act.size, stat_bytes,
stat_starttime, stat_lasttime);
}
}
+ received += read;
}
- (void) response();
-
- if (settime) {
+ if (act.settime) {
FILETIME actime, wrtime;
- TIME_POSIX_TO_WIN(atime, actime);
- TIME_POSIX_TO_WIN(mtime, wrtime);
+ TIME_POSIX_TO_WIN(act.atime, actime);
+ TIME_POSIX_TO_WIN(act.mtime, wrtime);
SetFileTime(f, NULL, &actime, &wrtime);
}
CloseHandle(f);
if (wrerror) {
- run_err("%s: Write error", namebuf);
+ run_err("%s: Write error", destfname);
continue;
}
- back->send("", 1);
+ (void) scp_finish_filerecv();
+ sfree(destfname);
+ sfree(act.name);
}
}
/*
- * We will copy local files to a remote server.
+ * We will copy local files to a remote server.
*/
static void toremote(int argc, char *argv[])
{
(void) response();
for (i = 0; i < argc - 1; i++) {
+ char *srcpath, *last;
HANDLE dir;
WIN32_FIND_DATA fdat;
src = argv[i];
errs++;
continue;
}
+
+ /*
+ * Trim off the last pathname component of `src', to
+ * provide the base pathname which will be prepended to
+ * filenames returned from Find{First,Next}File.
+ */
+ srcpath = dupstr(src);
+ last = stripslashes(srcpath);
+ if (last == srcpath) {
+ last = strchr(srcpath, ':');
+ if (last)
+ last++;
+ else
+ last = srcpath;
+ }
+ *last = '\0';
+
dir = FindFirstFile(src, &fdat);
if (dir == INVALID_HANDLE_VALUE) {
run_err("%s: No such file or directory", src);
}
do {
char *last;
- char namebuf[2048];
+ char *filename;
/*
* Ensure that . and .. are never matched by wildcards,
* but only by deliberate action.
} else
continue; /* ignore this one */
}
- if (strlen(src) + strlen(fdat.cFileName) >= sizeof(namebuf)) {
- tell_user(stderr, "%s: Name too long", src);
- continue;
- }
- strcpy(namebuf, src);
- if ((last = strrchr(namebuf, '/')) == NULL)
- last = namebuf;
- else
- last++;
- if (strrchr(last, '\\') != NULL)
- last = strrchr(last, '\\') + 1;
- if (last == namebuf && strrchr(namebuf, ':') != NULL)
- last = strchr(namebuf, ':') + 1;
- strcpy(last, fdat.cFileName);
- source(namebuf);
+ filename = dupcat(srcpath, fdat.cFileName, NULL);
+ source(filename);
+ sfree(filename);
} while (FindNextFile(dir, &fdat));
FindClose(dir);
+ sfree(srcpath);
}
}
int main(int argc, char *argv[])
{
int i;
- int list = 0;
default_protocol = PROT_TELNET;