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