Fix from RDB: patch up mis-aimed fallthroughs in a terminal
[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;
d0718310 96 HANDLE hin;
97 DWORD savemode, i;
a9422f39 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"
d0718310 107 "If you want to carry on connecting just once, without\n"
108 "adding the key to the cache, enter \"n\".\n"
109 "If you do not trust this host, press Return to abandon the\n"
110 "connection.\n"
111 "Store key in cache? (y/n) ";
a9422f39 112
113 static const char wrongmsg[] =
32874aea 114 "WARNING - POTENTIAL SECURITY BREACH!\n"
115 "The server's host key does not match the one PuTTY has\n"
116 "cached in the registry. This means that either the\n"
117 "server administrator has changed the host key, or you\n"
118 "have actually connected to another computer pretending\n"
119 "to be the server.\n"
120 "The new key fingerprint is:\n"
121 "%s\n"
122 "If you were expecting this change and trust the new key,\n"
d0718310 123 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
32874aea 124 "If you want to carry on connecting but without updating\n"
d0718310 125 "the cache, enter \"n\".\n"
32874aea 126 "If you want to abandon the connection completely, press\n"
127 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
128 "safe choice.\n"
129 "Update cached key? (y/n, Return cancels connection) ";
a9422f39 130
131 static const char abandoned[] = "Connection abandoned.\n";
132
133 char line[32];
134
135 /*
136 * Verify the key against the registry.
137 */
138 ret = verify_host_key(host, port, keytype, keystr);
139
32874aea 140 if (ret == 0) /* success - key matched OK */
141 return;
d0718310 142
32874aea 143 if (ret == 2) { /* key was different */
144 fprintf(stderr, wrongmsg, fingerprint);
b4453f49 145 fflush(stderr);
a9422f39 146 }
32874aea 147 if (ret == 1) { /* key was absent */
148 fprintf(stderr, absentmsg, fingerprint);
d0718310 149 fflush(stderr);
150 }
151
152 hin = GetStdHandle(STD_INPUT_HANDLE);
153 GetConsoleMode(hin, &savemode);
154 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
155 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
156 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
157 SetConsoleMode(hin, savemode);
158
159 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
160 if (line[0] == 'y' || line[0] == 'Y')
32874aea 161 store_host_key(host, port, keytype, keystr);
d0718310 162 } else {
163 fprintf(stderr, abandoned);
164 exit(0);
a9422f39 165 }
166}
fb09bf1c 167
cc87246d 168/* GUI Adaptation - Sept 2000 */
75cab814 169static void send_msg(HWND h, UINT message, WPARAM wParam)
cc87246d 170{
32874aea 171 while (!PostMessage(h, message, wParam, 0))
172 SleepEx(1000, TRUE);
cc87246d 173}
174
32874aea 175static void tell_char(FILE * stream, char c)
cc87246d 176{
177 if (!gui_mode)
178 fputc(c, stream);
32874aea 179 else {
cc87246d 180 unsigned int msg_id = WM_STD_OUT_CHAR;
32874aea 181 if (stream == stderr)
182 msg_id = WM_STD_ERR_CHAR;
183 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
cc87246d 184 }
185}
186
32874aea 187static void tell_str(FILE * stream, char *str)
cc87246d 188{
189 unsigned int i;
190
32874aea 191 for (i = 0; i < strlen(str); ++i)
cc87246d 192 tell_char(stream, str[i]);
193}
194
32874aea 195static void tell_user(FILE * stream, char *fmt, ...)
cc87246d 196{
32874aea 197 char str[0x100]; /* Make the size big enough */
cc87246d 198 va_list ap;
199 va_start(ap, fmt);
200 vsprintf(str, fmt, ap);
201 va_end(ap);
202 strcat(str, "\n");
203 tell_str(stream, str);
204}
205
32874aea 206static void gui_update_stats(char *name, unsigned long size,
207 int percentage, unsigned long elapsed)
cc87246d 208{
209 unsigned int i;
210
32874aea 211 if (strcmp(name, statname) != 0) {
212 for (i = 0; i < strlen(name); ++i)
213 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
214 (WPARAM) name[i]);
215 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
216 strcpy(statname, name);
cc87246d 217 }
32874aea 218 if (statsize != size) {
219 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
cc87246d 220 statsize = size;
221 }
32874aea 222 if (statelapsed != elapsed) {
223 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
224 (WPARAM) elapsed);
cc87246d 225 statelapsed = elapsed;
226 }
32874aea 227 if (statperct != percentage) {
228 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
229 (WPARAM) percentage);
cc87246d 230 statperct = percentage;
231 }
232}
233
fb09bf1c 234/*
07d9aa13 235 * Print an error message and perform a fatal exit.
236 */
237void fatalbox(char *fmt, ...)
238{
32874aea 239 char str[0x100]; /* Make the size big enough */
c51a56e2 240 va_list ap;
241 va_start(ap, fmt);
cc87246d 242 strcpy(str, "Fatal:");
32874aea 243 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 244 va_end(ap);
cc87246d 245 strcat(str, "\n");
246 tell_str(stderr, str);
2bc6a386 247 errs++;
248
249 if (gui_mode) {
250 unsigned int msg_id = WM_RET_ERR_CNT;
251 if (list)
252 msg_id = WM_LS_RET_ERR_CNT;
253 while (!PostMessage
254 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
255 0 /*lParam */ ))SleepEx(1000, TRUE);
256 }
cc87246d 257
c51a56e2 258 exit(1);
07d9aa13 259}
8d5de777 260void connection_fatal(char *fmt, ...)
261{
32874aea 262 char str[0x100]; /* Make the size big enough */
8d5de777 263 va_list ap;
264 va_start(ap, fmt);
265 strcpy(str, "Fatal:");
32874aea 266 vsprintf(str + strlen(str), fmt, ap);
8d5de777 267 va_end(ap);
268 strcat(str, "\n");
269 tell_str(stderr, str);
2bc6a386 270 errs++;
271
272 if (gui_mode) {
273 unsigned int msg_id = WM_RET_ERR_CNT;
274 if (list)
275 msg_id = WM_LS_RET_ERR_CNT;
276 while (!PostMessage
277 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
278 0 /*lParam */ ))SleepEx(1000, TRUE);
279 }
8d5de777 280
281 exit(1);
282}
07d9aa13 283
07d9aa13 284/*
8df7a775 285 * Be told what socket we're supposed to be using.
286 */
287static SOCKET scp_ssh_socket;
32874aea 288char *do_select(SOCKET skt, int startup)
289{
8df7a775 290 if (startup)
291 scp_ssh_socket = skt;
292 else
293 scp_ssh_socket = INVALID_SOCKET;
294 return NULL;
295}
296extern int select_result(WPARAM, LPARAM);
297
298/*
3bdaf79d 299 * Receive a block of data from the SSH link. Block until all data
300 * is available.
301 *
302 * To do this, we repeatedly call the SSH protocol module, with our
fe50e814 303 * own trap in from_backend() to catch the data that comes back. We
304 * do this until we have enough data.
3bdaf79d 305 */
8df7a775 306
32874aea 307static unsigned char *outptr; /* where to put the data */
308static unsigned outlen; /* how much data required */
3bdaf79d 309static unsigned char *pending = NULL; /* any spare data */
32874aea 310static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
311void from_backend(int is_stderr, char *data, int datalen)
312{
313 unsigned char *p = (unsigned char *) data;
314 unsigned len = (unsigned) datalen;
fe50e814 315
3bdaf79d 316 /*
fe50e814 317 * stderr data is just spouted to local stderr and otherwise
318 * ignored.
3bdaf79d 319 */
fe50e814 320 if (is_stderr) {
321 fwrite(data, 1, len, stderr);
322 return;
323 }
3bdaf79d 324
325 inbuf_head = 0;
326
327 /*
328 * If this is before the real session begins, just return.
329 */
330 if (!outptr)
32874aea 331 return;
3bdaf79d 332
333 if (outlen > 0) {
32874aea 334 unsigned used = outlen;
335 if (used > len)
336 used = len;
337 memcpy(outptr, p, used);
338 outptr += used;
339 outlen -= used;
340 p += used;
341 len -= used;
3bdaf79d 342 }
343
344 if (len > 0) {
32874aea 345 if (pendsize < pendlen + len) {
346 pendsize = pendlen + len + 4096;
347 pending = (pending ? srealloc(pending, pendsize) :
348 smalloc(pendsize));
349 if (!pending)
350 fatalbox("Out of memory");
351 }
352 memcpy(pending + pendlen, p, len);
353 pendlen += len;
3bdaf79d 354 }
355}
32874aea 356static int ssh_scp_recv(unsigned char *buf, int len)
357{
3bdaf79d 358 outptr = buf;
359 outlen = len;
360
361 /*
362 * See if the pending-input block contains some of what we
363 * need.
364 */
365 if (pendlen > 0) {
32874aea 366 unsigned pendused = pendlen;
367 if (pendused > outlen)
368 pendused = outlen;
3bdaf79d 369 memcpy(outptr, pending, pendused);
32874aea 370 memmove(pending, pending + pendused, pendlen - pendused);
3bdaf79d 371 outptr += pendused;
372 outlen -= pendused;
32874aea 373 pendlen -= pendused;
374 if (pendlen == 0) {
375 pendsize = 0;
376 sfree(pending);
377 pending = NULL;
378 }
379 if (outlen == 0)
380 return len;
3bdaf79d 381 }
382
383 while (outlen > 0) {
32874aea 384 fd_set readfds;
8df7a775 385
32874aea 386 FD_ZERO(&readfds);
387 FD_SET(scp_ssh_socket, &readfds);
388 if (select(1, &readfds, NULL, NULL, NULL) < 0)
389 return 0; /* doom */
390 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
3bdaf79d 391 }
392
393 return len;
394}
395
396/*
397 * Loop through the ssh connection and authentication process.
398 */
32874aea 399static void ssh_scp_init(void)
400{
8df7a775 401 if (scp_ssh_socket == INVALID_SOCKET)
3bdaf79d 402 return;
403 while (!back->sendok()) {
32874aea 404 fd_set readfds;
405 FD_ZERO(&readfds);
406 FD_SET(scp_ssh_socket, &readfds);
407 if (select(1, &readfds, NULL, NULL, NULL) < 0)
408 return; /* doom */
409 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
3bdaf79d 410 }
411}
412
413/*
07d9aa13 414 * Print an error message and exit after closing the SSH link.
415 */
416static void bump(char *fmt, ...)
417{
32874aea 418 char str[0x100]; /* Make the size big enough */
c51a56e2 419 va_list ap;
420 va_start(ap, fmt);
cc87246d 421 strcpy(str, "Fatal:");
32874aea 422 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 423 va_end(ap);
cc87246d 424 strcat(str, "\n");
425 tell_str(stderr, str);
2bc6a386 426 errs++;
cc87246d 427
eba78553 428 if (back != NULL && back->socket() != NULL) {
c51a56e2 429 char ch;
3bdaf79d 430 back->special(TS_EOF);
fb09bf1c 431 ssh_scp_recv(&ch, 1);
c51a56e2 432 }
2bc6a386 433
434 if (gui_mode) {
435 unsigned int msg_id = WM_RET_ERR_CNT;
436 if (list)
437 msg_id = WM_LS_RET_ERR_CNT;
438 while (!PostMessage
439 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
440 0 /*lParam */ ))SleepEx(1000, TRUE);
441 }
442
c51a56e2 443 exit(1);
07d9aa13 444}
445
fa17a66e 446static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
07d9aa13 447{
c51a56e2 448 HANDLE hin, hout;
fa17a66e 449 DWORD savemode, newmode, i;
b8a19193 450
fa17a66e 451 if (is_pw && password) {
32874aea 452 static int tried_once = 0;
453
454 if (tried_once) {
455 return 0;
456 } else {
457 strncpy(str, password, maxlen);
458 str[maxlen - 1] = '\0';
459 tried_once = 1;
460 return 1;
461 }
b8a19193 462 }
07d9aa13 463
cc87246d 464 /* GUI Adaptation - Sept 2000 */
465 if (gui_mode) {
32874aea 466 if (maxlen > 0)
467 str[0] = '\0';
cc87246d 468 } else {
469 hin = GetStdHandle(STD_INPUT_HANDLE);
470 hout = GetStdHandle(STD_OUTPUT_HANDLE);
471 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
472 bump("Cannot get standard input/output handles");
07d9aa13 473
cc87246d 474 GetConsoleMode(hin, &savemode);
32874aea 475 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
476 if (is_pw)
477 newmode &= ~ENABLE_ECHO_INPUT;
478 else
479 newmode |= ENABLE_ECHO_INPUT;
480 SetConsoleMode(hin, newmode);
07d9aa13 481
cc87246d 482 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
32874aea 483 ReadFile(hin, str, maxlen - 1, &i, NULL);
07d9aa13 484
cc87246d 485 SetConsoleMode(hin, savemode);
07d9aa13 486
32874aea 487 if ((int) i > maxlen)
488 i = maxlen - 1;
489 else
490 i = i - 2;
cc87246d 491 str[i] = '\0';
07d9aa13 492
fa17a66e 493 if (is_pw)
32874aea 494 WriteFile(hout, "\r\n", 2, &i, NULL);
cc87246d 495 }
85ee8208 496
497 return 1;
07d9aa13 498}
499
07d9aa13 500/*
501 * Open an SSH connection to user@host and execute cmd.
502 */
503static void do_cmd(char *host, char *user, char *cmd)
504{
c51a56e2 505 char *err, *realhost;
f5e6a5c6 506 DWORD namelen;
c51a56e2 507
508 if (host == NULL || host[0] == '\0')
509 bump("Empty host name");
510
511 /* Try to load settings for this host */
a9422f39 512 do_defaults(host, &cfg);
c51a56e2 513 if (cfg.host[0] == '\0') {
514 /* No settings for this host; use defaults */
32874aea 515 do_defaults(NULL, &cfg);
516 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
517 cfg.host[sizeof(cfg.host) - 1] = '\0';
c51a56e2 518 cfg.port = 22;
519 }
520
521 /* Set username */
522 if (user != NULL && user[0] != '\0') {
32874aea 523 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
524 cfg.username[sizeof(cfg.username) - 1] = '\0';
c51a56e2 525 } else if (cfg.username[0] == '\0') {
f5e6a5c6 526 namelen = 0;
527 if (GetUserName(user, &namelen) == FALSE)
528 bump("Empty user name");
93558b21 529 user = smalloc(namelen * sizeof(char));
f5e6a5c6 530 GetUserName(user, &namelen);
32874aea 531 if (verbose)
532 tell_user(stderr, "Guessing user name: %s", user);
533 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
534 cfg.username[sizeof(cfg.username) - 1] = '\0';
f5e6a5c6 535 free(user);
c51a56e2 536 }
537
538 if (cfg.protocol != PROT_SSH)
539 cfg.port = 22;
540
ed89e8a5 541 if (portnumber)
542 cfg.port = portnumber;
543
3bdaf79d 544 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
32874aea 545 cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
3bdaf79d 546 cfg.nopty = TRUE;
547
548 back = &ssh_backend;
549
8df7a775 550 err = back->init(cfg.host, cfg.port, &realhost);
c51a56e2 551 if (err != NULL)
552 bump("ssh_init: %s", err);
3bdaf79d 553 ssh_scp_init();
c51a56e2 554 if (verbose && realhost != NULL)
cc87246d 555 tell_user(stderr, "Connected to %s\n", realhost);
6e1ebb76 556 sfree(realhost);
07d9aa13 557}
558
07d9aa13 559/*
560 * Update statistic information about current file.
561 */
562static void print_stats(char *name, unsigned long size, unsigned long done,
32874aea 563 time_t start, time_t now)
07d9aa13 564{
c51a56e2 565 float ratebs;
566 unsigned long eta;
567 char etastr[10];
568 int pct;
569
cc87246d 570 /* GUI Adaptation - Sept 2000 */
571 if (gui_mode)
32874aea 572 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
573 (unsigned long) difftime(now, start));
cc87246d 574 else {
575 if (now > start)
576 ratebs = (float) done / (now - start);
577 else
578 ratebs = (float) done;
c51a56e2 579
cc87246d 580 if (ratebs < 1.0)
581 eta = size - done;
582 else
583 eta = (unsigned long) ((size - done) / ratebs);
584 sprintf(etastr, "%02ld:%02ld:%02ld",
585 eta / 3600, (eta % 3600) / 60, eta % 60);
c51a56e2 586
cc87246d 587 pct = (int) (100.0 * (float) done / size);
c51a56e2 588
cc87246d 589 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
32874aea 590 name, done / 1024, ratebs / 1024.0, etastr, pct);
c51a56e2 591
cc87246d 592 if (done == size)
593 printf("\n");
594 }
07d9aa13 595}
596
07d9aa13 597/*
598 * Find a colon in str and return a pointer to the colon.
39ddf0ff 599 * This is used to separate hostname from filename.
07d9aa13 600 */
32874aea 601static char *colon(char *str)
07d9aa13 602{
c51a56e2 603 /* We ignore a leading colon, since the hostname cannot be
32874aea 604 empty. We also ignore a colon as second character because
605 of filenames like f:myfile.txt. */
606 if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
c51a56e2 607 return (NULL);
32874aea 608 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
c51a56e2 609 str++;
610 if (*str == ':')
611 return (str);
612 else
613 return (NULL);
07d9aa13 614}
615
07d9aa13 616/*
617 * Wait for a response from the other side.
618 * Return 0 if ok, -1 if error.
619 */
620static int response(void)
621{
c51a56e2 622 char ch, resp, rbuf[2048];
623 int p;
624
fb09bf1c 625 if (ssh_scp_recv(&resp, 1) <= 0)
c51a56e2 626 bump("Lost connection");
627
628 p = 0;
629 switch (resp) {
32874aea 630 case 0: /* ok */
c51a56e2 631 return (0);
632 default:
633 rbuf[p++] = resp;
634 /* fallthrough */
32874aea 635 case 1: /* error */
636 case 2: /* fatal error */
c51a56e2 637 do {
fb09bf1c 638 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 639 bump("Protocol error: Lost connection");
640 rbuf[p++] = ch;
641 } while (p < sizeof(rbuf) && ch != '\n');
32874aea 642 rbuf[p - 1] = '\0';
c51a56e2 643 if (resp == 1)
cc87246d 644 tell_user(stderr, "%s\n", rbuf);
c51a56e2 645 else
646 bump("%s", rbuf);
647 errs++;
648 return (-1);
649 }
07d9aa13 650}
651
07d9aa13 652/*
653 * Send an error message to the other side and to the screen.
654 * Increment error counter.
655 */
656static void run_err(const char *fmt, ...)
657{
c51a56e2 658 char str[2048];
659 va_list ap;
660 va_start(ap, fmt);
661 errs++;
9520eba8 662 strcpy(str, "scp: ");
32874aea 663 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 664 strcat(str, "\n");
ff89646a 665 back->send("\001", 1); /* scp protocol error prefix */
3bdaf79d 666 back->send(str, strlen(str));
32874aea 667 tell_user(stderr, "%s", str);
c51a56e2 668 va_end(ap);
07d9aa13 669}
670
07d9aa13 671/*
672 * Execute the source part of the SCP protocol.
673 */
674static void source(char *src)
675{
c51a56e2 676 char buf[2048];
677 unsigned long size;
678 char *last;
679 HANDLE f;
680 DWORD attr;
681 unsigned long i;
682 unsigned long stat_bytes;
683 time_t stat_starttime, stat_lasttime;
684
685 attr = GetFileAttributes(src);
32874aea 686 if (attr == (DWORD) - 1) {
c51a56e2 687 run_err("%s: No such file or directory", src);
688 return;
689 }
690
691 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
7f1f80de 692 if (recursive) {
32874aea 693 /*
694 * Avoid . and .. directories.
695 */
696 char *p;
697 p = strrchr(src, '/');
698 if (!p)
699 p = strrchr(src, '\\');
700 if (!p)
701 p = src;
702 else
703 p++;
704 if (!strcmp(p, ".") || !strcmp(p, ".."))
705 /* skip . and .. */ ;
706 else
707 rsource(src);
708 } else {
c51a56e2 709 run_err("%s: not a regular file", src);
32874aea 710 }
c51a56e2 711 return;
712 }
713
714 if ((last = strrchr(src, '/')) == NULL)
715 last = src;
716 else
717 last++;
718 if (strrchr(last, '\\') != NULL)
719 last = strrchr(last, '\\') + 1;
720 if (last == src && strchr(src, ':') != NULL)
721 last = strchr(src, ':') + 1;
722
723 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
724 OPEN_EXISTING, 0, 0);
725 if (f == INVALID_HANDLE_VALUE) {
486543a1 726 run_err("%s: Cannot open file", src);
c51a56e2 727 return;
728 }
729
730 if (preserve) {
731 FILETIME actime, wrtime;
732 unsigned long mtime, atime;
733 GetFileTime(f, NULL, &actime, &wrtime);
734 TIME_WIN_TO_POSIX(actime, atime);
735 TIME_WIN_TO_POSIX(wrtime, mtime);
736 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
3bdaf79d 737 back->send(buf, strlen(buf));
07d9aa13 738 if (response())
c51a56e2 739 return;
740 }
741
742 size = GetFileSize(f, NULL);
743 sprintf(buf, "C0644 %lu %s\n", size, last);
744 if (verbose)
cc87246d 745 tell_user(stderr, "Sending file modes: %s", buf);
3bdaf79d 746 back->send(buf, strlen(buf));
c51a56e2 747 if (response())
748 return;
749
2d466ffd 750 stat_bytes = 0;
751 stat_starttime = time(NULL);
752 stat_lasttime = 0;
c51a56e2 753
754 for (i = 0; i < size; i += 4096) {
755 char transbuf[4096];
756 DWORD j, k = 4096;
32874aea 757 if (i + k > size)
758 k = size - i;
759 if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
760 if (statistics)
761 printf("\n");
c51a56e2 762 bump("%s: Read error", src);
07d9aa13 763 }
3bdaf79d 764 back->send(transbuf, k);
c51a56e2 765 if (statistics) {
766 stat_bytes += k;
32874aea 767 if (time(NULL) != stat_lasttime || i + k == size) {
c51a56e2 768 stat_lasttime = time(NULL);
769 print_stats(last, size, stat_bytes,
770 stat_starttime, stat_lasttime);
771 }
07d9aa13 772 }
c51a56e2 773 }
774 CloseHandle(f);
07d9aa13 775
3bdaf79d 776 back->send("", 1);
c51a56e2 777 (void) response();
07d9aa13 778}
779
07d9aa13 780/*
781 * Recursively send the contents of a directory.
782 */
783static void rsource(char *src)
784{
c51a56e2 785 char buf[2048];
786 char *last;
787 HANDLE dir;
788 WIN32_FIND_DATA fdat;
789 int ok;
790
791 if ((last = strrchr(src, '/')) == NULL)
792 last = src;
793 else
794 last++;
795 if (strrchr(last, '\\') != NULL)
796 last = strrchr(last, '\\') + 1;
797 if (last == src && strchr(src, ':') != NULL)
798 last = strchr(src, ':') + 1;
799
800 /* maybe send filetime */
801
802 sprintf(buf, "D0755 0 %s\n", last);
803 if (verbose)
cc87246d 804 tell_user(stderr, "Entering directory: %s", buf);
3bdaf79d 805 back->send(buf, strlen(buf));
c51a56e2 806 if (response())
807 return;
808
809 sprintf(buf, "%s/*", src);
810 dir = FindFirstFile(buf, &fdat);
811 ok = (dir != INVALID_HANDLE_VALUE);
812 while (ok) {
813 if (strcmp(fdat.cFileName, ".") == 0 ||
814 strcmp(fdat.cFileName, "..") == 0) {
32874aea 815 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
c51a56e2 816 run_err("%s/%s: Name too long", src, fdat.cFileName);
817 } else {
818 sprintf(buf, "%s/%s", src, fdat.cFileName);
819 source(buf);
07d9aa13 820 }
c51a56e2 821 ok = FindNextFile(dir, &fdat);
822 }
823 FindClose(dir);
07d9aa13 824
c51a56e2 825 sprintf(buf, "E\n");
3bdaf79d 826 back->send(buf, strlen(buf));
c51a56e2 827 (void) response();
07d9aa13 828}
829
07d9aa13 830/*
831 * Execute the sink part of the SCP protocol.
832 */
ca2d5943 833static void sink(char *targ, char *src)
07d9aa13 834{
c51a56e2 835 char buf[2048];
836 char namebuf[2048];
837 char ch;
838 int targisdir = 0;
996c8c3b 839 int settime;
c51a56e2 840 int exists;
841 DWORD attr;
842 HANDLE f;
843 unsigned long mtime, atime;
844 unsigned int mode;
845 unsigned long size, i;
846 int wrerror = 0;
847 unsigned long stat_bytes;
848 time_t stat_starttime, stat_lasttime;
849 char *stat_name;
850
851 attr = GetFileAttributes(targ);
32874aea 852 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
c51a56e2 853 targisdir = 1;
854
855 if (targetshouldbedirectory && !targisdir)
856 bump("%s: Not a directory", targ);
857
3bdaf79d 858 back->send("", 1);
c51a56e2 859 while (1) {
860 settime = 0;
32874aea 861 gottime:
fb09bf1c 862 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 863 return;
864 if (ch == '\n')
865 bump("Protocol error: Unexpected newline");
866 i = 0;
867 buf[i++] = ch;
868 do {
fb09bf1c 869 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 870 bump("Lost connection");
871 buf[i++] = ch;
872 } while (i < sizeof(buf) && ch != '\n');
32874aea 873 buf[i - 1] = '\0';
c51a56e2 874 switch (buf[0]) {
32874aea 875 case '\01': /* error */
876 tell_user(stderr, "%s\n", buf + 1);
c51a56e2 877 errs++;
878 continue;
32874aea 879 case '\02': /* fatal error */
880 bump("%s", buf + 1);
c51a56e2 881 case 'E':
3bdaf79d 882 back->send("", 1);
c51a56e2 883 return;
884 case 'T':
32874aea 885 if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
c51a56e2 886 settime = 1;
3bdaf79d 887 back->send("", 1);
c51a56e2 888 goto gottime;
889 }
890 bump("Protocol error: Illegal time format");
891 case 'C':
892 case 'D':
893 break;
894 default:
895 bump("Protocol error: Expected control record");
896 }
07d9aa13 897
32874aea 898 if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
c51a56e2 899 bump("Protocol error: Illegal file descriptor format");
ca2d5943 900 /* Security fix: ensure the file ends up where we asked for it. */
c51a56e2 901 if (targisdir) {
902 char t[2048];
9520eba8 903 char *p;
c51a56e2 904 strcpy(t, targ);
905 if (targ[0] != '\0')
906 strcat(t, "/");
9520eba8 907 p = namebuf + strlen(namebuf);
908 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
909 p--;
910 strcat(t, p);
c51a56e2 911 strcpy(namebuf, t);
912 } else {
913 strcpy(namebuf, targ);
914 }
915 attr = GetFileAttributes(namebuf);
32874aea 916 exists = (attr != (DWORD) - 1);
c51a56e2 917
918 if (buf[0] == 'D') {
919 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
920 run_err("%s: Not a directory", namebuf);
921 continue;
922 }
923 if (!exists) {
32874aea 924 if (!CreateDirectory(namebuf, NULL)) {
925 run_err("%s: Cannot create directory", namebuf);
c51a56e2 926 continue;
927 }
928 }
ca2d5943 929 sink(namebuf, NULL);
c51a56e2 930 /* can we set the timestamp for directories ? */
931 continue;
932 }
07d9aa13 933
c51a56e2 934 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
935 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
936 if (f == INVALID_HANDLE_VALUE) {
937 run_err("%s: Cannot create file", namebuf);
938 continue;
939 }
07d9aa13 940
3bdaf79d 941 back->send("", 1);
07d9aa13 942
2d466ffd 943 stat_bytes = 0;
944 stat_starttime = time(NULL);
945 stat_lasttime = 0;
946 if ((stat_name = strrchr(namebuf, '/')) == NULL)
947 stat_name = namebuf;
948 else
949 stat_name++;
950 if (strrchr(stat_name, '\\') != NULL)
951 stat_name = strrchr(stat_name, '\\') + 1;
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 */