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