Trim leading whitespace off the hostname if it's there.
[u/mdw/putty] / scp.c
CommitLineData
07d9aa13 1/*
2 * scp.c - Scp (Secure Copy) client for PuTTY.
fb09bf1c 3 * Joris van Rantwijk, Simon Tatham
07d9aa13 4 *
5 * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen.
6 * They, in turn, used stuff from BSD rcp.
cc87246d 7 *
8 * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000
07d9aa13 9 */
10
07d9aa13 11#include <windows.h>
4d331a77 12#ifndef AUTO_WINSOCK
13#ifdef WINSOCK_TWO
14#include <winsock2.h>
15#else
07d9aa13 16#include <winsock.h>
4d331a77 17#endif
18#endif
07d9aa13 19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <time.h>
feb7fdfe 23#include <assert.h>
cc87246d 24/* GUI Adaptation - Sept 2000 */
25#include <winuser.h>
26#include <winbase.h>
07d9aa13 27
28#define PUTTY_DO_GLOBALS
29#include "putty.h"
8c3cd914 30#include "winstuff.h"
a9422f39 31#include "storage.h"
07d9aa13 32
07d9aa13 33#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
c51a56e2 34 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
07d9aa13 35#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
c51a56e2 36 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
07d9aa13 37
cc87246d 38/* GUI Adaptation - Sept 2000 */
39#define WM_APP_BASE 0x8000
40#define WM_STD_OUT_CHAR ( WM_APP_BASE+400 )
41#define WM_STD_ERR_CHAR ( WM_APP_BASE+401 )
42#define WM_STATS_CHAR ( WM_APP_BASE+402 )
43#define WM_STATS_SIZE ( WM_APP_BASE+403 )
44#define WM_STATS_PERCENT ( WM_APP_BASE+404 )
45#define WM_STATS_ELAPSED ( WM_APP_BASE+405 )
46#define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
47#define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
48
2bc6a386 49static int list = 0;
fb09bf1c 50static int verbose = 0;
07d9aa13 51static int recursive = 0;
52static int preserve = 0;
53static int targetshouldbedirectory = 0;
54static int statistics = 1;
b8a19193 55static int portnumber = 0;
56static char *password = NULL;
07d9aa13 57static int errs = 0;
cc87246d 58/* GUI Adaptation - Sept 2000 */
59#define NAME_STR_MAX 2048
32874aea 60static char statname[NAME_STR_MAX + 1];
cc87246d 61static unsigned long statsize = 0;
62static int statperct = 0;
90a14a09 63static unsigned long statelapsed = 0;
cc87246d 64static int gui_mode = 0;
65static char *gui_hwnd = NULL;
07d9aa13 66
67static void source(char *src);
68static void rsource(char *src);
ca2d5943 69static void sink(char *targ, char *src);
cc87246d 70/* GUI Adaptation - Sept 2000 */
32874aea 71static void tell_char(FILE * stream, char c);
72static void tell_str(FILE * stream, char *str);
73static void tell_user(FILE * stream, char *fmt, ...);
cc87246d 74static void send_char_msg(unsigned int msg_id, char c);
75static void send_str_msg(unsigned int msg_id, char *str);
90a14a09 76static void gui_update_stats(char *name, unsigned long size,
32874aea 77 int percentage, unsigned long elapsed);
07d9aa13 78
32874aea 79void logevent(char *string)
80{
81}
a9422f39 82
32874aea 83void ldisc_send(char *buf, int len)
84{
feb7fdfe 85 /*
86 * This is only here because of the calls to ldisc_send(NULL,
87 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
88 * as an ldisc. So if we get called with any real data, I want
89 * to know about it.
90 */
91 assert(len == 0);
92}
93
a9422f39 94void verify_ssh_host_key(char *host, int port, char *keytype,
32874aea 95 char *keystr, char *fingerprint)
96{
a9422f39 97 int ret;
98
99 static const char absentmsg[] =
32874aea 100 "The server's host key is not cached in the registry. You\n"
101 "have no guarantee that the server is the computer you\n"
102 "think it is.\n"
103 "The server's key fingerprint is:\n"
104 "%s\n"
105 "If you trust this host, enter \"y\" to add the key to\n"
106 "PuTTY's cache and carry on connecting.\n"
107 "If you do not trust this host, enter \"n\" to abandon the\n"
108 "connection.\n" "Continue connecting? (y/n) ";
a9422f39 109
110 static const char wrongmsg[] =
32874aea 111 "WARNING - POTENTIAL SECURITY BREACH!\n"
112 "The server's host key does not match the one PuTTY has\n"
113 "cached in the registry. This means that either the\n"
114 "server administrator has changed the host key, or you\n"
115 "have actually connected to another computer pretending\n"
116 "to be the server.\n"
117 "The new key fingerprint is:\n"
118 "%s\n"
119 "If you were expecting this change and trust the new key,\n"
120 "enter Yes to update PuTTY's cache and continue connecting.\n"
121 "If you want to carry on connecting but without updating\n"
122 "the cache, enter No.\n"
123 "If you want to abandon the connection completely, press\n"
124 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
125 "safe choice.\n"
126 "Update cached key? (y/n, Return cancels connection) ";
a9422f39 127
128 static const char abandoned[] = "Connection abandoned.\n";
129
130 char line[32];
131
132 /*
133 * Verify the key against the registry.
134 */
135 ret = verify_host_key(host, port, keytype, keystr);
136
32874aea 137 if (ret == 0) /* success - key matched OK */
138 return;
139 if (ret == 2) { /* key was different */
140 fprintf(stderr, wrongmsg, fingerprint);
b4453f49 141 fflush(stderr);
32874aea 142 if (fgets(line, sizeof(line), stdin) &&
143 line[0] != '\0' && line[0] != '\n') {
144 if (line[0] == 'y' || line[0] == 'Y')
145 store_host_key(host, port, keytype, keystr);
146 } else {
147 fprintf(stderr, abandoned);
b4453f49 148 fflush(stderr);
32874aea 149 exit(0);
150 }
a9422f39 151 }
32874aea 152 if (ret == 1) { /* key was absent */
153 fprintf(stderr, absentmsg, fingerprint);
154 if (fgets(line, sizeof(line), stdin) &&
155 (line[0] == 'y' || line[0] == 'Y'))
156 store_host_key(host, port, keytype, keystr);
157 else {
158 fprintf(stderr, abandoned);
159 exit(0);
160 }
a9422f39 161 }
162}
fb09bf1c 163
cc87246d 164/* GUI Adaptation - Sept 2000 */
75cab814 165static void send_msg(HWND h, UINT message, WPARAM wParam)
cc87246d 166{
32874aea 167 while (!PostMessage(h, message, wParam, 0))
168 SleepEx(1000, TRUE);
cc87246d 169}
170
32874aea 171static void tell_char(FILE * stream, char c)
cc87246d 172{
173 if (!gui_mode)
174 fputc(c, stream);
32874aea 175 else {
cc87246d 176 unsigned int msg_id = WM_STD_OUT_CHAR;
32874aea 177 if (stream == stderr)
178 msg_id = WM_STD_ERR_CHAR;
179 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
cc87246d 180 }
181}
182
32874aea 183static void tell_str(FILE * stream, char *str)
cc87246d 184{
185 unsigned int i;
186
32874aea 187 for (i = 0; i < strlen(str); ++i)
cc87246d 188 tell_char(stream, str[i]);
189}
190
32874aea 191static void tell_user(FILE * stream, char *fmt, ...)
cc87246d 192{
32874aea 193 char str[0x100]; /* Make the size big enough */
cc87246d 194 va_list ap;
195 va_start(ap, fmt);
196 vsprintf(str, fmt, ap);
197 va_end(ap);
198 strcat(str, "\n");
199 tell_str(stream, str);
200}
201
32874aea 202static void gui_update_stats(char *name, unsigned long size,
203 int percentage, unsigned long elapsed)
cc87246d 204{
205 unsigned int i;
206
32874aea 207 if (strcmp(name, statname) != 0) {
208 for (i = 0; i < strlen(name); ++i)
209 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
210 (WPARAM) name[i]);
211 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
212 strcpy(statname, name);
cc87246d 213 }
32874aea 214 if (statsize != size) {
215 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
cc87246d 216 statsize = size;
217 }
32874aea 218 if (statelapsed != elapsed) {
219 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
220 (WPARAM) elapsed);
cc87246d 221 statelapsed = elapsed;
222 }
32874aea 223 if (statperct != percentage) {
224 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
225 (WPARAM) percentage);
cc87246d 226 statperct = percentage;
227 }
228}
229
fb09bf1c 230/*
07d9aa13 231 * Print an error message and perform a fatal exit.
232 */
233void fatalbox(char *fmt, ...)
234{
32874aea 235 char str[0x100]; /* Make the size big enough */
c51a56e2 236 va_list ap;
237 va_start(ap, fmt);
cc87246d 238 strcpy(str, "Fatal:");
32874aea 239 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 240 va_end(ap);
cc87246d 241 strcat(str, "\n");
242 tell_str(stderr, str);
2bc6a386 243 errs++;
244
245 if (gui_mode) {
246 unsigned int msg_id = WM_RET_ERR_CNT;
247 if (list)
248 msg_id = WM_LS_RET_ERR_CNT;
249 while (!PostMessage
250 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
251 0 /*lParam */ ))SleepEx(1000, TRUE);
252 }
cc87246d 253
c51a56e2 254 exit(1);
07d9aa13 255}
8d5de777 256void connection_fatal(char *fmt, ...)
257{
32874aea 258 char str[0x100]; /* Make the size big enough */
8d5de777 259 va_list ap;
260 va_start(ap, fmt);
261 strcpy(str, "Fatal:");
32874aea 262 vsprintf(str + strlen(str), fmt, ap);
8d5de777 263 va_end(ap);
264 strcat(str, "\n");
265 tell_str(stderr, str);
2bc6a386 266 errs++;
267
268 if (gui_mode) {
269 unsigned int msg_id = WM_RET_ERR_CNT;
270 if (list)
271 msg_id = WM_LS_RET_ERR_CNT;
272 while (!PostMessage
273 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
274 0 /*lParam */ ))SleepEx(1000, TRUE);
275 }
8d5de777 276
277 exit(1);
278}
07d9aa13 279
07d9aa13 280/*
8df7a775 281 * Be told what socket we're supposed to be using.
282 */
283static SOCKET scp_ssh_socket;
32874aea 284char *do_select(SOCKET skt, int startup)
285{
8df7a775 286 if (startup)
287 scp_ssh_socket = skt;
288 else
289 scp_ssh_socket = INVALID_SOCKET;
290 return NULL;
291}
292extern int select_result(WPARAM, LPARAM);
293
294/*
3bdaf79d 295 * Receive a block of data from the SSH link. Block until all data
296 * is available.
297 *
298 * To do this, we repeatedly call the SSH protocol module, with our
fe50e814 299 * own trap in from_backend() to catch the data that comes back. We
300 * do this until we have enough data.
3bdaf79d 301 */
8df7a775 302
32874aea 303static unsigned char *outptr; /* where to put the data */
304static unsigned outlen; /* how much data required */
3bdaf79d 305static unsigned char *pending = NULL; /* any spare data */
32874aea 306static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
307void from_backend(int is_stderr, char *data, int datalen)
308{
309 unsigned char *p = (unsigned char *) data;
310 unsigned len = (unsigned) datalen;
fe50e814 311
3bdaf79d 312 /*
fe50e814 313 * stderr data is just spouted to local stderr and otherwise
314 * ignored.
3bdaf79d 315 */
fe50e814 316 if (is_stderr) {
317 fwrite(data, 1, len, stderr);
318 return;
319 }
3bdaf79d 320
321 inbuf_head = 0;
322
323 /*
324 * If this is before the real session begins, just return.
325 */
326 if (!outptr)
32874aea 327 return;
3bdaf79d 328
329 if (outlen > 0) {
32874aea 330 unsigned used = outlen;
331 if (used > len)
332 used = len;
333 memcpy(outptr, p, used);
334 outptr += used;
335 outlen -= used;
336 p += used;
337 len -= used;
3bdaf79d 338 }
339
340 if (len > 0) {
32874aea 341 if (pendsize < pendlen + len) {
342 pendsize = pendlen + len + 4096;
343 pending = (pending ? srealloc(pending, pendsize) :
344 smalloc(pendsize));
345 if (!pending)
346 fatalbox("Out of memory");
347 }
348 memcpy(pending + pendlen, p, len);
349 pendlen += len;
3bdaf79d 350 }
351}
32874aea 352static int ssh_scp_recv(unsigned char *buf, int len)
353{
3bdaf79d 354 outptr = buf;
355 outlen = len;
356
357 /*
358 * See if the pending-input block contains some of what we
359 * need.
360 */
361 if (pendlen > 0) {
32874aea 362 unsigned pendused = pendlen;
363 if (pendused > outlen)
364 pendused = outlen;
3bdaf79d 365 memcpy(outptr, pending, pendused);
32874aea 366 memmove(pending, pending + pendused, pendlen - pendused);
3bdaf79d 367 outptr += pendused;
368 outlen -= pendused;
32874aea 369 pendlen -= pendused;
370 if (pendlen == 0) {
371 pendsize = 0;
372 sfree(pending);
373 pending = NULL;
374 }
375 if (outlen == 0)
376 return len;
3bdaf79d 377 }
378
379 while (outlen > 0) {
32874aea 380 fd_set readfds;
8df7a775 381
32874aea 382 FD_ZERO(&readfds);
383 FD_SET(scp_ssh_socket, &readfds);
384 if (select(1, &readfds, NULL, NULL, NULL) < 0)
385 return 0; /* doom */
386 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
3bdaf79d 387 }
388
389 return len;
390}
391
392/*
393 * Loop through the ssh connection and authentication process.
394 */
32874aea 395static void ssh_scp_init(void)
396{
8df7a775 397 if (scp_ssh_socket == INVALID_SOCKET)
3bdaf79d 398 return;
399 while (!back->sendok()) {
32874aea 400 fd_set readfds;
401 FD_ZERO(&readfds);
402 FD_SET(scp_ssh_socket, &readfds);
403 if (select(1, &readfds, NULL, NULL, NULL) < 0)
404 return; /* doom */
405 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
3bdaf79d 406 }
407}
408
409/*
07d9aa13 410 * Print an error message and exit after closing the SSH link.
411 */
412static void bump(char *fmt, ...)
413{
32874aea 414 char str[0x100]; /* Make the size big enough */
c51a56e2 415 va_list ap;
416 va_start(ap, fmt);
cc87246d 417 strcpy(str, "Fatal:");
32874aea 418 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 419 va_end(ap);
cc87246d 420 strcat(str, "\n");
421 tell_str(stderr, str);
2bc6a386 422 errs++;
cc87246d 423
eba78553 424 if (back != NULL && back->socket() != NULL) {
c51a56e2 425 char ch;
3bdaf79d 426 back->special(TS_EOF);
fb09bf1c 427 ssh_scp_recv(&ch, 1);
c51a56e2 428 }
2bc6a386 429
430 if (gui_mode) {
431 unsigned int msg_id = WM_RET_ERR_CNT;
432 if (list)
433 msg_id = WM_LS_RET_ERR_CNT;
434 while (!PostMessage
435 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
436 0 /*lParam */ ))SleepEx(1000, TRUE);
437 }
438
c51a56e2 439 exit(1);
07d9aa13 440}
441
fa17a66e 442static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
07d9aa13 443{
c51a56e2 444 HANDLE hin, hout;
fa17a66e 445 DWORD savemode, newmode, i;
b8a19193 446
fa17a66e 447 if (is_pw && password) {
32874aea 448 static int tried_once = 0;
449
450 if (tried_once) {
451 return 0;
452 } else {
453 strncpy(str, password, maxlen);
454 str[maxlen - 1] = '\0';
455 tried_once = 1;
456 return 1;
457 }
b8a19193 458 }
07d9aa13 459
cc87246d 460 /* GUI Adaptation - Sept 2000 */
461 if (gui_mode) {
32874aea 462 if (maxlen > 0)
463 str[0] = '\0';
cc87246d 464 } else {
465 hin = GetStdHandle(STD_INPUT_HANDLE);
466 hout = GetStdHandle(STD_OUTPUT_HANDLE);
467 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
468 bump("Cannot get standard input/output handles");
07d9aa13 469
cc87246d 470 GetConsoleMode(hin, &savemode);
32874aea 471 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
472 if (is_pw)
473 newmode &= ~ENABLE_ECHO_INPUT;
474 else
475 newmode |= ENABLE_ECHO_INPUT;
476 SetConsoleMode(hin, newmode);
07d9aa13 477
cc87246d 478 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
32874aea 479 ReadFile(hin, str, maxlen - 1, &i, NULL);
07d9aa13 480
cc87246d 481 SetConsoleMode(hin, savemode);
07d9aa13 482
32874aea 483 if ((int) i > maxlen)
484 i = maxlen - 1;
485 else
486 i = i - 2;
cc87246d 487 str[i] = '\0';
07d9aa13 488
fa17a66e 489 if (is_pw)
32874aea 490 WriteFile(hout, "\r\n", 2, &i, NULL);
cc87246d 491 }
85ee8208 492
493 return 1;
07d9aa13 494}
495
07d9aa13 496/*
497 * Open an SSH connection to user@host and execute cmd.
498 */
499static void do_cmd(char *host, char *user, char *cmd)
500{
c51a56e2 501 char *err, *realhost;
f5e6a5c6 502 DWORD namelen;
c51a56e2 503
504 if (host == NULL || host[0] == '\0')
505 bump("Empty host name");
506
507 /* Try to load settings for this host */
a9422f39 508 do_defaults(host, &cfg);
c51a56e2 509 if (cfg.host[0] == '\0') {
510 /* No settings for this host; use defaults */
32874aea 511 do_defaults(NULL, &cfg);
512 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
513 cfg.host[sizeof(cfg.host) - 1] = '\0';
c51a56e2 514 cfg.port = 22;
515 }
516
517 /* Set username */
518 if (user != NULL && user[0] != '\0') {
32874aea 519 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
520 cfg.username[sizeof(cfg.username) - 1] = '\0';
c51a56e2 521 } else if (cfg.username[0] == '\0') {
f5e6a5c6 522 namelen = 0;
523 if (GetUserName(user, &namelen) == FALSE)
524 bump("Empty user name");
93558b21 525 user = smalloc(namelen * sizeof(char));
f5e6a5c6 526 GetUserName(user, &namelen);
32874aea 527 if (verbose)
528 tell_user(stderr, "Guessing user name: %s", user);
529 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
530 cfg.username[sizeof(cfg.username) - 1] = '\0';
f5e6a5c6 531 free(user);
c51a56e2 532 }
533
534 if (cfg.protocol != PROT_SSH)
535 cfg.port = 22;
536
ed89e8a5 537 if (portnumber)
538 cfg.port = portnumber;
539
3bdaf79d 540 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
32874aea 541 cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
3bdaf79d 542 cfg.nopty = TRUE;
543
544 back = &ssh_backend;
545
8df7a775 546 err = back->init(cfg.host, cfg.port, &realhost);
c51a56e2 547 if (err != NULL)
548 bump("ssh_init: %s", err);
3bdaf79d 549 ssh_scp_init();
c51a56e2 550 if (verbose && realhost != NULL)
cc87246d 551 tell_user(stderr, "Connected to %s\n", realhost);
6e1ebb76 552 sfree(realhost);
07d9aa13 553}
554
07d9aa13 555/*
556 * Update statistic information about current file.
557 */
558static void print_stats(char *name, unsigned long size, unsigned long done,
32874aea 559 time_t start, time_t now)
07d9aa13 560{
c51a56e2 561 float ratebs;
562 unsigned long eta;
563 char etastr[10];
564 int pct;
565
cc87246d 566 /* GUI Adaptation - Sept 2000 */
567 if (gui_mode)
32874aea 568 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
569 (unsigned long) difftime(now, start));
cc87246d 570 else {
571 if (now > start)
572 ratebs = (float) done / (now - start);
573 else
574 ratebs = (float) done;
c51a56e2 575
cc87246d 576 if (ratebs < 1.0)
577 eta = size - done;
578 else
579 eta = (unsigned long) ((size - done) / ratebs);
580 sprintf(etastr, "%02ld:%02ld:%02ld",
581 eta / 3600, (eta % 3600) / 60, eta % 60);
c51a56e2 582
cc87246d 583 pct = (int) (100.0 * (float) done / size);
c51a56e2 584
cc87246d 585 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
32874aea 586 name, done / 1024, ratebs / 1024.0, etastr, pct);
c51a56e2 587
cc87246d 588 if (done == size)
589 printf("\n");
590 }
07d9aa13 591}
592
07d9aa13 593/*
594 * Find a colon in str and return a pointer to the colon.
39ddf0ff 595 * This is used to separate hostname from filename.
07d9aa13 596 */
32874aea 597static char *colon(char *str)
07d9aa13 598{
c51a56e2 599 /* We ignore a leading colon, since the hostname cannot be
32874aea 600 empty. We also ignore a colon as second character because
601 of filenames like f:myfile.txt. */
602 if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
c51a56e2 603 return (NULL);
32874aea 604 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
c51a56e2 605 str++;
606 if (*str == ':')
607 return (str);
608 else
609 return (NULL);
07d9aa13 610}
611
07d9aa13 612/*
613 * Wait for a response from the other side.
614 * Return 0 if ok, -1 if error.
615 */
616static int response(void)
617{
c51a56e2 618 char ch, resp, rbuf[2048];
619 int p;
620
fb09bf1c 621 if (ssh_scp_recv(&resp, 1) <= 0)
c51a56e2 622 bump("Lost connection");
623
624 p = 0;
625 switch (resp) {
32874aea 626 case 0: /* ok */
c51a56e2 627 return (0);
628 default:
629 rbuf[p++] = resp;
630 /* fallthrough */
32874aea 631 case 1: /* error */
632 case 2: /* fatal error */
c51a56e2 633 do {
fb09bf1c 634 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 635 bump("Protocol error: Lost connection");
636 rbuf[p++] = ch;
637 } while (p < sizeof(rbuf) && ch != '\n');
32874aea 638 rbuf[p - 1] = '\0';
c51a56e2 639 if (resp == 1)
cc87246d 640 tell_user(stderr, "%s\n", rbuf);
c51a56e2 641 else
642 bump("%s", rbuf);
643 errs++;
644 return (-1);
645 }
07d9aa13 646}
647
07d9aa13 648/*
649 * Send an error message to the other side and to the screen.
650 * Increment error counter.
651 */
652static void run_err(const char *fmt, ...)
653{
c51a56e2 654 char str[2048];
655 va_list ap;
656 va_start(ap, fmt);
657 errs++;
9520eba8 658 strcpy(str, "scp: ");
32874aea 659 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 660 strcat(str, "\n");
ff89646a 661 back->send("\001", 1); /* scp protocol error prefix */
3bdaf79d 662 back->send(str, strlen(str));
32874aea 663 tell_user(stderr, "%s", str);
c51a56e2 664 va_end(ap);
07d9aa13 665}
666
07d9aa13 667/*
668 * Execute the source part of the SCP protocol.
669 */
670static void source(char *src)
671{
c51a56e2 672 char buf[2048];
673 unsigned long size;
674 char *last;
675 HANDLE f;
676 DWORD attr;
677 unsigned long i;
678 unsigned long stat_bytes;
679 time_t stat_starttime, stat_lasttime;
680
681 attr = GetFileAttributes(src);
32874aea 682 if (attr == (DWORD) - 1) {
c51a56e2 683 run_err("%s: No such file or directory", src);
684 return;
685 }
686
687 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
7f1f80de 688 if (recursive) {
32874aea 689 /*
690 * Avoid . and .. directories.
691 */
692 char *p;
693 p = strrchr(src, '/');
694 if (!p)
695 p = strrchr(src, '\\');
696 if (!p)
697 p = src;
698 else
699 p++;
700 if (!strcmp(p, ".") || !strcmp(p, ".."))
701 /* skip . and .. */ ;
702 else
703 rsource(src);
704 } else {
c51a56e2 705 run_err("%s: not a regular file", src);
32874aea 706 }
c51a56e2 707 return;
708 }
709
710 if ((last = strrchr(src, '/')) == NULL)
711 last = src;
712 else
713 last++;
714 if (strrchr(last, '\\') != NULL)
715 last = strrchr(last, '\\') + 1;
716 if (last == src && strchr(src, ':') != NULL)
717 last = strchr(src, ':') + 1;
718
719 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
720 OPEN_EXISTING, 0, 0);
721 if (f == INVALID_HANDLE_VALUE) {
486543a1 722 run_err("%s: Cannot open file", src);
c51a56e2 723 return;
724 }
725
726 if (preserve) {
727 FILETIME actime, wrtime;
728 unsigned long mtime, atime;
729 GetFileTime(f, NULL, &actime, &wrtime);
730 TIME_WIN_TO_POSIX(actime, atime);
731 TIME_WIN_TO_POSIX(wrtime, mtime);
732 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
3bdaf79d 733 back->send(buf, strlen(buf));
07d9aa13 734 if (response())
c51a56e2 735 return;
736 }
737
738 size = GetFileSize(f, NULL);
739 sprintf(buf, "C0644 %lu %s\n", size, last);
740 if (verbose)
cc87246d 741 tell_user(stderr, "Sending file modes: %s", buf);
3bdaf79d 742 back->send(buf, strlen(buf));
c51a56e2 743 if (response())
744 return;
745
746 if (statistics) {
747 stat_bytes = 0;
748 stat_starttime = time(NULL);
749 stat_lasttime = 0;
750 }
751
752 for (i = 0; i < size; i += 4096) {
753 char transbuf[4096];
754 DWORD j, k = 4096;
32874aea 755 if (i + k > size)
756 k = size - i;
757 if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
758 if (statistics)
759 printf("\n");
c51a56e2 760 bump("%s: Read error", src);
07d9aa13 761 }
3bdaf79d 762 back->send(transbuf, k);
c51a56e2 763 if (statistics) {
764 stat_bytes += k;
32874aea 765 if (time(NULL) != stat_lasttime || i + k == size) {
c51a56e2 766 stat_lasttime = time(NULL);
767 print_stats(last, size, stat_bytes,
768 stat_starttime, stat_lasttime);
769 }
07d9aa13 770 }
c51a56e2 771 }
772 CloseHandle(f);
07d9aa13 773
3bdaf79d 774 back->send("", 1);
c51a56e2 775 (void) response();
07d9aa13 776}
777
07d9aa13 778/*
779 * Recursively send the contents of a directory.
780 */
781static void rsource(char *src)
782{
c51a56e2 783 char buf[2048];
784 char *last;
785 HANDLE dir;
786 WIN32_FIND_DATA fdat;
787 int ok;
788
789 if ((last = strrchr(src, '/')) == NULL)
790 last = src;
791 else
792 last++;
793 if (strrchr(last, '\\') != NULL)
794 last = strrchr(last, '\\') + 1;
795 if (last == src && strchr(src, ':') != NULL)
796 last = strchr(src, ':') + 1;
797
798 /* maybe send filetime */
799
800 sprintf(buf, "D0755 0 %s\n", last);
801 if (verbose)
cc87246d 802 tell_user(stderr, "Entering directory: %s", buf);
3bdaf79d 803 back->send(buf, strlen(buf));
c51a56e2 804 if (response())
805 return;
806
807 sprintf(buf, "%s/*", src);
808 dir = FindFirstFile(buf, &fdat);
809 ok = (dir != INVALID_HANDLE_VALUE);
810 while (ok) {
811 if (strcmp(fdat.cFileName, ".") == 0 ||
812 strcmp(fdat.cFileName, "..") == 0) {
32874aea 813 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
c51a56e2 814 run_err("%s/%s: Name too long", src, fdat.cFileName);
815 } else {
816 sprintf(buf, "%s/%s", src, fdat.cFileName);
817 source(buf);
07d9aa13 818 }
c51a56e2 819 ok = FindNextFile(dir, &fdat);
820 }
821 FindClose(dir);
07d9aa13 822
c51a56e2 823 sprintf(buf, "E\n");
3bdaf79d 824 back->send(buf, strlen(buf));
c51a56e2 825 (void) response();
07d9aa13 826}
827
07d9aa13 828/*
829 * Execute the sink part of the SCP protocol.
830 */
ca2d5943 831static void sink(char *targ, char *src)
07d9aa13 832{
c51a56e2 833 char buf[2048];
834 char namebuf[2048];
835 char ch;
836 int targisdir = 0;
996c8c3b 837 int settime;
c51a56e2 838 int exists;
839 DWORD attr;
840 HANDLE f;
841 unsigned long mtime, atime;
842 unsigned int mode;
843 unsigned long size, i;
844 int wrerror = 0;
845 unsigned long stat_bytes;
846 time_t stat_starttime, stat_lasttime;
847 char *stat_name;
848
849 attr = GetFileAttributes(targ);
32874aea 850 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
c51a56e2 851 targisdir = 1;
852
853 if (targetshouldbedirectory && !targisdir)
854 bump("%s: Not a directory", targ);
855
3bdaf79d 856 back->send("", 1);
c51a56e2 857 while (1) {
858 settime = 0;
32874aea 859 gottime:
fb09bf1c 860 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 861 return;
862 if (ch == '\n')
863 bump("Protocol error: Unexpected newline");
864 i = 0;
865 buf[i++] = ch;
866 do {
fb09bf1c 867 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 868 bump("Lost connection");
869 buf[i++] = ch;
870 } while (i < sizeof(buf) && ch != '\n');
32874aea 871 buf[i - 1] = '\0';
c51a56e2 872 switch (buf[0]) {
32874aea 873 case '\01': /* error */
874 tell_user(stderr, "%s\n", buf + 1);
c51a56e2 875 errs++;
876 continue;
32874aea 877 case '\02': /* fatal error */
878 bump("%s", buf + 1);
c51a56e2 879 case 'E':
3bdaf79d 880 back->send("", 1);
c51a56e2 881 return;
882 case 'T':
32874aea 883 if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
c51a56e2 884 settime = 1;
3bdaf79d 885 back->send("", 1);
c51a56e2 886 goto gottime;
887 }
888 bump("Protocol error: Illegal time format");
889 case 'C':
890 case 'D':
891 break;
892 default:
893 bump("Protocol error: Expected control record");
894 }
07d9aa13 895
32874aea 896 if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
c51a56e2 897 bump("Protocol error: Illegal file descriptor format");
ca2d5943 898 /* Security fix: ensure the file ends up where we asked for it. */
c51a56e2 899 if (targisdir) {
900 char t[2048];
9520eba8 901 char *p;
c51a56e2 902 strcpy(t, targ);
903 if (targ[0] != '\0')
904 strcat(t, "/");
9520eba8 905 p = namebuf + strlen(namebuf);
906 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
907 p--;
908 strcat(t, p);
c51a56e2 909 strcpy(namebuf, t);
910 } else {
911 strcpy(namebuf, targ);
912 }
913 attr = GetFileAttributes(namebuf);
32874aea 914 exists = (attr != (DWORD) - 1);
c51a56e2 915
916 if (buf[0] == 'D') {
917 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
918 run_err("%s: Not a directory", namebuf);
919 continue;
920 }
921 if (!exists) {
32874aea 922 if (!CreateDirectory(namebuf, NULL)) {
923 run_err("%s: Cannot create directory", namebuf);
c51a56e2 924 continue;
925 }
926 }
ca2d5943 927 sink(namebuf, NULL);
c51a56e2 928 /* can we set the timestamp for directories ? */
929 continue;
930 }
07d9aa13 931
c51a56e2 932 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
933 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
934 if (f == INVALID_HANDLE_VALUE) {
935 run_err("%s: Cannot create file", namebuf);
936 continue;
937 }
07d9aa13 938
3bdaf79d 939 back->send("", 1);
07d9aa13 940
c51a56e2 941 if (statistics) {
942 stat_bytes = 0;
943 stat_starttime = time(NULL);
944 stat_lasttime = 0;
945 if ((stat_name = strrchr(namebuf, '/')) == NULL)
946 stat_name = namebuf;
947 else
948 stat_name++;
949 if (strrchr(stat_name, '\\') != NULL)
950 stat_name = strrchr(stat_name, '\\') + 1;
951 }
07d9aa13 952
c51a56e2 953 for (i = 0; i < size; i += 4096) {
954 char transbuf[4096];
996c8c3b 955 DWORD j, k = 4096;
32874aea 956 if (i + k > size)
957 k = size - i;
fb09bf1c 958 if (ssh_scp_recv(transbuf, k) == 0)
c51a56e2 959 bump("Lost connection");
32874aea 960 if (wrerror)
961 continue;
962 if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
c51a56e2 963 wrerror = 1;
964 if (statistics)
965 printf("\r%-25.25s | %50s\n",
966 stat_name,
967 "Write error.. waiting for end of file");
968 continue;
969 }
970 if (statistics) {
971 stat_bytes += k;
32874aea 972 if (time(NULL) > stat_lasttime || i + k == size) {
c51a56e2 973 stat_lasttime = time(NULL);
974 print_stats(stat_name, size, stat_bytes,
975 stat_starttime, stat_lasttime);
07d9aa13 976 }
c51a56e2 977 }
978 }
979 (void) response();
07d9aa13 980
c51a56e2 981 if (settime) {
982 FILETIME actime, wrtime;
983 TIME_POSIX_TO_WIN(atime, actime);
984 TIME_POSIX_TO_WIN(mtime, wrtime);
985 SetFileTime(f, NULL, &actime, &wrtime);
07d9aa13 986 }
07d9aa13 987
c51a56e2 988 CloseHandle(f);
989 if (wrerror) {
990 run_err("%s: Write error", namebuf);
991 continue;
992 }
3bdaf79d 993 back->send("", 1);
c51a56e2 994 }
995}
07d9aa13 996
997/*
998 * We will copy local files to a remote server.
999 */
1000static void toremote(int argc, char *argv[])
1001{
c51a56e2 1002 char *src, *targ, *host, *user;
1003 char *cmd;
1004 int i;
1005
32874aea 1006 targ = argv[argc - 1];
c51a56e2 1007
39ddf0ff 1008 /* Separate host from filename */
c51a56e2 1009 host = targ;
1010 targ = colon(targ);
1011 if (targ == NULL)
1012 bump("targ == NULL in toremote()");
1013 *targ++ = '\0';
1014 if (*targ == '\0')
1015 targ = ".";
1016 /* Substitute "." for emtpy target */
1017
39ddf0ff 1018 /* Separate host and username */
c51a56e2 1019 user = host;
1020 host = strrchr(host, '@');
1021 if (host == NULL) {
1022 host = user;
1023 user = NULL;
1024 } else {
1025 *host++ = '\0';
1026 if (*user == '\0')
1027 user = NULL;
1028 }
1029
1030 if (argc == 2) {
1031 /* Find out if the source filespec covers multiple files
32874aea 1032 if so, we should set the targetshouldbedirectory flag */
c51a56e2 1033 HANDLE fh;
1034 WIN32_FIND_DATA fdat;
1035 if (colon(argv[0]) != NULL)
1036 bump("%s: Remote to remote not supported", argv[0]);
1037 fh = FindFirstFile(argv[0], &fdat);
1038 if (fh == INVALID_HANDLE_VALUE)
1039 bump("%s: No such file or directory\n", argv[0]);
1040 if (FindNextFile(fh, &fdat))
1041 targetshouldbedirectory = 1;
1042 FindClose(fh);
1043 }
1044
1045 cmd = smalloc(strlen(targ) + 100);
1046 sprintf(cmd, "scp%s%s%s%s -t %s",
1047 verbose ? " -v" : "",
1048 recursive ? " -r" : "",
1049 preserve ? " -p" : "",
32874aea 1050 targetshouldbedirectory ? " -d" : "", targ);
c51a56e2 1051 do_cmd(host, user, cmd);
1052 sfree(cmd);
1053
1054 (void) response();
1055
1056 for (i = 0; i < argc - 1; i++) {
1057 HANDLE dir;
1058 WIN32_FIND_DATA fdat;
1059 src = argv[i];
1060 if (colon(src) != NULL) {
cc87246d 1061 tell_user(stderr, "%s: Remote to remote not supported\n", src);
c51a56e2 1062 errs++;
1063 continue;
07d9aa13 1064 }
c51a56e2 1065 dir = FindFirstFile(src, &fdat);
1066 if (dir == INVALID_HANDLE_VALUE) {
1067 run_err("%s: No such file or directory", src);
1068 continue;
07d9aa13 1069 }
c51a56e2 1070 do {
1071 char *last;
1072 char namebuf[2048];
7f266ffb 1073 /*
1074 * Ensure that . and .. are never matched by wildcards,
1075 * but only by deliberate action.
1076 */
1077 if (!strcmp(fdat.cFileName, ".") ||
1078 !strcmp(fdat.cFileName, "..")) {
1079 /*
1080 * Find*File has returned a special dir. We require
1081 * that _either_ `src' ends in a backslash followed
1082 * by that string, _or_ `src' is precisely that
1083 * string.
1084 */
1085 int len = strlen(src), dlen = strlen(fdat.cFileName);
1086 if (len == dlen && !strcmp(src, fdat.cFileName)) {
32874aea 1087 /* ok */ ;
1088 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1089 !strcmp(src + len - dlen, fdat.cFileName)) {
1090 /* ok */ ;
7f266ffb 1091 } else
1092 continue; /* ignore this one */
1093 }
32874aea 1094 if (strlen(src) + strlen(fdat.cFileName) >= sizeof(namebuf)) {
cc87246d 1095 tell_user(stderr, "%s: Name too long", src);
c51a56e2 1096 continue;
1097 }
1098 strcpy(namebuf, src);
1099 if ((last = strrchr(namebuf, '/')) == NULL)
1100 last = namebuf;
1101 else
1102 last++;
1103 if (strrchr(last, '\\') != NULL)
1104 last = strrchr(last, '\\') + 1;
1105 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1106 last = strchr(namebuf, ':') + 1;
1107 strcpy(last, fdat.cFileName);
1108 source(namebuf);
1109 } while (FindNextFile(dir, &fdat));
1110 FindClose(dir);
1111 }
07d9aa13 1112}
1113
07d9aa13 1114/*
1115 * We will copy files from a remote server to the local machine.
1116 */
1117static void tolocal(int argc, char *argv[])
1118{
c51a56e2 1119 char *src, *targ, *host, *user;
1120 char *cmd;
1121
1122 if (argc != 2)
1123 bump("More than one remote source not supported");
1124
1125 src = argv[0];
1126 targ = argv[1];
1127
39ddf0ff 1128 /* Separate host from filename */
c51a56e2 1129 host = src;
1130 src = colon(src);
1131 if (src == NULL)
1132 bump("Local to local copy not supported");
1133 *src++ = '\0';
1134 if (*src == '\0')
1135 src = ".";
1136 /* Substitute "." for empty filename */
1137
39ddf0ff 1138 /* Separate username and hostname */
c51a56e2 1139 user = host;
1140 host = strrchr(host, '@');
1141 if (host == NULL) {
1142 host = user;
1143 user = NULL;
1144 } else {
1145 *host++ = '\0';
1146 if (*user == '\0')
1147 user = NULL;
1148 }
1149
1150 cmd = smalloc(strlen(src) + 100);
1151 sprintf(cmd, "scp%s%s%s%s -f %s",
1152 verbose ? " -v" : "",
1153 recursive ? " -r" : "",
1154 preserve ? " -p" : "",
32874aea 1155 targetshouldbedirectory ? " -d" : "", src);
c51a56e2 1156 do_cmd(host, user, cmd);
1157 sfree(cmd);
1158
ca2d5943 1159 sink(targ, src);
07d9aa13 1160}
1161
07d9aa13 1162/*
39ddf0ff 1163 * We will issue a list command to get a remote directory.
1164 */
1165static void get_dir_list(int argc, char *argv[])
1166{
1167 char *src, *host, *user;
1168 char *cmd, *p, *q;
1169 char c;
1170
1171 src = argv[0];
1172
1173 /* Separate host from filename */
1174 host = src;
1175 src = colon(src);
1176 if (src == NULL)
1177 bump("Local to local copy not supported");
1178 *src++ = '\0';
1179 if (*src == '\0')
1180 src = ".";
1181 /* Substitute "." for empty filename */
1182
1183 /* Separate username and hostname */
1184 user = host;
1185 host = strrchr(host, '@');
1186 if (host == NULL) {
1187 host = user;
1188 user = NULL;
1189 } else {
1190 *host++ = '\0';
1191 if (*user == '\0')
1192 user = NULL;
1193 }
1194
32874aea 1195 cmd = smalloc(4 * strlen(src) + 100);
39ddf0ff 1196 strcpy(cmd, "ls -la '");
1197 p = cmd + strlen(cmd);
1198 for (q = src; *q; q++) {
1199 if (*q == '\'') {
32874aea 1200 *p++ = '\'';
1201 *p++ = '\\';
1202 *p++ = '\'';
1203 *p++ = '\'';
39ddf0ff 1204 } else {
1205 *p++ = *q;
1206 }
1207 }
1208 *p++ = '\'';
1209 *p = '\0';
cc87246d 1210
39ddf0ff 1211 do_cmd(host, user, cmd);
1212 sfree(cmd);
1213
fb09bf1c 1214 while (ssh_scp_recv(&c, 1) > 0)
cc87246d 1215 tell_char(stdout, c);
39ddf0ff 1216}
1217
1218/*
07d9aa13 1219 * Initialize the Win$ock driver.
1220 */
996c8c3b 1221static void init_winsock(void)
07d9aa13 1222{
c51a56e2 1223 WORD winsock_ver;
1224 WSADATA wsadata;
1225
1226 winsock_ver = MAKEWORD(1, 1);
1227 if (WSAStartup(winsock_ver, &wsadata))
1228 bump("Unable to initialise WinSock");
32874aea 1229 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
c51a56e2 1230 bump("WinSock version is incompatible with 1.1");
07d9aa13 1231}
1232
07d9aa13 1233/*
1234 * Short description of parameters.
1235 */
996c8c3b 1236static void usage(void)
07d9aa13 1237{
c51a56e2 1238 printf("PuTTY Secure Copy client\n");
1239 printf("%s\n", ver);
a3e55ea1 1240 printf("Usage: pscp [options] [user@]host:source target\n");
32874aea 1241 printf
1242 (" pscp [options] source [source...] [user@]host:target\n");
a3e55ea1 1243 printf(" pscp [options] -ls user@host:filespec\n");
b8a19193 1244 printf("Options:\n");
1245 printf(" -p preserve file attributes\n");
1246 printf(" -q quiet, don't show statistics\n");
1247 printf(" -r copy directories recursively\n");
1248 printf(" -v show verbose messages\n");
1249 printf(" -P port connect to specified port\n");
1250 printf(" -pw passw login with specified password\n");
ee8b0370 1251#if 0
1252 /*
1253 * -gui is an internal option, used by GUI front ends to get
1254 * pscp to pass progress reports back to them. It's not an
1255 * ordinary user-accessible option, so it shouldn't be part of
1256 * the command-line help. The only people who need to know
1257 * about it are programmers, and they can read the source.
1258 */
32874aea 1259 printf
1260 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
ee8b0370 1261#endif
c51a56e2 1262 exit(1);
07d9aa13 1263}
1264
07d9aa13 1265/*
1266 * Main program (no, really?)
1267 */
1268int main(int argc, char *argv[])
1269{
c51a56e2 1270 int i;
1271
fb09bf1c 1272 default_protocol = PROT_TELNET;
1273
67779be7 1274 flags = FLAG_STDERR;
fa17a66e 1275 ssh_get_line = &get_line;
c51a56e2 1276 init_winsock();
8df7a775 1277 sk_init();
c51a56e2 1278
1279 for (i = 1; i < argc; i++) {
1280 if (argv[i][0] != '-')
1281 break;
1282 if (strcmp(argv[i], "-v") == 0)
4017be6d 1283 verbose = 1, flags |= FLAG_VERBOSE;
c51a56e2 1284 else if (strcmp(argv[i], "-r") == 0)
1285 recursive = 1;
1286 else if (strcmp(argv[i], "-p") == 0)
1287 preserve = 1;
1288 else if (strcmp(argv[i], "-q") == 0)
1289 statistics = 0;
32874aea 1290 else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
c51a56e2 1291 usage();
32874aea 1292 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
b8a19193 1293 portnumber = atoi(argv[++i]);
32874aea 1294 else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
b8a19193 1295 password = argv[++i];
32874aea 1296 else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
cc87246d 1297 gui_hwnd = argv[++i];
1298 gui_mode = 1;
1299 } else if (strcmp(argv[i], "-ls") == 0)
32874aea 1300 list = 1;
1301 else if (strcmp(argv[i], "--") == 0) {
1302 i++;
1303 break;
1304 } else
c51a56e2 1305 usage();
1306 }
1307 argc -= i;
1308 argv += i;
eba78553 1309 back = NULL;
c51a56e2 1310
39ddf0ff 1311 if (list) {
1312 if (argc != 1)
1313 usage();
1314 get_dir_list(argc, argv);
c51a56e2 1315
39ddf0ff 1316 } else {
1317
1318 if (argc < 2)
1319 usage();
1320 if (argc > 2)
1321 targetshouldbedirectory = 1;
1322
32874aea 1323 if (colon(argv[argc - 1]) != NULL)
39ddf0ff 1324 toremote(argc, argv);
1325 else
1326 tolocal(argc, argv);
1327 }
c51a56e2 1328
eba78553 1329 if (back != NULL && back->socket() != NULL) {
c51a56e2 1330 char ch;
3bdaf79d 1331 back->special(TS_EOF);
fb09bf1c 1332 ssh_scp_recv(&ch, 1);
c51a56e2 1333 }
1334 WSACleanup();
1335 random_save_seed();
07d9aa13 1336
cc87246d 1337 /* GUI Adaptation - August 2000 */
1338 if (gui_mode) {
1339 unsigned int msg_id = WM_RET_ERR_CNT;
32874aea 1340 if (list)
1341 msg_id = WM_LS_RET_ERR_CNT;
1342 while (!PostMessage
1343 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
1344 0 /*lParam */ ))SleepEx(1000, TRUE);
cc87246d 1345 }
c51a56e2 1346 return (errs == 0 ? 0 : 1);
07d9aa13 1347}
1348
1349/* end */