Bug fix: line discipline selection is not enabled until after ssh
[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 #include <winsock.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <time.h>
17 /* GUI Adaptation - Sept 2000 */
18 #include <winuser.h>
19 #include <winbase.h>
20
21 #define PUTTY_DO_GLOBALS
22 #include "putty.h"
23 #include "scp.h"
24
25 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
26 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
27 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
28 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
29
30 /* GUI Adaptation - Sept 2000 */
31 #define WM_APP_BASE 0x8000
32 #define WM_STD_OUT_CHAR ( WM_APP_BASE+400 )
33 #define WM_STD_ERR_CHAR ( WM_APP_BASE+401 )
34 #define WM_STATS_CHAR ( WM_APP_BASE+402 )
35 #define WM_STATS_SIZE ( WM_APP_BASE+403 )
36 #define WM_STATS_PERCENT ( WM_APP_BASE+404 )
37 #define WM_STATS_ELAPSED ( WM_APP_BASE+405 )
38 #define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
39 #define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
40
41 static int verbose = 0;
42 static int recursive = 0;
43 static int preserve = 0;
44 static int targetshouldbedirectory = 0;
45 static int statistics = 1;
46 static int portnumber = 0;
47 static char *password = NULL;
48 static int errs = 0;
49 static int connection_open = 0;
50 /* GUI Adaptation - Sept 2000 */
51 #define NAME_STR_MAX 2048
52 static char statname[NAME_STR_MAX+1];
53 static unsigned long statsize = 0;
54 static int statperct = 0;
55 static time_t statelapsed = 0;
56 static int gui_mode = 0;
57 static char *gui_hwnd = NULL;
58
59 static void source(char *src);
60 static void rsource(char *src);
61 static void sink(char *targ);
62 /* GUI Adaptation - Sept 2000 */
63 static void tell_char(FILE *stream, char c);
64 static void tell_str(FILE *stream, char *str);
65 static void tell_user(FILE *stream, char *fmt, ...);
66 static void send_char_msg(unsigned int msg_id, char c);
67 static void send_str_msg(unsigned int msg_id, char *str);
68 static void gui_update_stats(char *name, unsigned long size, int percentage, time_t elapsed);
69
70 /*
71 * These functions are needed to link with ssh.c, but never get called.
72 */
73 void term_out(void)
74 {
75 abort();
76 }
77 void begin_session(void) {
78 }
79
80 /* GUI Adaptation - Sept 2000 */
81 void send_msg(HWND h, UINT message, WPARAM wParam)
82 {
83 while (!PostMessage( h, message, wParam, 0))
84 SleepEx(1000,TRUE);
85 }
86
87 void tell_char(FILE *stream, char c)
88 {
89 if (!gui_mode)
90 fputc(c, stream);
91 else
92 {
93 unsigned int msg_id = WM_STD_OUT_CHAR;
94 if (stream = stderr) msg_id = WM_STD_ERR_CHAR;
95 send_msg( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)c );
96 }
97 }
98
99 void tell_str(FILE *stream, char *str)
100 {
101 unsigned int i;
102
103 for( i = 0; i < strlen(str); ++i )
104 tell_char(stream, str[i]);
105 }
106
107 void tell_user(FILE *stream, char *fmt, ...)
108 {
109 char str[0x100]; /* Make the size big enough */
110 va_list ap;
111 va_start(ap, fmt);
112 vsprintf(str, fmt, ap);
113 va_end(ap);
114 strcat(str, "\n");
115 tell_str(stream, str);
116 }
117
118 void gui_update_stats(char *name, unsigned long size, int percentage, time_t elapsed)
119 {
120 unsigned int i;
121
122 if (strcmp(name,statname) != 0)
123 {
124 for( i = 0; i < strlen(name); ++i )
125 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)name[i]);
126 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)'\n' );
127 strcpy(statname,name);
128 }
129 if (statsize != size)
130 {
131 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM)size );
132 statsize = size;
133 }
134 if (statelapsed != elapsed)
135 {
136 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM)elapsed );
137 statelapsed = elapsed;
138 }
139 if (statperct != percentage)
140 {
141 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_PERCENT, (WPARAM)percentage );
142 statperct = percentage;
143 }
144 }
145
146 /*
147 * Print an error message and perform a fatal exit.
148 */
149 void fatalbox(char *fmt, ...)
150 {
151 char str[0x100]; /* Make the size big enough */
152 va_list ap;
153 va_start(ap, fmt);
154 strcpy(str, "Fatal:");
155 vsprintf(str+strlen(str), fmt, ap);
156 va_end(ap);
157 strcat(str, "\n");
158 tell_str(stderr, str);
159
160 exit(1);
161 }
162 void connection_fatal(char *fmt, ...)
163 {
164 char str[0x100]; /* Make the size big enough */
165 va_list ap;
166 va_start(ap, fmt);
167 strcpy(str, "Fatal:");
168 vsprintf(str+strlen(str), fmt, ap);
169 va_end(ap);
170 strcat(str, "\n");
171 tell_str(stderr, str);
172
173 exit(1);
174 }
175
176 /*
177 * Print an error message and exit after closing the SSH link.
178 */
179 static void bump(char *fmt, ...)
180 {
181 char str[0x100]; /* Make the size big enough */
182 va_list ap;
183 va_start(ap, fmt);
184 strcpy(str, "Fatal:");
185 vsprintf(str+strlen(str), fmt, ap);
186 va_end(ap);
187 strcat(str, "\n");
188 tell_str(stderr, str);
189
190 if (connection_open) {
191 char ch;
192 ssh_scp_send_eof();
193 ssh_scp_recv(&ch, 1);
194 }
195 exit(1);
196 }
197
198 static int get_password(const char *prompt, char *str, int maxlen)
199 {
200 HANDLE hin, hout;
201 DWORD savemode, i;
202
203 if (password) {
204 static int tried_once = 0;
205
206 if (tried_once) {
207 return 0;
208 } else {
209 strncpy(str, password, maxlen);
210 str[maxlen-1] = '\0';
211 tried_once = 1;
212 return 1;
213 }
214 }
215
216 /* GUI Adaptation - Sept 2000 */
217 if (gui_mode) {
218 if (maxlen>0) str[0] = '\0';
219 } else {
220 hin = GetStdHandle(STD_INPUT_HANDLE);
221 hout = GetStdHandle(STD_OUTPUT_HANDLE);
222 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
223 bump("Cannot get standard input/output handles");
224
225 GetConsoleMode(hin, &savemode);
226 SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
227 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
228
229 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
230 ReadFile(hin, str, maxlen-1, &i, NULL);
231
232 SetConsoleMode(hin, savemode);
233
234 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
235 str[i] = '\0';
236
237 WriteFile(hout, "\r\n", 2, &i, NULL);
238 }
239
240 return 1;
241 }
242
243 /*
244 * Open an SSH connection to user@host and execute cmd.
245 */
246 static void do_cmd(char *host, char *user, char *cmd)
247 {
248 char *err, *realhost;
249
250 if (host == NULL || host[0] == '\0')
251 bump("Empty host name");
252
253 /* Try to load settings for this host */
254 do_defaults(host);
255 if (cfg.host[0] == '\0') {
256 /* No settings for this host; use defaults */
257 strncpy(cfg.host, host, sizeof(cfg.host)-1);
258 cfg.host[sizeof(cfg.host)-1] = '\0';
259 cfg.port = 22;
260 }
261
262 /* Set username */
263 if (user != NULL && user[0] != '\0') {
264 strncpy(cfg.username, user, sizeof(cfg.username)-1);
265 cfg.username[sizeof(cfg.username)-1] = '\0';
266 } else if (cfg.username[0] == '\0') {
267 bump("Empty user name");
268 }
269
270 if (cfg.protocol != PROT_SSH)
271 cfg.port = 22;
272
273 if (portnumber)
274 cfg.port = portnumber;
275
276 err = ssh_scp_init(cfg.host, cfg.port, cmd, &realhost);
277 if (err != NULL)
278 bump("ssh_init: %s", err);
279 if (verbose && realhost != NULL)
280 tell_user(stderr, "Connected to %s\n", realhost);
281
282 connection_open = 1;
283 }
284
285 /*
286 * Update statistic information about current file.
287 */
288 static void print_stats(char *name, unsigned long size, unsigned long done,
289 time_t start, time_t now)
290 {
291 float ratebs;
292 unsigned long eta;
293 char etastr[10];
294 int pct;
295
296 /* GUI Adaptation - Sept 2000 */
297 if (gui_mode)
298 gui_update_stats(name, size, ((done *100) / size), now-start);
299 else {
300 if (now > start)
301 ratebs = (float) done / (now - start);
302 else
303 ratebs = (float) done;
304
305 if (ratebs < 1.0)
306 eta = size - done;
307 else
308 eta = (unsigned long) ((size - done) / ratebs);
309 sprintf(etastr, "%02ld:%02ld:%02ld",
310 eta / 3600, (eta % 3600) / 60, eta % 60);
311
312 pct = (int) (100.0 * (float) done / size);
313
314 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
315 name, done / 1024, ratebs / 1024.0,
316 etastr, pct);
317
318 if (done == size)
319 printf("\n");
320 }
321 }
322
323 /*
324 * Find a colon in str and return a pointer to the colon.
325 * This is used to separate hostname from filename.
326 */
327 static char * colon(char *str)
328 {
329 /* We ignore a leading colon, since the hostname cannot be
330 empty. We also ignore a colon as second character because
331 of filenames like f:myfile.txt. */
332 if (str[0] == '\0' ||
333 str[0] == ':' ||
334 str[1] == ':')
335 return (NULL);
336 while (*str != '\0' &&
337 *str != ':' &&
338 *str != '/' &&
339 *str != '\\')
340 str++;
341 if (*str == ':')
342 return (str);
343 else
344 return (NULL);
345 }
346
347 /*
348 * Wait for a response from the other side.
349 * Return 0 if ok, -1 if error.
350 */
351 static int response(void)
352 {
353 char ch, resp, rbuf[2048];
354 int p;
355
356 if (ssh_scp_recv(&resp, 1) <= 0)
357 bump("Lost connection");
358
359 p = 0;
360 switch (resp) {
361 case 0: /* ok */
362 return (0);
363 default:
364 rbuf[p++] = resp;
365 /* fallthrough */
366 case 1: /* error */
367 case 2: /* fatal error */
368 do {
369 if (ssh_scp_recv(&ch, 1) <= 0)
370 bump("Protocol error: Lost connection");
371 rbuf[p++] = ch;
372 } while (p < sizeof(rbuf) && ch != '\n');
373 rbuf[p-1] = '\0';
374 if (resp == 1)
375 tell_user(stderr, "%s\n", rbuf);
376 else
377 bump("%s", rbuf);
378 errs++;
379 return (-1);
380 }
381 }
382
383 /*
384 * Send an error message to the other side and to the screen.
385 * Increment error counter.
386 */
387 static void run_err(const char *fmt, ...)
388 {
389 char str[2048];
390 va_list ap;
391 va_start(ap, fmt);
392 errs++;
393 strcpy(str, "\01scp: ");
394 vsprintf(str+strlen(str), fmt, ap);
395 strcat(str, "\n");
396 ssh_scp_send(str, strlen(str));
397 tell_user(stderr, "%s",str);
398 va_end(ap);
399 }
400
401 /*
402 * Execute the source part of the SCP protocol.
403 */
404 static void source(char *src)
405 {
406 char buf[2048];
407 unsigned long size;
408 char *last;
409 HANDLE f;
410 DWORD attr;
411 unsigned long i;
412 unsigned long stat_bytes;
413 time_t stat_starttime, stat_lasttime;
414
415 attr = GetFileAttributes(src);
416 if (attr == (DWORD)-1) {
417 run_err("%s: No such file or directory", src);
418 return;
419 }
420
421 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
422 if (recursive) {
423 /*
424 * Avoid . and .. directories.
425 */
426 char *p;
427 p = strrchr(src, '/');
428 if (!p)
429 p = strrchr(src, '\\');
430 if (!p)
431 p = src;
432 else
433 p++;
434 if (!strcmp(p, ".") || !strcmp(p, ".."))
435 /* skip . and .. */;
436 else
437 rsource(src);
438 } else {
439 run_err("%s: not a regular file", src);
440 }
441 return;
442 }
443
444 if ((last = strrchr(src, '/')) == NULL)
445 last = src;
446 else
447 last++;
448 if (strrchr(last, '\\') != NULL)
449 last = strrchr(last, '\\') + 1;
450 if (last == src && strchr(src, ':') != NULL)
451 last = strchr(src, ':') + 1;
452
453 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
454 OPEN_EXISTING, 0, 0);
455 if (f == INVALID_HANDLE_VALUE) {
456 run_err("%s: Cannot open file", src);
457 return;
458 }
459
460 if (preserve) {
461 FILETIME actime, wrtime;
462 unsigned long mtime, atime;
463 GetFileTime(f, NULL, &actime, &wrtime);
464 TIME_WIN_TO_POSIX(actime, atime);
465 TIME_WIN_TO_POSIX(wrtime, mtime);
466 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
467 ssh_scp_send(buf, strlen(buf));
468 if (response())
469 return;
470 }
471
472 size = GetFileSize(f, NULL);
473 sprintf(buf, "C0644 %lu %s\n", size, last);
474 if (verbose)
475 tell_user(stderr, "Sending file modes: %s", buf);
476 ssh_scp_send(buf, strlen(buf));
477 if (response())
478 return;
479
480 if (statistics) {
481 stat_bytes = 0;
482 stat_starttime = time(NULL);
483 stat_lasttime = 0;
484 }
485
486 for (i = 0; i < size; i += 4096) {
487 char transbuf[4096];
488 DWORD j, k = 4096;
489 if (i + k > size) k = size - i;
490 if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) {
491 if (statistics) printf("\n");
492 bump("%s: Read error", src);
493 }
494 ssh_scp_send(transbuf, k);
495 if (statistics) {
496 stat_bytes += k;
497 if (time(NULL) != stat_lasttime ||
498 i + k == size) {
499 stat_lasttime = time(NULL);
500 print_stats(last, size, stat_bytes,
501 stat_starttime, stat_lasttime);
502 }
503 }
504 }
505 CloseHandle(f);
506
507 ssh_scp_send("", 1);
508 (void) response();
509 }
510
511 /*
512 * Recursively send the contents of a directory.
513 */
514 static void rsource(char *src)
515 {
516 char buf[2048];
517 char *last;
518 HANDLE dir;
519 WIN32_FIND_DATA fdat;
520 int ok;
521
522 if ((last = strrchr(src, '/')) == NULL)
523 last = src;
524 else
525 last++;
526 if (strrchr(last, '\\') != NULL)
527 last = strrchr(last, '\\') + 1;
528 if (last == src && strchr(src, ':') != NULL)
529 last = strchr(src, ':') + 1;
530
531 /* maybe send filetime */
532
533 sprintf(buf, "D0755 0 %s\n", last);
534 if (verbose)
535 tell_user(stderr, "Entering directory: %s", buf);
536 ssh_scp_send(buf, strlen(buf));
537 if (response())
538 return;
539
540 sprintf(buf, "%s/*", src);
541 dir = FindFirstFile(buf, &fdat);
542 ok = (dir != INVALID_HANDLE_VALUE);
543 while (ok) {
544 if (strcmp(fdat.cFileName, ".") == 0 ||
545 strcmp(fdat.cFileName, "..") == 0) {
546 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >=
547 sizeof(buf)) {
548 run_err("%s/%s: Name too long", src, fdat.cFileName);
549 } else {
550 sprintf(buf, "%s/%s", src, fdat.cFileName);
551 source(buf);
552 }
553 ok = FindNextFile(dir, &fdat);
554 }
555 FindClose(dir);
556
557 sprintf(buf, "E\n");
558 ssh_scp_send(buf, strlen(buf));
559 (void) response();
560 }
561
562 /*
563 * Execute the sink part of the SCP protocol.
564 */
565 static void sink(char *targ)
566 {
567 char buf[2048];
568 char namebuf[2048];
569 char ch;
570 int targisdir = 0;
571 int settime;
572 int exists;
573 DWORD attr;
574 HANDLE f;
575 unsigned long mtime, atime;
576 unsigned int mode;
577 unsigned long size, i;
578 int wrerror = 0;
579 unsigned long stat_bytes;
580 time_t stat_starttime, stat_lasttime;
581 char *stat_name;
582
583 attr = GetFileAttributes(targ);
584 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
585 targisdir = 1;
586
587 if (targetshouldbedirectory && !targisdir)
588 bump("%s: Not a directory", targ);
589
590 ssh_scp_send("", 1);
591 while (1) {
592 settime = 0;
593 gottime:
594 if (ssh_scp_recv(&ch, 1) <= 0)
595 return;
596 if (ch == '\n')
597 bump("Protocol error: Unexpected newline");
598 i = 0;
599 buf[i++] = ch;
600 do {
601 if (ssh_scp_recv(&ch, 1) <= 0)
602 bump("Lost connection");
603 buf[i++] = ch;
604 } while (i < sizeof(buf) && ch != '\n');
605 buf[i-1] = '\0';
606 switch (buf[0]) {
607 case '\01': /* error */
608 tell_user(stderr, "%s\n", buf+1);
609 errs++;
610 continue;
611 case '\02': /* fatal error */
612 bump("%s", buf+1);
613 case 'E':
614 ssh_scp_send("", 1);
615 return;
616 case 'T':
617 if (sscanf(buf, "T%ld %*d %ld %*d",
618 &mtime, &atime) == 2) {
619 settime = 1;
620 ssh_scp_send("", 1);
621 goto gottime;
622 }
623 bump("Protocol error: Illegal time format");
624 case 'C':
625 case 'D':
626 break;
627 default:
628 bump("Protocol error: Expected control record");
629 }
630
631 if (sscanf(buf+1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
632 bump("Protocol error: Illegal file descriptor format");
633 if (targisdir) {
634 char t[2048];
635 strcpy(t, targ);
636 if (targ[0] != '\0')
637 strcat(t, "/");
638 strcat(t, namebuf);
639 strcpy(namebuf, t);
640 } else {
641 strcpy(namebuf, targ);
642 }
643 attr = GetFileAttributes(namebuf);
644 exists = (attr != (DWORD)-1);
645
646 if (buf[0] == 'D') {
647 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
648 run_err("%s: Not a directory", namebuf);
649 continue;
650 }
651 if (!exists) {
652 if (! CreateDirectory(namebuf, NULL)) {
653 run_err("%s: Cannot create directory",
654 namebuf);
655 continue;
656 }
657 }
658 sink(namebuf);
659 /* can we set the timestamp for directories ? */
660 continue;
661 }
662
663 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
664 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
665 if (f == INVALID_HANDLE_VALUE) {
666 run_err("%s: Cannot create file", namebuf);
667 continue;
668 }
669
670 ssh_scp_send("", 1);
671
672 if (statistics) {
673 stat_bytes = 0;
674 stat_starttime = time(NULL);
675 stat_lasttime = 0;
676 if ((stat_name = strrchr(namebuf, '/')) == NULL)
677 stat_name = namebuf;
678 else
679 stat_name++;
680 if (strrchr(stat_name, '\\') != NULL)
681 stat_name = strrchr(stat_name, '\\') + 1;
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 (ssh_scp_recv(transbuf, k) == 0)
689 bump("Lost connection");
690 if (wrerror) continue;
691 if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) {
692 wrerror = 1;
693 if (statistics)
694 printf("\r%-25.25s | %50s\n",
695 stat_name,
696 "Write error.. waiting for end of file");
697 continue;
698 }
699 if (statistics) {
700 stat_bytes += k;
701 if (time(NULL) > stat_lasttime ||
702 i + k == size) {
703 stat_lasttime = time(NULL);
704 print_stats(stat_name, size, stat_bytes,
705 stat_starttime, stat_lasttime);
706 }
707 }
708 }
709 (void) response();
710
711 if (settime) {
712 FILETIME actime, wrtime;
713 TIME_POSIX_TO_WIN(atime, actime);
714 TIME_POSIX_TO_WIN(mtime, wrtime);
715 SetFileTime(f, NULL, &actime, &wrtime);
716 }
717
718 CloseHandle(f);
719 if (wrerror) {
720 run_err("%s: Write error", namebuf);
721 continue;
722 }
723 ssh_scp_send("", 1);
724 }
725 }
726
727 /*
728 * We will copy local files to a remote server.
729 */
730 static void toremote(int argc, char *argv[])
731 {
732 char *src, *targ, *host, *user;
733 char *cmd;
734 int i;
735
736 targ = argv[argc-1];
737
738 /* Separate host from filename */
739 host = targ;
740 targ = colon(targ);
741 if (targ == NULL)
742 bump("targ == NULL in toremote()");
743 *targ++ = '\0';
744 if (*targ == '\0')
745 targ = ".";
746 /* Substitute "." for emtpy target */
747
748 /* Separate host and username */
749 user = host;
750 host = strrchr(host, '@');
751 if (host == NULL) {
752 host = user;
753 user = NULL;
754 } else {
755 *host++ = '\0';
756 if (*user == '\0')
757 user = NULL;
758 }
759
760 if (argc == 2) {
761 /* Find out if the source filespec covers multiple files
762 if so, we should set the targetshouldbedirectory flag */
763 HANDLE fh;
764 WIN32_FIND_DATA fdat;
765 if (colon(argv[0]) != NULL)
766 bump("%s: Remote to remote not supported", argv[0]);
767 fh = FindFirstFile(argv[0], &fdat);
768 if (fh == INVALID_HANDLE_VALUE)
769 bump("%s: No such file or directory\n", argv[0]);
770 if (FindNextFile(fh, &fdat))
771 targetshouldbedirectory = 1;
772 FindClose(fh);
773 }
774
775 cmd = smalloc(strlen(targ) + 100);
776 sprintf(cmd, "scp%s%s%s%s -t %s",
777 verbose ? " -v" : "",
778 recursive ? " -r" : "",
779 preserve ? " -p" : "",
780 targetshouldbedirectory ? " -d" : "",
781 targ);
782 do_cmd(host, user, cmd);
783 sfree(cmd);
784
785 (void) response();
786
787 for (i = 0; i < argc - 1; i++) {
788 HANDLE dir;
789 WIN32_FIND_DATA fdat;
790 src = argv[i];
791 if (colon(src) != NULL) {
792 tell_user(stderr, "%s: Remote to remote not supported\n", src);
793 errs++;
794 continue;
795 }
796 dir = FindFirstFile(src, &fdat);
797 if (dir == INVALID_HANDLE_VALUE) {
798 run_err("%s: No such file or directory", src);
799 continue;
800 }
801 do {
802 char *last;
803 char namebuf[2048];
804 if (strlen(src) + strlen(fdat.cFileName) >=
805 sizeof(namebuf)) {
806 tell_user(stderr, "%s: Name too long", src);
807 continue;
808 }
809 strcpy(namebuf, src);
810 if ((last = strrchr(namebuf, '/')) == NULL)
811 last = namebuf;
812 else
813 last++;
814 if (strrchr(last, '\\') != NULL)
815 last = strrchr(last, '\\') + 1;
816 if (last == namebuf && strrchr(namebuf, ':') != NULL)
817 last = strchr(namebuf, ':') + 1;
818 strcpy(last, fdat.cFileName);
819 source(namebuf);
820 } while (FindNextFile(dir, &fdat));
821 FindClose(dir);
822 }
823 }
824
825 /*
826 * We will copy files from a remote server to the local machine.
827 */
828 static void tolocal(int argc, char *argv[])
829 {
830 char *src, *targ, *host, *user;
831 char *cmd;
832
833 if (argc != 2)
834 bump("More than one remote source not supported");
835
836 src = argv[0];
837 targ = argv[1];
838
839 /* Separate host from filename */
840 host = src;
841 src = colon(src);
842 if (src == NULL)
843 bump("Local to local copy not supported");
844 *src++ = '\0';
845 if (*src == '\0')
846 src = ".";
847 /* Substitute "." for empty filename */
848
849 /* Separate username and hostname */
850 user = host;
851 host = strrchr(host, '@');
852 if (host == NULL) {
853 host = user;
854 user = NULL;
855 } else {
856 *host++ = '\0';
857 if (*user == '\0')
858 user = NULL;
859 }
860
861 cmd = smalloc(strlen(src) + 100);
862 sprintf(cmd, "scp%s%s%s%s -f %s",
863 verbose ? " -v" : "",
864 recursive ? " -r" : "",
865 preserve ? " -p" : "",
866 targetshouldbedirectory ? " -d" : "",
867 src);
868 do_cmd(host, user, cmd);
869 sfree(cmd);
870
871 sink(targ);
872 }
873
874 /*
875 * We will issue a list command to get a remote directory.
876 */
877 static void get_dir_list(int argc, char *argv[])
878 {
879 char *src, *host, *user;
880 char *cmd, *p, *q;
881 char c;
882
883 src = argv[0];
884
885 /* Separate host from filename */
886 host = src;
887 src = colon(src);
888 if (src == NULL)
889 bump("Local to local copy not supported");
890 *src++ = '\0';
891 if (*src == '\0')
892 src = ".";
893 /* Substitute "." for empty filename */
894
895 /* Separate username and hostname */
896 user = host;
897 host = strrchr(host, '@');
898 if (host == NULL) {
899 host = user;
900 user = NULL;
901 } else {
902 *host++ = '\0';
903 if (*user == '\0')
904 user = NULL;
905 }
906
907 cmd = smalloc(4*strlen(src) + 100);
908 strcpy(cmd, "ls -la '");
909 p = cmd + strlen(cmd);
910 for (q = src; *q; q++) {
911 if (*q == '\'') {
912 *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\'';
913 } else {
914 *p++ = *q;
915 }
916 }
917 *p++ = '\'';
918 *p = '\0';
919
920 do_cmd(host, user, cmd);
921 sfree(cmd);
922
923 while (ssh_scp_recv(&c, 1) > 0)
924 tell_char(stdout, c);
925 }
926
927 /*
928 * Initialize the Win$ock driver.
929 */
930 static void init_winsock(void)
931 {
932 WORD winsock_ver;
933 WSADATA wsadata;
934
935 winsock_ver = MAKEWORD(1, 1);
936 if (WSAStartup(winsock_ver, &wsadata))
937 bump("Unable to initialise WinSock");
938 if (LOBYTE(wsadata.wVersion) != 1 ||
939 HIBYTE(wsadata.wVersion) != 1)
940 bump("WinSock version is incompatible with 1.1");
941 }
942
943 /*
944 * Short description of parameters.
945 */
946 static void usage(void)
947 {
948 printf("PuTTY Secure Copy client\n");
949 printf("%s\n", ver);
950 printf("Usage: pscp [options] [user@]host:source target\n");
951 printf(" pscp [options] source [source...] [user@]host:target\n");
952 printf(" pscp [options] -ls user@host:filespec\n");
953 printf("Options:\n");
954 printf(" -p preserve file attributes\n");
955 printf(" -q quiet, don't show statistics\n");
956 printf(" -r copy directories recursively\n");
957 printf(" -v show verbose messages\n");
958 printf(" -P port connect to specified port\n");
959 printf(" -pw passw login with specified password\n");
960 /* GUI Adaptation - Sept 2000 */
961 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
962 exit(1);
963 }
964
965 /*
966 * Main program (no, really?)
967 */
968 int main(int argc, char *argv[])
969 {
970 int i;
971 int list = 0;
972
973 default_protocol = PROT_TELNET;
974
975 flags = FLAG_STDERR;
976 ssh_get_password = &get_password;
977 init_winsock();
978
979 for (i = 1; i < argc; i++) {
980 if (argv[i][0] != '-')
981 break;
982 if (strcmp(argv[i], "-v") == 0)
983 verbose = 1, flags |= FLAG_VERBOSE;
984 else if (strcmp(argv[i], "-r") == 0)
985 recursive = 1;
986 else if (strcmp(argv[i], "-p") == 0)
987 preserve = 1;
988 else if (strcmp(argv[i], "-q") == 0)
989 statistics = 0;
990 else if (strcmp(argv[i], "-h") == 0 ||
991 strcmp(argv[i], "-?") == 0)
992 usage();
993 else if (strcmp(argv[i], "-P") == 0 && i+1 < argc)
994 portnumber = atoi(argv[++i]);
995 else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc)
996 password = argv[++i];
997 else if (strcmp(argv[i], "-gui") == 0 && i+1 < argc) {
998 gui_hwnd = argv[++i];
999 gui_mode = 1;
1000 } else if (strcmp(argv[i], "-ls") == 0)
1001 list = 1;
1002 else if (strcmp(argv[i], "--") == 0)
1003 { i++; break; }
1004 else
1005 usage();
1006 }
1007 argc -= i;
1008 argv += i;
1009
1010 if (list) {
1011 if (argc != 1)
1012 usage();
1013 get_dir_list(argc, argv);
1014
1015 } else {
1016
1017 if (argc < 2)
1018 usage();
1019 if (argc > 2)
1020 targetshouldbedirectory = 1;
1021
1022 if (colon(argv[argc-1]) != NULL)
1023 toremote(argc, argv);
1024 else
1025 tolocal(argc, argv);
1026 }
1027
1028 if (connection_open) {
1029 char ch;
1030 ssh_scp_send_eof();
1031 ssh_scp_recv(&ch, 1);
1032 }
1033 WSACleanup();
1034 random_save_seed();
1035
1036 /* GUI Adaptation - August 2000 */
1037 if (gui_mode) {
1038 unsigned int msg_id = WM_RET_ERR_CNT;
1039 if (list) msg_id = WM_LS_RET_ERR_CNT;
1040 while (!PostMessage( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)errs, 0/*lParam*/ ) )
1041 SleepEx(1000,TRUE);
1042 }
1043 return (errs == 0 ? 0 : 1);
1044 }
1045
1046 /* end */