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