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