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