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