Extensive changes that _should_ fix the socket buffering problems,
[u/mdw/putty] / scp.c
1 /*
2 * scp.c - Scp (Secure Copy) client for PuTTY.
3 * Joris van Rantwijk, Simon Tatham
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.
7 *
8 * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000
9 */
10
11 #include <windows.h>
12 #ifndef AUTO_WINSOCK
13 #ifdef WINSOCK_TWO
14 #include <winsock2.h>
15 #else
16 #include <winsock.h>
17 #endif
18 #endif
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <assert.h>
24 /* GUI Adaptation - Sept 2000 */
25 #include <winuser.h>
26 #include <winbase.h>
27
28 #define PUTTY_DO_GLOBALS
29 #include "putty.h"
30 #include "winstuff.h"
31 #include "storage.h"
32
33 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
34 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
35 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
36 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
37
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
49 static int list = 0;
50 static int verbose = 0;
51 static int recursive = 0;
52 static int preserve = 0;
53 static int targetshouldbedirectory = 0;
54 static int statistics = 1;
55 static int portnumber = 0;
56 static int prev_stats_len = 0;
57 static char *password = NULL;
58 static int errs = 0;
59 /* GUI Adaptation - Sept 2000 */
60 #define NAME_STR_MAX 2048
61 static char statname[NAME_STR_MAX + 1];
62 static unsigned long statsize = 0;
63 static int statperct = 0;
64 static unsigned long statelapsed = 0;
65 static int gui_mode = 0;
66 static char *gui_hwnd = NULL;
67
68 static void source(char *src);
69 static void rsource(char *src);
70 static void sink(char *targ, char *src);
71 /* GUI Adaptation - Sept 2000 */
72 static void tell_char(FILE * stream, char c);
73 static void tell_str(FILE * stream, char *str);
74 static void tell_user(FILE * stream, char *fmt, ...);
75 static void gui_update_stats(char *name, unsigned long size,
76 int percentage, unsigned long elapsed);
77
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
84 void logevent(char *string)
85 {
86 }
87
88 void ldisc_send(char *buf, int len)
89 {
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
99 void verify_ssh_host_key(char *host, int port, char *keytype,
100 char *keystr, char *fingerprint)
101 {
102 int ret;
103 HANDLE hin;
104 DWORD savemode, i;
105
106 static const char absentmsg[] =
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"
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) ";
119
120 static const char wrongmsg[] =
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"
130 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
131 "If you want to carry on connecting but without updating\n"
132 "the cache, enter \"n\".\n"
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) ";
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
147 if (ret == 0) /* success - key matched OK */
148 return;
149
150 if (ret == 2) { /* key was different */
151 fprintf(stderr, wrongmsg, fingerprint);
152 fflush(stderr);
153 }
154 if (ret == 1) { /* key was absent */
155 fprintf(stderr, absentmsg, fingerprint);
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')
168 store_host_key(host, port, keytype, keystr);
169 } else {
170 fprintf(stderr, abandoned);
171 exit(0);
172 }
173 }
174
175 /* GUI Adaptation - Sept 2000 */
176 static void send_msg(HWND h, UINT message, WPARAM wParam)
177 {
178 while (!PostMessage(h, message, wParam, 0))
179 SleepEx(1000, TRUE);
180 }
181
182 static void tell_char(FILE * stream, char c)
183 {
184 if (!gui_mode)
185 fputc(c, stream);
186 else {
187 unsigned int msg_id = WM_STD_OUT_CHAR;
188 if (stream == stderr)
189 msg_id = WM_STD_ERR_CHAR;
190 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
191 }
192 }
193
194 static void tell_str(FILE * stream, char *str)
195 {
196 unsigned int i;
197
198 for (i = 0; i < strlen(str); ++i)
199 tell_char(stream, str[i]);
200 }
201
202 static void tell_user(FILE * stream, char *fmt, ...)
203 {
204 char str[0x100]; /* Make the size big enough */
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
213 static void gui_update_stats(char *name, unsigned long size,
214 int percentage, unsigned long elapsed)
215 {
216 unsigned int i;
217
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);
224 }
225 if (statsize != size) {
226 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
227 statsize = size;
228 }
229 if (statelapsed != elapsed) {
230 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
231 (WPARAM) elapsed);
232 statelapsed = elapsed;
233 }
234 if (statperct != percentage) {
235 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
236 (WPARAM) percentage);
237 statperct = percentage;
238 }
239 }
240
241 /*
242 * Print an error message and perform a fatal exit.
243 */
244 void fatalbox(char *fmt, ...)
245 {
246 char str[0x100]; /* Make the size big enough */
247 va_list ap;
248 va_start(ap, fmt);
249 strcpy(str, "Fatal:");
250 vsprintf(str + strlen(str), fmt, ap);
251 va_end(ap);
252 strcat(str, "\n");
253 tell_str(stderr, str);
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 }
264
265 exit(1);
266 }
267 void connection_fatal(char *fmt, ...)
268 {
269 char str[0x100]; /* Make the size big enough */
270 va_list ap;
271 va_start(ap, fmt);
272 strcpy(str, "Fatal:");
273 vsprintf(str + strlen(str), fmt, ap);
274 va_end(ap);
275 strcat(str, "\n");
276 tell_str(stderr, str);
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 }
287
288 exit(1);
289 }
290
291 /*
292 * Be told what socket we're supposed to be using.
293 */
294 static SOCKET scp_ssh_socket;
295 char *do_select(SOCKET skt, int startup)
296 {
297 if (startup)
298 scp_ssh_socket = skt;
299 else
300 scp_ssh_socket = INVALID_SOCKET;
301 return NULL;
302 }
303 extern int select_result(WPARAM, LPARAM);
304
305 /*
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
310 * own trap in from_backend() to catch the data that comes back. We
311 * do this until we have enough data.
312 */
313
314 static unsigned char *outptr; /* where to put the data */
315 static unsigned outlen; /* how much data required */
316 static unsigned char *pending = NULL; /* any spare data */
317 static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
318 int from_backend(int is_stderr, char *data, int datalen)
319 {
320 unsigned char *p = (unsigned char *) data;
321 unsigned len = (unsigned) datalen;
322
323 /*
324 * stderr data is just spouted to local stderr and otherwise
325 * ignored.
326 */
327 if (is_stderr) {
328 fwrite(data, 1, len, stderr);
329 return 0;
330 }
331
332 inbuf_head = 0;
333
334 /*
335 * If this is before the real session begins, just return.
336 */
337 if (!outptr)
338 return 0;
339
340 if (outlen > 0) {
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;
349 }
350
351 if (len > 0) {
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;
361 }
362
363 return 0;
364 }
365 static 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;
375 }
376 static int ssh_scp_recv(unsigned char *buf, int len)
377 {
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) {
386 unsigned pendused = pendlen;
387 if (pendused > outlen)
388 pendused = outlen;
389 memcpy(outptr, pending, pendused);
390 memmove(pending, pending + pendused, pendlen - pendused);
391 outptr += pendused;
392 outlen -= pendused;
393 pendlen -= pendused;
394 if (pendlen == 0) {
395 pendsize = 0;
396 sfree(pending);
397 pending = NULL;
398 }
399 if (outlen == 0)
400 return len;
401 }
402
403 while (outlen > 0) {
404 if (!scp_process_network_event())
405 return 0; /* doom */
406 }
407
408 return len;
409 }
410
411 /*
412 * Loop through the ssh connection and authentication process.
413 */
414 static void ssh_scp_init(void)
415 {
416 if (scp_ssh_socket == INVALID_SOCKET)
417 return;
418 while (!back->sendok()) {
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);
425 }
426 }
427
428 /*
429 * Print an error message and exit after closing the SSH link.
430 */
431 static void bump(char *fmt, ...)
432 {
433 char str[0x100]; /* Make the size big enough */
434 va_list ap;
435 va_start(ap, fmt);
436 strcpy(str, "Fatal:");
437 vsprintf(str + strlen(str), fmt, ap);
438 va_end(ap);
439 strcat(str, "\n");
440 tell_str(stderr, str);
441 errs++;
442
443 if (back != NULL && back->socket() != NULL) {
444 char ch;
445 back->special(TS_EOF);
446 ssh_scp_recv(&ch, 1);
447 }
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
458 exit(1);
459 }
460
461 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
462 {
463 HANDLE hin, hout;
464 DWORD savemode, newmode, i;
465
466 if (is_pw && password) {
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 }
477 }
478
479 /* GUI Adaptation - Sept 2000 */
480 if (gui_mode) {
481 if (maxlen > 0)
482 str[0] = '\0';
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");
488
489 GetConsoleMode(hin, &savemode);
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);
496
497 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
498 ReadFile(hin, str, maxlen - 1, &i, NULL);
499
500 SetConsoleMode(hin, savemode);
501
502 if ((int) i > maxlen)
503 i = maxlen - 1;
504 else
505 i = i - 2;
506 str[i] = '\0';
507
508 if (is_pw)
509 WriteFile(hout, "\r\n", 2, &i, NULL);
510 }
511
512 return 1;
513 }
514
515 /*
516 * Open an SSH connection to user@host and execute cmd.
517 */
518 static void do_cmd(char *host, char *user, char *cmd)
519 {
520 char *err, *realhost;
521 DWORD namelen;
522
523 if (host == NULL || host[0] == '\0')
524 bump("Empty host name");
525
526 /* Try to load settings for this host */
527 do_defaults(host, &cfg);
528 if (cfg.host[0] == '\0') {
529 /* No settings for this host; use defaults */
530 do_defaults(NULL, &cfg);
531 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
532 cfg.host[sizeof(cfg.host) - 1] = '\0';
533 cfg.port = 22;
534 }
535
536 /* Set username */
537 if (user != NULL && user[0] != '\0') {
538 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
539 cfg.username[sizeof(cfg.username) - 1] = '\0';
540 } else if (cfg.username[0] == '\0') {
541 namelen = 0;
542 if (GetUserName(user, &namelen) == FALSE)
543 bump("Empty user name");
544 user = smalloc(namelen * sizeof(char));
545 GetUserName(user, &namelen);
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';
550 free(user);
551 }
552
553 if (cfg.protocol != PROT_SSH)
554 cfg.port = 22;
555
556 if (portnumber)
557 cfg.port = portnumber;
558
559 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
560 cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
561 cfg.nopty = TRUE;
562
563 back = &ssh_backend;
564
565 err = back->init(cfg.host, cfg.port, &realhost);
566 if (err != NULL)
567 bump("ssh_init: %s", err);
568 ssh_scp_init();
569 if (verbose && realhost != NULL)
570 tell_user(stderr, "Connected to %s\n", realhost);
571 sfree(realhost);
572 }
573
574 /*
575 * Update statistic information about current file.
576 */
577 static void print_stats(char *name, unsigned long size, unsigned long done,
578 time_t start, time_t now)
579 {
580 float ratebs;
581 unsigned long eta;
582 char etastr[10];
583 int pct;
584 int len;
585
586 /* GUI Adaptation - Sept 2000 */
587 if (gui_mode)
588 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
589 (unsigned long) difftime(now, start));
590 else {
591 if (now > start)
592 ratebs = (float) done / (now - start);
593 else
594 ratebs = (float) done;
595
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);
602
603 pct = (int) (100.0 * (float) done / size);
604
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;
610
611 if (done == size)
612 printf("\n");
613 }
614 }
615
616 /*
617 * Find a colon in str and return a pointer to the colon.
618 * This is used to separate hostname from filename.
619 */
620 static char *colon(char *str)
621 {
622 /* We ignore a leading colon, since the hostname cannot be
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] == ':')
626 return (NULL);
627 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
628 str++;
629 if (*str == ':')
630 return (str);
631 else
632 return (NULL);
633 }
634
635 /*
636 * Wait for a response from the other side.
637 * Return 0 if ok, -1 if error.
638 */
639 static int response(void)
640 {
641 char ch, resp, rbuf[2048];
642 int p;
643
644 if (ssh_scp_recv(&resp, 1) <= 0)
645 bump("Lost connection");
646
647 p = 0;
648 switch (resp) {
649 case 0: /* ok */
650 return (0);
651 default:
652 rbuf[p++] = resp;
653 /* fallthrough */
654 case 1: /* error */
655 case 2: /* fatal error */
656 do {
657 if (ssh_scp_recv(&ch, 1) <= 0)
658 bump("Protocol error: Lost connection");
659 rbuf[p++] = ch;
660 } while (p < sizeof(rbuf) && ch != '\n');
661 rbuf[p - 1] = '\0';
662 if (resp == 1)
663 tell_user(stderr, "%s\n", rbuf);
664 else
665 bump("%s", rbuf);
666 errs++;
667 return (-1);
668 }
669 }
670
671 /*
672 * Send an error message to the other side and to the screen.
673 * Increment error counter.
674 */
675 static void run_err(const char *fmt, ...)
676 {
677 char str[2048];
678 va_list ap;
679 va_start(ap, fmt);
680 errs++;
681 strcpy(str, "scp: ");
682 vsprintf(str + strlen(str), fmt, ap);
683 strcat(str, "\n");
684 back->send("\001", 1); /* scp protocol error prefix */
685 back->send(str, strlen(str));
686 tell_user(stderr, "%s", str);
687 va_end(ap);
688 }
689
690 /*
691 * Execute the source part of the SCP protocol.
692 */
693 static void source(char *src)
694 {
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);
705 if (attr == (DWORD) - 1) {
706 run_err("%s: No such file or directory", src);
707 return;
708 }
709
710 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
711 if (recursive) {
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 {
728 run_err("%s: not a regular file", src);
729 }
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) {
745 run_err("%s: Cannot open file", src);
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);
756 back->send(buf, strlen(buf));
757 if (response())
758 return;
759 }
760
761 size = GetFileSize(f, NULL);
762 sprintf(buf, "C0644 %lu %s\n", size, last);
763 if (verbose)
764 tell_user(stderr, "Sending file modes: %s", buf);
765 back->send(buf, strlen(buf));
766 if (response())
767 return;
768
769 stat_bytes = 0;
770 stat_starttime = time(NULL);
771 stat_lasttime = 0;
772
773 for (i = 0; i < size; i += 4096) {
774 char transbuf[4096];
775 DWORD j, k = 4096;
776 int bufsize;
777
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");
783 bump("%s: Read error", src);
784 }
785 bufsize = back->send(transbuf, k);
786 if (statistics) {
787 stat_bytes += k;
788 if (time(NULL) != stat_lasttime || i + k == size) {
789 stat_lasttime = time(NULL);
790 print_stats(last, size, stat_bytes,
791 stat_starttime, stat_lasttime);
792 }
793 }
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 }
806 }
807 CloseHandle(f);
808
809 back->send("", 1);
810 (void) response();
811 }
812
813 /*
814 * Recursively send the contents of a directory.
815 */
816 static void rsource(char *src)
817 {
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)
837 tell_user(stderr, "Entering directory: %s", buf);
838 back->send(buf, strlen(buf));
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) {
848 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
849 run_err("%s/%s: Name too long", src, fdat.cFileName);
850 } else {
851 sprintf(buf, "%s/%s", src, fdat.cFileName);
852 source(buf);
853 }
854 ok = FindNextFile(dir, &fdat);
855 }
856 FindClose(dir);
857
858 sprintf(buf, "E\n");
859 back->send(buf, strlen(buf));
860 (void) response();
861 }
862
863 /*
864 * Execute the sink part of the SCP protocol.
865 */
866 static void sink(char *targ, char *src)
867 {
868 char buf[2048];
869 char namebuf[2048];
870 char ch;
871 int targisdir = 0;
872 int settime;
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);
885 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
886 targisdir = 1;
887
888 if (targetshouldbedirectory && !targisdir)
889 bump("%s: Not a directory", targ);
890
891 back->send("", 1);
892 while (1) {
893 settime = 0;
894 gottime:
895 if (ssh_scp_recv(&ch, 1) <= 0)
896 return;
897 if (ch == '\n')
898 bump("Protocol error: Unexpected newline");
899 i = 0;
900 buf[i++] = ch;
901 do {
902 if (ssh_scp_recv(&ch, 1) <= 0)
903 bump("Lost connection");
904 buf[i++] = ch;
905 } while (i < sizeof(buf) && ch != '\n');
906 buf[i - 1] = '\0';
907 switch (buf[0]) {
908 case '\01': /* error */
909 tell_user(stderr, "%s\n", buf + 1);
910 errs++;
911 continue;
912 case '\02': /* fatal error */
913 bump("%s", buf + 1);
914 case 'E':
915 back->send("", 1);
916 return;
917 case 'T':
918 if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
919 settime = 1;
920 back->send("", 1);
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 }
930
931 if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
932 bump("Protocol error: Illegal file descriptor format");
933 /* Security fix: ensure the file ends up where we asked for it. */
934 if (targisdir) {
935 char t[2048];
936 char *p;
937 strcpy(t, targ);
938 if (targ[0] != '\0')
939 strcat(t, "/");
940 p = namebuf + strlen(namebuf);
941 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
942 p--;
943 strcat(t, p);
944 strcpy(namebuf, t);
945 } else {
946 strcpy(namebuf, targ);
947 }
948 attr = GetFileAttributes(namebuf);
949 exists = (attr != (DWORD) - 1);
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) {
957 if (!CreateDirectory(namebuf, NULL)) {
958 run_err("%s: Cannot create directory", namebuf);
959 continue;
960 }
961 }
962 sink(namebuf, NULL);
963 /* can we set the timestamp for directories ? */
964 continue;
965 }
966
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 }
973
974 back->send("", 1);
975
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;
985
986 for (i = 0; i < size; i += 4096) {
987 char transbuf[4096];
988 DWORD j, k = 4096;
989 if (i + k > size)
990 k = size - i;
991 if (ssh_scp_recv(transbuf, k) == 0)
992 bump("Lost connection");
993 if (wrerror)
994 continue;
995 if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
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;
1005 if (time(NULL) > stat_lasttime || i + k == size) {
1006 stat_lasttime = time(NULL);
1007 print_stats(stat_name, size, stat_bytes,
1008 stat_starttime, stat_lasttime);
1009 }
1010 }
1011 }
1012 (void) response();
1013
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);
1019 }
1020
1021 CloseHandle(f);
1022 if (wrerror) {
1023 run_err("%s: Write error", namebuf);
1024 continue;
1025 }
1026 back->send("", 1);
1027 }
1028 }
1029
1030 /*
1031 * We will copy local files to a remote server.
1032 */
1033 static void toremote(int argc, char *argv[])
1034 {
1035 char *src, *targ, *host, *user;
1036 char *cmd;
1037 int i;
1038
1039 targ = argv[argc - 1];
1040
1041 /* Separate host from filename */
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
1051 /* Separate host and username */
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
1065 if so, we should set the targetshouldbedirectory flag */
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" : "",
1083 targetshouldbedirectory ? " -d" : "", targ);
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) {
1094 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1095 errs++;
1096 continue;
1097 }
1098 dir = FindFirstFile(src, &fdat);
1099 if (dir == INVALID_HANDLE_VALUE) {
1100 run_err("%s: No such file or directory", src);
1101 continue;
1102 }
1103 do {
1104 char *last;
1105 char namebuf[2048];
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)) {
1120 /* ok */ ;
1121 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1122 !strcmp(src + len - dlen, fdat.cFileName)) {
1123 /* ok */ ;
1124 } else
1125 continue; /* ignore this one */
1126 }
1127 if (strlen(src) + strlen(fdat.cFileName) >= sizeof(namebuf)) {
1128 tell_user(stderr, "%s: Name too long", src);
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 }
1145 }
1146
1147 /*
1148 * We will copy files from a remote server to the local machine.
1149 */
1150 static void tolocal(int argc, char *argv[])
1151 {
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
1161 /* Separate host from filename */
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
1171 /* Separate username and hostname */
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" : "",
1188 targetshouldbedirectory ? " -d" : "", src);
1189 do_cmd(host, user, cmd);
1190 sfree(cmd);
1191
1192 sink(targ, src);
1193 }
1194
1195 /*
1196 * We will issue a list command to get a remote directory.
1197 */
1198 static 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
1228 cmd = smalloc(4 * strlen(src) + 100);
1229 strcpy(cmd, "ls -la '");
1230 p = cmd + strlen(cmd);
1231 for (q = src; *q; q++) {
1232 if (*q == '\'') {
1233 *p++ = '\'';
1234 *p++ = '\\';
1235 *p++ = '\'';
1236 *p++ = '\'';
1237 } else {
1238 *p++ = *q;
1239 }
1240 }
1241 *p++ = '\'';
1242 *p = '\0';
1243
1244 do_cmd(host, user, cmd);
1245 sfree(cmd);
1246
1247 while (ssh_scp_recv(&c, 1) > 0)
1248 tell_char(stdout, c);
1249 }
1250
1251 /*
1252 * Initialize the Win$ock driver.
1253 */
1254 static void init_winsock(void)
1255 {
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");
1262 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
1263 bump("WinSock version is incompatible with 1.1");
1264 }
1265
1266 /*
1267 * Short description of parameters.
1268 */
1269 static void usage(void)
1270 {
1271 printf("PuTTY Secure Copy client\n");
1272 printf("%s\n", ver);
1273 printf("Usage: pscp [options] [user@]host:source target\n");
1274 printf
1275 (" pscp [options] source [source...] [user@]host:target\n");
1276 printf(" pscp [options] -ls user@host:filespec\n");
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");
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 */
1292 printf
1293 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1294 #endif
1295 exit(1);
1296 }
1297
1298 /*
1299 * Main program (no, really?)
1300 */
1301 int main(int argc, char *argv[])
1302 {
1303 int i;
1304
1305 default_protocol = PROT_TELNET;
1306
1307 flags = FLAG_STDERR;
1308 ssh_get_line = &get_line;
1309 init_winsock();
1310 sk_init();
1311
1312 for (i = 1; i < argc; i++) {
1313 if (argv[i][0] != '-')
1314 break;
1315 if (strcmp(argv[i], "-v") == 0)
1316 verbose = 1, flags |= FLAG_VERBOSE;
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;
1323 else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
1324 usage();
1325 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
1326 portnumber = atoi(argv[++i]);
1327 else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
1328 password = argv[++i];
1329 else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
1330 gui_hwnd = argv[++i];
1331 gui_mode = 1;
1332 } else if (strcmp(argv[i], "-ls") == 0)
1333 list = 1;
1334 else if (strcmp(argv[i], "--") == 0) {
1335 i++;
1336 break;
1337 } else
1338 usage();
1339 }
1340 argc -= i;
1341 argv += i;
1342 back = NULL;
1343
1344 if (list) {
1345 if (argc != 1)
1346 usage();
1347 get_dir_list(argc, argv);
1348
1349 } else {
1350
1351 if (argc < 2)
1352 usage();
1353 if (argc > 2)
1354 targetshouldbedirectory = 1;
1355
1356 if (colon(argv[argc - 1]) != NULL)
1357 toremote(argc, argv);
1358 else
1359 tolocal(argc, argv);
1360 }
1361
1362 if (back != NULL && back->socket() != NULL) {
1363 char ch;
1364 back->special(TS_EOF);
1365 ssh_scp_recv(&ch, 1);
1366 }
1367 WSACleanup();
1368 random_save_seed();
1369
1370 /* GUI Adaptation - August 2000 */
1371 if (gui_mode) {
1372 unsigned int msg_id = WM_RET_ERR_CNT;
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);
1378 }
1379 return (errs == 0 ? 0 : 1);
1380 }
1381
1382 /* end */