]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - scp.c
c11f499e92c5cfa5647187c05f343aa1a1ebd74f
[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  * (SGT, 2001-09-10: Joris van Rantwijk assures me that although
9  * this file as originally submitted was inspired by, and
10  * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any
11  * actual code duplicated, so the above comment shouldn't give rise
12  * to licensing issues.)
13  */
14
15 #include <windows.h>
16 #ifndef AUTO_WINSOCK
17 #ifdef WINSOCK_TWO
18 #include <winsock2.h>
19 #else
20 #include <winsock.h>
21 #endif
22 #endif
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <time.h>
28 #include <assert.h>
29
30 #define PUTTY_DO_GLOBALS
31 #include "putty.h"
32 #include "ssh.h"
33 #include "sftp.h"
34 #include "winstuff.h"
35 #include "storage.h"
36
37 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
38         ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
39 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
40         ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
41
42 /* GUI Adaptation - Sept 2000 */
43 #define   WM_APP_BASE           0x8000
44 #define   WM_STD_OUT_CHAR       ( WM_APP_BASE+400 )
45 #define   WM_STD_ERR_CHAR       ( WM_APP_BASE+401 )
46 #define   WM_STATS_CHAR         ( WM_APP_BASE+402 )
47 #define   WM_STATS_SIZE         ( WM_APP_BASE+403 )
48 #define   WM_STATS_PERCENT      ( WM_APP_BASE+404 )
49 #define   WM_STATS_ELAPSED      ( WM_APP_BASE+405 )
50 #define   WM_RET_ERR_CNT        ( WM_APP_BASE+406 )
51 #define   WM_LS_RET_ERR_CNT     ( WM_APP_BASE+407 )
52 #define   WM_STATS_DONE         ( WM_APP_BASE+408 )
53 #define   WM_STATS_ETA          ( WM_APP_BASE+409 )
54 #define   WM_STATS_RATEBS       ( WM_APP_BASE+410 )
55
56 static int list = 0;
57 static int verbose = 0;
58 static int recursive = 0;
59 static int preserve = 0;
60 static int targetshouldbedirectory = 0;
61 static int statistics = 1;
62 static int portnumber = 0;
63 static int prev_stats_len = 0;
64 static int scp_unsafe_mode = 0;
65 static char *password = NULL;
66 static int errs = 0;
67 /* GUI Adaptation - Sept 2000 */
68 #define NAME_STR_MAX 2048
69 static char statname[NAME_STR_MAX + 1];
70 static unsigned long statsize = 0;
71 static unsigned long statdone = 0;
72 static unsigned long stateta = 0;
73 static unsigned long statratebs = 0;
74 static int statperct = 0;
75 static unsigned long statelapsed = 0;
76 static int gui_mode = 0;
77 static char *gui_hwnd = NULL;
78 static int using_sftp = 0;
79
80 static void source(char *src);
81 static void rsource(char *src);
82 static void sink(char *targ, char *src);
83 /* GUI Adaptation - Sept 2000 */
84 static void tell_char(FILE * stream, char c);
85 static void tell_str(FILE * stream, char *str);
86 static void tell_user(FILE * stream, char *fmt, ...);
87 static void gui_update_stats(char *name, unsigned long size,
88                              int percentage, unsigned long elapsed, 
89                              unsigned long done, unsigned long eta,
90                              unsigned long ratebs);
91
92 /*
93  * The maximum amount of queued data we accept before we stop and
94  * wait for the server to process some.
95  */
96 #define MAX_SCP_BUFSIZE 16384
97
98 void logevent(char *string)
99 {
100 }
101
102 void ldisc_send(char *buf, int len, int interactive)
103 {
104     /*
105      * This is only here because of the calls to ldisc_send(NULL,
106      * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
107      * as an ldisc. So if we get called with any real data, I want
108      * to know about it.
109      */
110     assert(len == 0);
111 }
112
113 void verify_ssh_host_key(char *host, int port, char *keytype,
114                          char *keystr, char *fingerprint)
115 {
116     int ret;
117     HANDLE hin;
118     DWORD savemode, i;
119
120     static const char absentmsg[] =
121         "The server's host key is not cached in the registry. You\n"
122         "have no guarantee that the server is the computer you\n"
123         "think it is.\n"
124         "The server's key fingerprint is:\n"
125         "%s\n"
126         "If you trust this host, enter \"y\" to add the key to\n"
127         "PuTTY's cache and carry on connecting.\n"
128         "If you want to carry on connecting just once, without\n"
129         "adding the key to the cache, enter \"n\".\n"
130         "If you do not trust this host, press Return to abandon the\n"
131         "connection.\n"
132         "Store key in cache? (y/n) ";
133
134     static const char wrongmsg[] =
135         "WARNING - POTENTIAL SECURITY BREACH!\n"
136         "The server's host key does not match the one PuTTY has\n"
137         "cached in the registry. This means that either the\n"
138         "server administrator has changed the host key, or you\n"
139         "have actually connected to another computer pretending\n"
140         "to be the server.\n"
141         "The new key fingerprint is:\n"
142         "%s\n"
143         "If you were expecting this change and trust the new key,\n"
144         "enter \"y\" to update PuTTY's cache and continue connecting.\n"
145         "If you want to carry on connecting but without updating\n"
146         "the cache, enter \"n\".\n"
147         "If you want to abandon the connection completely, press\n"
148         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
149         "safe choice.\n"
150         "Update cached key? (y/n, Return cancels connection) ";
151
152     static const char abandoned[] = "Connection abandoned.\n";
153
154     char line[32];
155
156     /*
157      * Verify the key against the registry.
158      */
159     ret = verify_host_key(host, port, keytype, keystr);
160
161     if (ret == 0)                      /* success - key matched OK */
162         return;
163
164     if (ret == 2) {                    /* key was different */
165         fprintf(stderr, wrongmsg, fingerprint);
166         fflush(stderr);
167     }
168     if (ret == 1) {                    /* key was absent */
169         fprintf(stderr, absentmsg, fingerprint);
170         fflush(stderr);
171     }
172
173     hin = GetStdHandle(STD_INPUT_HANDLE);
174     GetConsoleMode(hin, &savemode);
175     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
176                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
177     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
178     SetConsoleMode(hin, savemode);
179
180     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
181         if (line[0] == 'y' || line[0] == 'Y')
182             store_host_key(host, port, keytype, keystr);
183     } else {
184         fprintf(stderr, abandoned);
185         exit(0);
186     }
187 }
188
189 /*
190  * Ask whether the selected cipher is acceptable (since it was
191  * below the configured 'warn' threshold).
192  * cs: 0 = both ways, 1 = client->server, 2 = server->client
193  */
194 void askcipher(char *ciphername, int cs)
195 {
196     HANDLE hin;
197     DWORD savemode, i;
198
199     static const char msg[] =
200         "The first %scipher supported by the server is\n"
201         "%s, which is below the configured warning threshold.\n"
202         "Continue with connection? (y/n) ";
203     static const char abandoned[] = "Connection abandoned.\n";
204
205     char line[32];
206
207     fprintf(stderr, msg,
208             (cs == 0) ? "" :
209             (cs == 1) ? "client-to-server " :
210                         "server-to-client ",
211             ciphername);
212     fflush(stderr);
213
214     hin = GetStdHandle(STD_INPUT_HANDLE);
215     GetConsoleMode(hin, &savemode);
216     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
217                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
218     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
219     SetConsoleMode(hin, savemode);
220
221     if (line[0] == 'y' || line[0] == 'Y') {
222         return;
223     } else {
224         fprintf(stderr, abandoned);
225         exit(0);
226     }
227 }
228
229 /* GUI Adaptation - Sept 2000 */
230 static void send_msg(HWND h, UINT message, WPARAM wParam)
231 {
232     while (!PostMessage(h, message, wParam, 0))
233         SleepEx(1000, TRUE);
234 }
235
236 static void tell_char(FILE * stream, char c)
237 {
238     if (!gui_mode)
239         fputc(c, stream);
240     else {
241         unsigned int msg_id = WM_STD_OUT_CHAR;
242         if (stream == stderr)
243             msg_id = WM_STD_ERR_CHAR;
244         send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
245     }
246 }
247
248 static void tell_str(FILE * stream, char *str)
249 {
250     unsigned int i;
251
252     for (i = 0; i < strlen(str); ++i)
253         tell_char(stream, str[i]);
254 }
255
256 static void tell_user(FILE * stream, char *fmt, ...)
257 {
258     char str[0x100];                   /* Make the size big enough */
259     va_list ap;
260     va_start(ap, fmt);
261     vsprintf(str, fmt, ap);
262     va_end(ap);
263     strcat(str, "\n");
264     tell_str(stream, str);
265 }
266
267 static void gui_update_stats(char *name, unsigned long size,
268                              int percentage, unsigned long elapsed,
269                              unsigned long done, unsigned long eta,
270                              unsigned long ratebs)
271 {
272     unsigned int i;
273
274     if (strcmp(name, statname) != 0) {
275         for (i = 0; i < strlen(name); ++i)
276             send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
277                      (WPARAM) name[i]);
278         send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
279         strcpy(statname, name);
280     }
281     if (statsize != size) {
282         send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
283         statsize = size;
284     }
285     if (statdone != done) {
286         send_msg((HWND) atoi(gui_hwnd), WM_STATS_DONE, (WPARAM) done);
287         statdone = done;
288     }
289     if (stateta != eta) {
290         send_msg((HWND) atoi(gui_hwnd), WM_STATS_ETA, (WPARAM) eta);
291         stateta = eta;
292     }
293     if (statratebs != ratebs) {
294         send_msg((HWND) atoi(gui_hwnd), WM_STATS_RATEBS, (WPARAM) ratebs);
295         statratebs = ratebs;
296     }
297     if (statelapsed != elapsed) {
298         send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
299                  (WPARAM) elapsed);
300         statelapsed = elapsed;
301     }
302     if (statperct != percentage) {
303         send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
304                  (WPARAM) percentage);
305         statperct = percentage;
306     }
307 }
308
309 /*
310  *  Print an error message and perform a fatal exit.
311  */
312 void fatalbox(char *fmt, ...)
313 {
314     char str[0x100];                   /* Make the size big enough */
315     va_list ap;
316     va_start(ap, fmt);
317     strcpy(str, "Fatal: ");
318     vsprintf(str + strlen(str), fmt, ap);
319     va_end(ap);
320     strcat(str, "\n");
321     tell_str(stderr, str);
322     errs++;
323
324     if (gui_mode) {
325         unsigned int msg_id = WM_RET_ERR_CNT;
326         if (list)
327             msg_id = WM_LS_RET_ERR_CNT;
328         while (!PostMessage
329                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
330                 0 /*lParam */ ))SleepEx(1000, TRUE);
331     }
332
333     exit(1);
334 }
335 void connection_fatal(char *fmt, ...)
336 {
337     char str[0x100];                   /* Make the size big enough */
338     va_list ap;
339     va_start(ap, fmt);
340     strcpy(str, "Fatal: ");
341     vsprintf(str + strlen(str), fmt, ap);
342     va_end(ap);
343     strcat(str, "\n");
344     tell_str(stderr, str);
345     errs++;
346
347     if (gui_mode) {
348         unsigned int msg_id = WM_RET_ERR_CNT;
349         if (list)
350             msg_id = WM_LS_RET_ERR_CNT;
351         while (!PostMessage
352                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
353                 0 /*lParam */ ))SleepEx(1000, TRUE);
354     }
355
356     exit(1);
357 }
358
359 /*
360  * Be told what socket we're supposed to be using.
361  */
362 static SOCKET scp_ssh_socket;
363 char *do_select(SOCKET skt, int startup)
364 {
365     if (startup)
366         scp_ssh_socket = skt;
367     else
368         scp_ssh_socket = INVALID_SOCKET;
369     return NULL;
370 }
371 extern int select_result(WPARAM, LPARAM);
372
373 /*
374  * Receive a block of data from the SSH link. Block until all data
375  * is available.
376  *
377  * To do this, we repeatedly call the SSH protocol module, with our
378  * own trap in from_backend() to catch the data that comes back. We
379  * do this until we have enough data.
380  */
381
382 static unsigned char *outptr;          /* where to put the data */
383 static unsigned outlen;                /* how much data required */
384 static unsigned char *pending = NULL;  /* any spare data */
385 static unsigned pendlen = 0, pendsize = 0;      /* length and phys. size of buffer */
386 int from_backend(int is_stderr, char *data, int datalen)
387 {
388     unsigned char *p = (unsigned char *) data;
389     unsigned len = (unsigned) datalen;
390
391     /*
392      * stderr data is just spouted to local stderr and otherwise
393      * ignored.
394      */
395     if (is_stderr) {
396         fwrite(data, 1, len, stderr);
397         return 0;
398     }
399
400     /*
401      * If this is before the real session begins, just return.
402      */
403     if (!outptr)
404         return 0;
405
406     if (outlen > 0) {
407         unsigned used = outlen;
408         if (used > len)
409             used = len;
410         memcpy(outptr, p, used);
411         outptr += used;
412         outlen -= used;
413         p += used;
414         len -= used;
415     }
416
417     if (len > 0) {
418         if (pendsize < pendlen + len) {
419             pendsize = pendlen + len + 4096;
420             pending = (pending ? srealloc(pending, pendsize) :
421                        smalloc(pendsize));
422             if (!pending)
423                 fatalbox("Out of memory");
424         }
425         memcpy(pending + pendlen, p, len);
426         pendlen += len;
427     }
428
429     return 0;
430 }
431 static int scp_process_network_event(void)
432 {
433     fd_set readfds;
434
435     FD_ZERO(&readfds);
436     FD_SET(scp_ssh_socket, &readfds);
437     if (select(1, &readfds, NULL, NULL, NULL) < 0)
438         return 0;                      /* doom */
439     select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
440     return 1;
441 }
442 static int ssh_scp_recv(unsigned char *buf, int len)
443 {
444     outptr = buf;
445     outlen = len;
446
447     /*
448      * See if the pending-input block contains some of what we
449      * need.
450      */
451     if (pendlen > 0) {
452         unsigned pendused = pendlen;
453         if (pendused > outlen)
454             pendused = outlen;
455         memcpy(outptr, pending, pendused);
456         memmove(pending, pending + pendused, pendlen - pendused);
457         outptr += pendused;
458         outlen -= pendused;
459         pendlen -= pendused;
460         if (pendlen == 0) {
461             pendsize = 0;
462             sfree(pending);
463             pending = NULL;
464         }
465         if (outlen == 0)
466             return len;
467     }
468
469     while (outlen > 0) {
470         if (!scp_process_network_event())
471             return 0;                  /* doom */
472     }
473
474     return len;
475 }
476
477 /*
478  * Loop through the ssh connection and authentication process.
479  */
480 static void ssh_scp_init(void)
481 {
482     if (scp_ssh_socket == INVALID_SOCKET)
483         return;
484     while (!back->sendok()) {
485         fd_set readfds;
486         FD_ZERO(&readfds);
487         FD_SET(scp_ssh_socket, &readfds);
488         if (select(1, &readfds, NULL, NULL, NULL) < 0)
489             return;                    /* doom */
490         select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
491     }
492     using_sftp = !ssh_fallback_cmd;
493 }
494
495 /*
496  *  Print an error message and exit after closing the SSH link.
497  */
498 static void bump(char *fmt, ...)
499 {
500     char str[0x100];                   /* Make the size big enough */
501     va_list ap;
502     va_start(ap, fmt);
503     strcpy(str, "Fatal: ");
504     vsprintf(str + strlen(str), fmt, ap);
505     va_end(ap);
506     strcat(str, "\n");
507     tell_str(stderr, str);
508     errs++;
509
510     if (back != NULL && back->socket() != NULL) {
511         char ch;
512         back->special(TS_EOF);
513         ssh_scp_recv(&ch, 1);
514     }
515
516     if (gui_mode) {
517         unsigned int msg_id = WM_RET_ERR_CNT;
518         if (list)
519             msg_id = WM_LS_RET_ERR_CNT;
520         while (!PostMessage
521                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
522                 0 /*lParam */ ))SleepEx(1000, TRUE);
523     }
524
525     exit(1);
526 }
527
528 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
529 {
530     HANDLE hin, hout;
531     DWORD savemode, newmode, i;
532
533     if (is_pw && password) {
534         static int tried_once = 0;
535
536         if (tried_once) {
537             return 0;
538         } else {
539             strncpy(str, password, maxlen);
540             str[maxlen - 1] = '\0';
541             tried_once = 1;
542             return 1;
543         }
544     }
545
546     /* GUI Adaptation - Sept 2000 */
547     if (gui_mode) {
548         if (maxlen > 0)
549             str[0] = '\0';
550     } else {
551         hin = GetStdHandle(STD_INPUT_HANDLE);
552         hout = GetStdHandle(STD_OUTPUT_HANDLE);
553         if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
554             bump("Cannot get standard input/output handles");
555
556         GetConsoleMode(hin, &savemode);
557         newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
558         if (is_pw)
559             newmode &= ~ENABLE_ECHO_INPUT;
560         else
561             newmode |= ENABLE_ECHO_INPUT;
562         SetConsoleMode(hin, newmode);
563
564         WriteFile(hout, prompt, strlen(prompt), &i, NULL);
565         ReadFile(hin, str, maxlen - 1, &i, NULL);
566
567         SetConsoleMode(hin, savemode);
568
569         if ((int) i > maxlen)
570             i = maxlen - 1;
571         else
572             i = i - 2;
573         str[i] = '\0';
574
575         if (is_pw)
576             WriteFile(hout, "\r\n", 2, &i, NULL);
577     }
578
579     return 1;
580 }
581
582 /*
583  *  Open an SSH connection to user@host and execute cmd.
584  */
585 static void do_cmd(char *host, char *user, char *cmd)
586 {
587     char *err, *realhost;
588     DWORD namelen;
589
590     if (host == NULL || host[0] == '\0')
591         bump("Empty host name");
592
593     /* Try to load settings for this host */
594     do_defaults(host, &cfg);
595     if (cfg.host[0] == '\0') {
596         /* No settings for this host; use defaults */
597         do_defaults(NULL, &cfg);
598         strncpy(cfg.host, host, sizeof(cfg.host) - 1);
599         cfg.host[sizeof(cfg.host) - 1] = '\0';
600         cfg.port = 22;
601     }
602
603     /*
604      * Trim leading whitespace off the hostname if it's there.
605      */
606     {
607         int space = strspn(cfg.host, " \t");
608         memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
609     }
610
611     /* See if host is of the form user@host */
612     if (cfg.host[0] != '\0') {
613         char *atsign = strchr(cfg.host, '@');
614         /* Make sure we're not overflowing the user field */
615         if (atsign) {
616             if (atsign - cfg.host < sizeof cfg.username) {
617                 strncpy(cfg.username, cfg.host, atsign - cfg.host);
618                 cfg.username[atsign - cfg.host] = '\0';
619             }
620             memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
621         }
622     }
623
624     /*
625      * Trim a colon suffix off the hostname if it's there.
626      */
627     cfg.host[strcspn(cfg.host, ":")] = '\0';
628
629     /* Set username */
630     if (user != NULL && user[0] != '\0') {
631         strncpy(cfg.username, user, sizeof(cfg.username) - 1);
632         cfg.username[sizeof(cfg.username) - 1] = '\0';
633     } else if (cfg.username[0] == '\0') {
634         namelen = 0;
635         if (GetUserName(user, &namelen) == FALSE)
636             bump("Empty user name");
637         user = smalloc(namelen * sizeof(char));
638         GetUserName(user, &namelen);
639         if (verbose)
640             tell_user(stderr, "Guessing user name: %s", user);
641         strncpy(cfg.username, user, sizeof(cfg.username) - 1);
642         cfg.username[sizeof(cfg.username) - 1] = '\0';
643         free(user);
644     }
645
646     if (cfg.protocol != PROT_SSH)
647         cfg.port = 22;
648
649     if (portnumber)
650         cfg.port = portnumber;
651
652     /*
653      * Disable scary things which shouldn't be enabled for simple
654      * things like SCP and SFTP: agent forwarding, port forwarding,
655      * X forwarding.
656      */
657     cfg.x11_forward = 0;
658     cfg.agentfwd = 0;
659     cfg.portfwd[0] = cfg.portfwd[1] = '\0';
660
661     /*
662      * Attempt to start the SFTP subsystem as a first choice,
663      * falling back to the provided scp command if that fails.
664      */
665     strcpy(cfg.remote_cmd, "sftp");
666     cfg.ssh_subsys = TRUE;
667     cfg.remote_cmd_ptr2 = cmd;
668     cfg.ssh_subsys2 = FALSE;
669     cfg.nopty = TRUE;
670
671     back = &ssh_backend;
672
673     err = back->init(cfg.host, cfg.port, &realhost);
674     if (err != NULL)
675         bump("ssh_init: %s", err);
676     ssh_scp_init();
677     if (verbose && realhost != NULL)
678         tell_user(stderr, "Connected to %s\n", realhost);
679     sfree(realhost);
680 }
681
682 /*
683  *  Update statistic information about current file.
684  */
685 static void print_stats(char *name, unsigned long size, unsigned long done,
686                         time_t start, time_t now)
687 {
688     float ratebs;
689     unsigned long eta;
690     char etastr[10];
691     int pct;
692     int len;
693     int elap;
694
695     elap = (unsigned long) difftime(now, start);
696
697     if (now > start)
698         ratebs = (float) done / elap;
699     else
700         ratebs = (float) done;
701
702     if (ratebs < 1.0)
703         eta = size - done;
704     else
705         eta = (unsigned long) ((size - done) / ratebs);
706     sprintf(etastr, "%02ld:%02ld:%02ld",
707             eta / 3600, (eta % 3600) / 60, eta % 60);
708
709     pct = (int) (100 * (done * 1.0 / size));
710
711     if (gui_mode)
712         /* GUI Adaptation - Sept 2000 */
713         gui_update_stats(name, size, pct, elap, done, eta, 
714                          (unsigned long) ratebs);
715     else {
716         len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
717                      name, done / 1024, ratebs / 1024.0, etastr, pct);
718         if (len < prev_stats_len)
719             printf("%*s", prev_stats_len - len, "");
720         prev_stats_len = len;
721
722         if (done == size)
723             printf("\n");
724     }
725 }
726
727 /*
728  *  Find a colon in str and return a pointer to the colon.
729  *  This is used to separate hostname from filename.
730  */
731 static char *colon(char *str)
732 {
733     /* We ignore a leading colon, since the hostname cannot be
734        empty. We also ignore a colon as second character because
735        of filenames like f:myfile.txt. */
736     if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
737         return (NULL);
738     while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
739         str++;
740     if (*str == ':')
741         return (str);
742     else
743         return (NULL);
744 }
745
746 /*
747  * Return a pointer to the portion of str that comes after the last
748  * slash (or backslash or colon, if `local' is TRUE).
749  */
750 static char *stripslashes(char *str, int local)
751 {
752     char *p;
753
754     if (local) {
755         p = strchr(str, ':');
756         if (p) str = p+1;
757     }
758
759     p = strrchr(str, '/');
760     if (p) str = p+1;
761
762     if (local) {
763         p = strrchr(str, '\\');
764         if (p) str = p+1;
765     }
766
767     return str;
768 }
769
770 /*
771  * Determine whether a string is entirely composed of dots.
772  */
773 static int is_dots(char *str)
774 {
775     return str[strspn(str, ".")] == '\0';
776 }
777
778 /*
779  *  Wait for a response from the other side.
780  *  Return 0 if ok, -1 if error.
781  */
782 static int response(void)
783 {
784     char ch, resp, rbuf[2048];
785     int p;
786
787     if (ssh_scp_recv(&resp, 1) <= 0)
788         bump("Lost connection");
789
790     p = 0;
791     switch (resp) {
792       case 0:                          /* ok */
793         return (0);
794       default:
795         rbuf[p++] = resp;
796         /* fallthrough */
797       case 1:                          /* error */
798       case 2:                          /* fatal error */
799         do {
800             if (ssh_scp_recv(&ch, 1) <= 0)
801                 bump("Protocol error: Lost connection");
802             rbuf[p++] = ch;
803         } while (p < sizeof(rbuf) && ch != '\n');
804         rbuf[p - 1] = '\0';
805         if (resp == 1)
806             tell_user(stderr, "%s\n", rbuf);
807         else
808             bump("%s", rbuf);
809         errs++;
810         return (-1);
811     }
812 }
813
814 int sftp_recvdata(char *buf, int len)
815 {
816     return ssh_scp_recv(buf, len);
817 }
818 int sftp_senddata(char *buf, int len)
819 {
820     back->send((unsigned char *) buf, len);
821     return 1;
822 }
823
824 /* ----------------------------------------------------------------------
825  * sftp-based replacement for the hacky `pscp -ls'.
826  */
827 static int sftp_ls_compare(const void *av, const void *bv)
828 {
829     const struct fxp_name *a = (const struct fxp_name *) av;
830     const struct fxp_name *b = (const struct fxp_name *) bv;
831     return strcmp(a->filename, b->filename);
832 }
833 void scp_sftp_listdir(char *dirname)
834 {
835     struct fxp_handle *dirh;
836     struct fxp_names *names;
837     struct fxp_name *ournames;
838     int nnames, namesize;
839     int i;
840
841     printf("Listing directory %s\n", dirname);
842
843     dirh = fxp_opendir(dirname);
844     if (dirh == NULL) {
845         printf("Unable to open %s: %s\n", dirname, fxp_error());
846     } else {
847         nnames = namesize = 0;
848         ournames = NULL;
849
850         while (1) {
851
852             names = fxp_readdir(dirh);
853             if (names == NULL) {
854                 if (fxp_error_type() == SSH_FX_EOF)
855                     break;
856                 printf("Reading directory %s: %s\n", dirname, fxp_error());
857                 break;
858             }
859             if (names->nnames == 0) {
860                 fxp_free_names(names);
861                 break;
862             }
863
864             if (nnames + names->nnames >= namesize) {
865                 namesize += names->nnames + 128;
866                 ournames =
867                     srealloc(ournames, namesize * sizeof(*ournames));
868             }
869
870             for (i = 0; i < names->nnames; i++)
871                 ournames[nnames++] = names->names[i];
872
873             names->nnames = 0;         /* prevent free_names */
874             fxp_free_names(names);
875         }
876         fxp_close(dirh);
877
878         /*
879          * Now we have our filenames. Sort them by actual file
880          * name, and then output the longname parts.
881          */
882         qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
883
884         /*
885          * And print them.
886          */
887         for (i = 0; i < nnames; i++)
888             printf("%s\n", ournames[i].longname);
889     }
890 }
891
892 /* ----------------------------------------------------------------------
893  * Helper routines that contain the actual SCP protocol elements,
894  * implemented both as SCP1 and SFTP.
895  */
896
897 static struct scp_sftp_dirstack {
898     struct scp_sftp_dirstack *next;
899     struct fxp_name *names;
900     int namepos, namelen;
901     char *dirpath;
902     char *wildcard;
903     int matched_something;             /* wildcard match set was non-empty */
904 } *scp_sftp_dirstack_head;
905 static char *scp_sftp_remotepath, *scp_sftp_currentname;
906 static char *scp_sftp_wildcard;
907 static int scp_sftp_targetisdir, scp_sftp_donethistarget;
908 static int scp_sftp_preserve, scp_sftp_recursive;
909 static unsigned long scp_sftp_mtime, scp_sftp_atime;
910 static int scp_has_times;
911 static struct fxp_handle *scp_sftp_filehandle;
912 static uint64 scp_sftp_fileoffset;
913
914 void scp_source_setup(char *target, int shouldbedir)
915 {
916     if (using_sftp) {
917         /*
918          * Find out whether the target filespec is in fact a
919          * directory.
920          */
921         struct fxp_attrs attrs;
922
923         if (!fxp_stat(target, &attrs) ||
924             !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))
925             scp_sftp_targetisdir = 0;
926         else
927             scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0;
928
929         if (shouldbedir && !scp_sftp_targetisdir) {
930             bump("pscp: remote filespec %s: not a directory\n", target);
931         }
932
933         scp_sftp_remotepath = dupstr(target);
934
935         scp_has_times = 0;
936     } else {
937         (void) response();
938     }
939 }
940
941 int scp_send_errmsg(char *str)
942 {
943     if (using_sftp) {
944         /* do nothing; we never need to send our errors to the server */
945     } else {
946         back->send("\001", 1);         /* scp protocol error prefix */
947         back->send(str, strlen(str));
948     }
949     return 0;                          /* can't fail */
950 }
951
952 int scp_send_filetimes(unsigned long mtime, unsigned long atime)
953 {
954     if (using_sftp) {
955         scp_sftp_mtime = mtime;
956         scp_sftp_atime = atime;
957         scp_has_times = 1;
958         return 0;
959     } else {
960         char buf[80];
961         sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
962         back->send(buf, strlen(buf));
963         return response();
964     }
965 }
966
967 int scp_send_filename(char *name, unsigned long size, int modes)
968 {
969     if (using_sftp) {
970         char *fullname;
971         if (scp_sftp_targetisdir) {
972             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
973         } else {
974             fullname = dupstr(scp_sftp_remotepath);
975         }
976         scp_sftp_filehandle =
977             fxp_open(fullname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
978         if (!scp_sftp_filehandle) {
979             tell_user(stderr, "pscp: unable to open %s: %s",
980                       fullname, fxp_error());
981             errs++;
982             return 1;
983         }
984         scp_sftp_fileoffset = uint64_make(0, 0);
985         sfree(fullname);
986         return 0;
987     } else {
988         char buf[40];
989         sprintf(buf, "C%04o %lu ", modes, size);
990         back->send(buf, strlen(buf));
991         back->send(name, strlen(name));
992         back->send("\n", 1);
993         return response();
994     }
995 }
996
997 int scp_send_filedata(char *data, int len)
998 {
999     if (using_sftp) {
1000         if (!scp_sftp_filehandle) {
1001             return 1;
1002         }
1003         if (!fxp_write(scp_sftp_filehandle, data, scp_sftp_fileoffset, len)) {
1004             tell_user(stderr, "error while writing: %s\n", fxp_error());
1005             errs++;
1006             return 1;
1007         }
1008         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);
1009         return 0;
1010     } else {
1011         int bufsize = back->send(data, len);
1012
1013         /*
1014          * If the network transfer is backing up - that is, the
1015          * remote site is not accepting data as fast as we can
1016          * produce it - then we must loop on network events until
1017          * we have space in the buffer again.
1018          */
1019         while (bufsize > MAX_SCP_BUFSIZE) {
1020             if (!scp_process_network_event())
1021                 return 1;
1022             bufsize = back->sendbuffer();
1023         }
1024
1025         return 0;
1026     }
1027 }
1028
1029 int scp_send_finish(void)
1030 {
1031     if (using_sftp) {
1032         struct fxp_attrs attrs;
1033         if (!scp_sftp_filehandle) {
1034             return 1;
1035         }
1036         if (scp_has_times) {
1037             attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;
1038             attrs.atime = scp_sftp_atime;
1039             attrs.mtime = scp_sftp_mtime;
1040             if (!fxp_fsetstat(scp_sftp_filehandle, attrs)) {
1041                 tell_user(stderr, "unable to set file times: %s\n", fxp_error());
1042                 errs++;
1043             }
1044         }
1045         fxp_close(scp_sftp_filehandle);
1046         scp_has_times = 0;
1047         return 0;
1048     } else {
1049         back->send("", 1);
1050         return response();
1051     }
1052 }
1053
1054 char *scp_save_remotepath(void)
1055 {
1056     if (using_sftp)
1057         return scp_sftp_remotepath;
1058     else
1059         return NULL;
1060 }
1061
1062 void scp_restore_remotepath(char *data)
1063 {
1064     if (using_sftp)
1065         scp_sftp_remotepath = data;
1066 }
1067
1068 int scp_send_dirname(char *name, int modes)
1069 {
1070     if (using_sftp) {
1071         char *fullname;
1072         char const *err;
1073         struct fxp_attrs attrs;
1074         if (scp_sftp_targetisdir) {
1075             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
1076         } else {
1077             fullname = dupstr(scp_sftp_remotepath);
1078         }
1079
1080         /*
1081          * We don't worry about whether we managed to create the
1082          * directory, because if it exists already it's OK just to
1083          * use it. Instead, we will stat it afterwards, and if it
1084          * exists and is a directory we will assume we were either
1085          * successful or it didn't matter.
1086          */
1087         if (!fxp_mkdir(fullname))
1088             err = fxp_error();
1089         else
1090             err = "server reported no error";
1091         if (!fxp_stat(fullname, &attrs) ||
1092             !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
1093             !(attrs.permissions & 0040000)) {
1094             tell_user(stderr, "unable to create directory %s: %s",
1095                       fullname, err);
1096             errs++;
1097             return 1;
1098         }
1099
1100         scp_sftp_remotepath = fullname;
1101
1102         return 0;
1103     } else {
1104         char buf[40];
1105         sprintf(buf, "D%04o 0 ", modes);
1106         back->send(buf, strlen(buf));
1107         back->send(name, strlen(name));
1108         back->send("\n", 1);
1109         return response();
1110     }
1111 }
1112
1113 int scp_send_enddir(void)
1114 {
1115     if (using_sftp) {
1116         sfree(scp_sftp_remotepath);
1117         return 0;
1118     } else {
1119         back->send("E\n", 2);
1120         return response();
1121     }
1122 }
1123
1124 /*
1125  * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init.
1126  * That's bad. The difference is that scp_sink_setup is called once
1127  * right at the start, whereas scp_sink_init is called to
1128  * initialise every level of recursion in the protocol.
1129  */
1130 int scp_sink_setup(char *source, int preserve, int recursive)
1131 {
1132     if (using_sftp) {
1133         char *newsource;
1134         /*
1135          * It's possible that the source string we've been given
1136          * contains a wildcard. If so, we must split the directory
1137          * away from the wildcard itself (throwing an error if any
1138          * wildcardness comes before the final slash) and arrange
1139          * things so that a dirstack entry will be set up.
1140          */
1141         newsource = smalloc(1+strlen(source));
1142         if (!wc_unescape(newsource, source)) {
1143             /* Yes, here we go; it's a wildcard. Bah. */
1144             char *dupsource, *lastpart, *dirpart, *wildcard;
1145             dupsource = dupstr(source);
1146             lastpart = stripslashes(dupsource, 0);
1147             wildcard = dupstr(lastpart);
1148             *lastpart = '\0';
1149             if (*dupsource && dupsource[1]) {
1150                 /*
1151                  * The remains of dupsource are at least two
1152                  * characters long, meaning the pathname wasn't
1153                  * empty or just `/'. Hence, we remove the trailing
1154                  * slash.
1155                  */
1156                 lastpart[-1] = '\0';
1157             } else if (!*dupsource) {
1158                 /*
1159                  * The remains of dupsource are _empty_ - the whole
1160                  * pathname was a wildcard. Hence we need to
1161                  * replace it with ".".
1162                  */
1163                 sfree(dupsource);
1164                 dupsource = dupstr(".");
1165             }
1166
1167             /*
1168              * Now we have separated our string into dupsource (the
1169              * directory part) and wildcard. Both of these will
1170              * need freeing at some point. Next step is to remove
1171              * wildcard escapes from the directory part, throwing
1172              * an error if it contains a real wildcard.
1173              */
1174             dirpart = smalloc(1+strlen(dupsource));
1175             if (!wc_unescape(dirpart, dupsource)) {
1176                 tell_user(stderr, "%s: multiple-level wildcards unsupported",
1177                           source);
1178                 errs++;
1179                 sfree(dirpart);
1180                 sfree(wildcard);
1181                 sfree(dupsource);
1182                 return 1;
1183             }
1184
1185             /*
1186              * Now we have dirpart (unescaped, ie a valid remote
1187              * path), and wildcard (a wildcard). This will be
1188              * sufficient to arrange a dirstack entry.
1189              */
1190             scp_sftp_remotepath = dirpart;
1191             scp_sftp_wildcard = wildcard;
1192             sfree(dupsource);
1193         } else {
1194             scp_sftp_remotepath = newsource;
1195             scp_sftp_wildcard = NULL;
1196         }
1197         scp_sftp_preserve = preserve;
1198         scp_sftp_recursive = recursive;
1199         scp_sftp_donethistarget = 0;
1200         scp_sftp_dirstack_head = NULL;
1201     }
1202     return 0;
1203 }
1204
1205 int scp_sink_init(void)
1206 {
1207     if (!using_sftp) {
1208         back->send("", 1);
1209     }
1210     return 0;
1211 }
1212
1213 #define SCP_SINK_FILE   1
1214 #define SCP_SINK_DIR    2
1215 #define SCP_SINK_ENDDIR 3
1216 #define SCP_SINK_RETRY  4              /* not an action; just try again */
1217 struct scp_sink_action {
1218     int action;                        /* FILE, DIR, ENDDIR */
1219     char *buf;                         /* will need freeing after use */
1220     char *name;                        /* filename or dirname (not ENDDIR) */
1221     int mode;                          /* access mode (not ENDDIR) */
1222     unsigned long size;                /* file size (not ENDDIR) */
1223     int settime;                       /* 1 if atime and mtime are filled */
1224     unsigned long atime, mtime;        /* access times for the file */
1225 };
1226
1227 int scp_get_sink_action(struct scp_sink_action *act)
1228 {
1229     if (using_sftp) {
1230         char *fname;
1231         int must_free_fname;
1232         struct fxp_attrs attrs;
1233         int ret;
1234
1235         if (!scp_sftp_dirstack_head) {
1236             if (!scp_sftp_donethistarget) {
1237                 /*
1238                  * Simple case: we are only dealing with one file.
1239                  */
1240                 fname = scp_sftp_remotepath;
1241                 must_free_fname = 0;
1242                 scp_sftp_donethistarget = 1;
1243             } else {
1244                 /*
1245                  * Even simpler case: one file _which we've done_.
1246                  * Return 1 (finished).
1247                  */
1248                 return 1;
1249             }
1250         } else {
1251             /*
1252              * We're now in the middle of stepping through a list
1253              * of names returned from fxp_readdir(); so let's carry
1254              * on.
1255              */
1256             struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;
1257             while (head->namepos < head->namelen &&
1258                    (is_dots(head->names[head->namepos].filename) ||
1259                     (head->wildcard &&
1260                      !wc_match(head->wildcard,
1261                                head->names[head->namepos].filename))))
1262                 head->namepos++;       /* skip . and .. */
1263             if (head->namepos < head->namelen) {
1264                 head->matched_something = 1;
1265                 fname = dupcat(head->dirpath, "/",
1266                                head->names[head->namepos++].filename,
1267                                NULL);
1268                 must_free_fname = 1;
1269             } else {
1270                 /*
1271                  * We've come to the end of the list; pop it off
1272                  * the stack and return an ENDDIR action (or RETRY
1273                  * if this was a wildcard match).
1274                  */
1275                 if (head->wildcard) {
1276                     act->action = SCP_SINK_RETRY;
1277                     if (!head->matched_something) {
1278                         tell_user(stderr, "pscp: wildcard '%s' matched "
1279                                   "no files", head->wildcard);
1280                         errs++;
1281                     }
1282                     sfree(head->wildcard);
1283
1284                 } else {
1285                     act->action = SCP_SINK_ENDDIR;
1286                 }
1287
1288                 sfree(head->dirpath);
1289                 sfree(head->names);
1290                 scp_sftp_dirstack_head = head->next;
1291                 sfree(head);
1292
1293                 return 0;
1294             }
1295         }
1296
1297         /*
1298          * Now we have a filename. Stat it, and see if it's a file
1299          * or a directory.
1300          */
1301         ret = fxp_stat(fname, &attrs);
1302         if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
1303             tell_user(stderr, "unable to identify %s: %s", fname,
1304                       ret ? "file type not supplied" : fxp_error());
1305             errs++;
1306             return 1;
1307         }
1308
1309         if (attrs.permissions & 0040000) {
1310             struct scp_sftp_dirstack *newitem;
1311             struct fxp_handle *dirhandle;
1312             int nnames, namesize;
1313             struct fxp_name *ournames;
1314             struct fxp_names *names;
1315
1316             /*
1317              * It's a directory. If we're not in recursive mode,
1318              * this merits a complaint (which is fatal if the name
1319              * was specified directly, but not if it was matched by
1320              * a wildcard).
1321              * 
1322              * We skip this complaint completely if
1323              * scp_sftp_wildcard is set, because that's an
1324              * indication that we're not actually supposed to
1325              * _recursively_ transfer the dir, just scan it for
1326              * things matching the wildcard.
1327              */
1328             if (!scp_sftp_recursive && !scp_sftp_wildcard) {
1329                 tell_user(stderr, "pscp: %s: is a directory", fname);
1330                 errs++;
1331                 if (must_free_fname) sfree(fname);
1332                 if (scp_sftp_dirstack_head) {
1333                     act->action = SCP_SINK_RETRY;
1334                     return 0;
1335                 } else {
1336                     return 1;
1337                 }
1338             }
1339
1340             /*
1341              * Otherwise, the fun begins. We must fxp_opendir() the
1342              * directory, slurp the filenames into memory, return
1343              * SCP_SINK_DIR (unless this is a wildcard match), and
1344              * set targetisdir. The next time we're called, we will
1345              * run through the list of filenames one by one,
1346              * matching them against a wildcard if present.
1347              * 
1348              * If targetisdir is _already_ set (meaning we're
1349              * already in the middle of going through another such
1350              * list), we must push the other (target,namelist) pair
1351              * on a stack.
1352              */
1353             dirhandle = fxp_opendir(fname);
1354             if (!dirhandle) {
1355                 tell_user(stderr, "scp: unable to open directory %s: %s",
1356                           fname, fxp_error());
1357                 if (must_free_fname) sfree(fname);
1358                 errs++;
1359                 return 1;
1360             }
1361             nnames = namesize = 0;
1362             ournames = NULL;
1363             while (1) {
1364                 int i;
1365
1366                 names = fxp_readdir(dirhandle);
1367                 if (names == NULL) {
1368                     if (fxp_error_type() == SSH_FX_EOF)
1369                         break;
1370                     tell_user(stderr, "scp: reading directory %s: %s\n",
1371                               fname, fxp_error());
1372                     if (must_free_fname) sfree(fname);
1373                     sfree(ournames);
1374                     errs++;
1375                     return 1;
1376                 }
1377                 if (names->nnames == 0) {
1378                     fxp_free_names(names);
1379                     break;
1380                 }
1381                 if (nnames + names->nnames >= namesize) {
1382                     namesize += names->nnames + 128;
1383                     ournames =
1384                         srealloc(ournames, namesize * sizeof(*ournames));
1385                 }
1386                 for (i = 0; i < names->nnames; i++)
1387                     ournames[nnames++] = names->names[i];
1388                 names->nnames = 0;             /* prevent free_names */
1389                 fxp_free_names(names);
1390             }
1391             fxp_close(dirhandle);
1392
1393             newitem = smalloc(sizeof(struct scp_sftp_dirstack));
1394             newitem->next = scp_sftp_dirstack_head;
1395             newitem->names = ournames;
1396             newitem->namepos = 0;
1397             newitem->namelen = nnames;
1398             if (must_free_fname)
1399                 newitem->dirpath = fname;
1400             else
1401                 newitem->dirpath = dupstr(fname);
1402             if (scp_sftp_wildcard) {
1403                 newitem->wildcard = scp_sftp_wildcard;
1404                 newitem->matched_something = 0;
1405                 scp_sftp_wildcard = NULL;
1406             } else {
1407                 newitem->wildcard = NULL;
1408             }
1409             scp_sftp_dirstack_head = newitem;
1410
1411             if (newitem->wildcard) {
1412                 act->action = SCP_SINK_RETRY;
1413             } else {
1414                 act->action = SCP_SINK_DIR;
1415                 act->buf = dupstr(stripslashes(fname, 0));
1416                 act->name = act->buf;
1417                 act->size = 0;         /* duhh, it's a directory */
1418                 act->mode = 07777 & attrs.permissions;
1419                 if (scp_sftp_preserve &&
1420                     (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
1421                     act->atime = attrs.atime;
1422                     act->mtime = attrs.mtime;
1423                     act->settime = 1;
1424                 } else
1425                     act->settime = 0;
1426             }
1427             return 0;
1428
1429         } else {
1430             /*
1431              * It's a file. Return SCP_SINK_FILE.
1432              */
1433             act->action = SCP_SINK_FILE;
1434             act->buf = dupstr(stripslashes(fname, 0));
1435             act->name = act->buf;
1436             if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
1437                 if (uint64_compare(attrs.size,
1438                                    uint64_make(0, ULONG_MAX)) > 0) {
1439                     act->size = ULONG_MAX;   /* *boggle* */
1440                 } else
1441                     act->size = attrs.size.lo;
1442             } else
1443                 act->size = ULONG_MAX;   /* no idea */
1444             act->mode = 07777 & attrs.permissions;
1445             if (scp_sftp_preserve &&
1446                 (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
1447                 act->atime = attrs.atime;
1448                 act->mtime = attrs.mtime;
1449                 act->settime = 1;
1450             } else
1451                 act->settime = 0;
1452             if (must_free_fname)
1453                 scp_sftp_currentname = fname;
1454             else
1455                 scp_sftp_currentname = dupstr(fname);
1456             return 0;
1457         }
1458
1459     } else {
1460         int done = 0;
1461         int i, bufsize;
1462         int action;
1463         char ch;
1464
1465         act->settime = 0;
1466         act->buf = NULL;
1467         bufsize = 0;
1468
1469         while (!done) {
1470             if (ssh_scp_recv(&ch, 1) <= 0)
1471                 return 1;
1472             if (ch == '\n')
1473                 bump("Protocol error: Unexpected newline");
1474             i = 0;
1475             action = ch;
1476             do {
1477                 if (ssh_scp_recv(&ch, 1) <= 0)
1478                     bump("Lost connection");
1479                 if (i >= bufsize) {
1480                     bufsize = i + 128;
1481                     act->buf = srealloc(act->buf, bufsize);
1482                 }
1483                 act->buf[i++] = ch;
1484             } while (ch != '\n');
1485             act->buf[i - 1] = '\0';
1486             switch (action) {
1487               case '\01':                      /* error */
1488                 tell_user(stderr, "%s\n", act->buf);
1489                 errs++;
1490                 continue;                      /* go round again */
1491               case '\02':                      /* fatal error */
1492                 bump("%s", act->buf);
1493               case 'E':
1494                 back->send("", 1);
1495                 act->action = SCP_SINK_ENDDIR;
1496                 return 0;
1497               case 'T':
1498                 if (sscanf(act->buf, "%ld %*d %ld %*d",
1499                            &act->mtime, &act->atime) == 2) {
1500                     act->settime = 1;
1501                     back->send("", 1);
1502                     continue;          /* go round again */
1503                 }
1504                 bump("Protocol error: Illegal time format");
1505               case 'C':
1506               case 'D':
1507                 act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
1508                 break;
1509               default:
1510                 bump("Protocol error: Expected control record");
1511             }
1512             /*
1513              * We will go round this loop only once, unless we hit
1514              * `continue' above.
1515              */
1516             done = 1;
1517         }
1518
1519         /*
1520          * If we get here, we must have seen SCP_SINK_FILE or
1521          * SCP_SINK_DIR.
1522          */
1523         if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
1524             bump("Protocol error: Illegal file descriptor format");
1525         act->name = act->buf + i;
1526         return 0;
1527     }
1528 }
1529
1530 int scp_accept_filexfer(void)
1531 {
1532     if (using_sftp) {
1533         scp_sftp_filehandle =
1534             fxp_open(scp_sftp_currentname, SSH_FXF_READ);
1535         if (!scp_sftp_filehandle) {
1536             tell_user(stderr, "pscp: unable to open %s: %s",
1537                       scp_sftp_currentname, fxp_error());
1538             errs++;
1539             return 1;
1540         }
1541         scp_sftp_fileoffset = uint64_make(0, 0);
1542         sfree(scp_sftp_currentname);
1543         return 0;
1544     } else {
1545         back->send("", 1);
1546         return 0;                      /* can't fail */
1547     }
1548 }
1549
1550 int scp_recv_filedata(char *data, int len)
1551 {
1552     if (using_sftp) {
1553         int actuallen = fxp_read(scp_sftp_filehandle, data,
1554                                  scp_sftp_fileoffset, len);
1555         if (actuallen == -1 && fxp_error_type() != SSH_FX_EOF) {
1556             tell_user(stderr, "pscp: error while reading: %s", fxp_error());
1557             errs++;
1558             return -1;
1559         }
1560         if (actuallen < 0)
1561             actuallen = 0;
1562
1563         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, actuallen);
1564
1565         return actuallen;
1566     } else {
1567         return ssh_scp_recv(data, len);
1568     }
1569 }
1570
1571 int scp_finish_filerecv(void)
1572 {
1573     if (using_sftp) {
1574         fxp_close(scp_sftp_filehandle);
1575         return 0;
1576     } else {
1577         back->send("", 1);
1578         return response();
1579     }
1580 }
1581
1582 /* ----------------------------------------------------------------------
1583  *  Send an error message to the other side and to the screen.
1584  *  Increment error counter.
1585  */
1586 static void run_err(const char *fmt, ...)
1587 {
1588     char str[2048];
1589     va_list ap;
1590     va_start(ap, fmt);
1591     errs++;
1592     strcpy(str, "scp: ");
1593     vsprintf(str + strlen(str), fmt, ap);
1594     strcat(str, "\n");
1595     scp_send_errmsg(str);
1596     tell_user(stderr, "%s", str);
1597     va_end(ap);
1598 }
1599
1600 /*
1601  *  Execute the source part of the SCP protocol.
1602  */
1603 static void source(char *src)
1604 {
1605     unsigned long size;
1606     char *last;
1607     HANDLE f;
1608     DWORD attr;
1609     unsigned long i;
1610     unsigned long stat_bytes;
1611     time_t stat_starttime, stat_lasttime;
1612
1613     attr = GetFileAttributes(src);
1614     if (attr == (DWORD) - 1) {
1615         run_err("%s: No such file or directory", src);
1616         return;
1617     }
1618
1619     if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1620         if (recursive) {
1621             /*
1622              * Avoid . and .. directories.
1623              */
1624             char *p;
1625             p = strrchr(src, '/');
1626             if (!p)
1627                 p = strrchr(src, '\\');
1628             if (!p)
1629                 p = src;
1630             else
1631                 p++;
1632             if (!strcmp(p, ".") || !strcmp(p, ".."))
1633                 /* skip . and .. */ ;
1634             else
1635                 rsource(src);
1636         } else {
1637             run_err("%s: not a regular file", src);
1638         }
1639         return;
1640     }
1641
1642     if ((last = strrchr(src, '/')) == NULL)
1643         last = src;
1644     else
1645         last++;
1646     if (strrchr(last, '\\') != NULL)
1647         last = strrchr(last, '\\') + 1;
1648     if (last == src && strchr(src, ':') != NULL)
1649         last = strchr(src, ':') + 1;
1650
1651     f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
1652                    OPEN_EXISTING, 0, 0);
1653     if (f == INVALID_HANDLE_VALUE) {
1654         run_err("%s: Cannot open file", src);
1655         return;
1656     }
1657
1658     if (preserve) {
1659         FILETIME actime, wrtime;
1660         unsigned long mtime, atime;
1661         GetFileTime(f, NULL, &actime, &wrtime);
1662         TIME_WIN_TO_POSIX(actime, atime);
1663         TIME_WIN_TO_POSIX(wrtime, mtime);
1664         if (scp_send_filetimes(mtime, atime))
1665             return;
1666     }
1667
1668     size = GetFileSize(f, NULL);
1669     if (verbose)
1670         tell_user(stderr, "Sending file %s, size=%lu", last, size);
1671     if (scp_send_filename(last, size, 0644))
1672         return;
1673
1674     stat_bytes = 0;
1675     stat_starttime = time(NULL);
1676     stat_lasttime = 0;
1677
1678     for (i = 0; i < size; i += 4096) {
1679         char transbuf[4096];
1680         DWORD j, k = 4096;
1681
1682         if (i + k > size)
1683             k = size - i;
1684         if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
1685             if (statistics)
1686                 printf("\n");
1687             bump("%s: Read error", src);
1688         }
1689         if (scp_send_filedata(transbuf, k))
1690             bump("%s: Network error occurred", src);
1691
1692         if (statistics) {
1693             stat_bytes += k;
1694             if (time(NULL) != stat_lasttime || i + k == size) {
1695                 stat_lasttime = time(NULL);
1696                 print_stats(last, size, stat_bytes,
1697                             stat_starttime, stat_lasttime);
1698             }
1699         }
1700
1701     }
1702     CloseHandle(f);
1703
1704     (void) scp_send_finish();
1705 }
1706
1707 /*
1708  *  Recursively send the contents of a directory.
1709  */
1710 static void rsource(char *src)
1711 {
1712     char *last, *findfile;
1713     char *save_target;
1714     HANDLE dir;
1715     WIN32_FIND_DATA fdat;
1716     int ok;
1717
1718     if ((last = strrchr(src, '/')) == NULL)
1719         last = src;
1720     else
1721         last++;
1722     if (strrchr(last, '\\') != NULL)
1723         last = strrchr(last, '\\') + 1;
1724     if (last == src && strchr(src, ':') != NULL)
1725         last = strchr(src, ':') + 1;
1726
1727     /* maybe send filetime */
1728
1729     save_target = scp_save_remotepath();
1730
1731     if (verbose)
1732         tell_user(stderr, "Entering directory: %s", last);
1733     if (scp_send_dirname(last, 0755))
1734         return;
1735
1736     findfile = dupcat(src, "/*", NULL);
1737     dir = FindFirstFile(findfile, &fdat);
1738     ok = (dir != INVALID_HANDLE_VALUE);
1739     while (ok) {
1740         if (strcmp(fdat.cFileName, ".") == 0 ||
1741             strcmp(fdat.cFileName, "..") == 0) {
1742             /* ignore . and .. */
1743         } else {
1744             char *foundfile = dupcat(src, "/", fdat.cFileName, NULL);
1745             source(foundfile);
1746             sfree(foundfile);
1747         }
1748         ok = FindNextFile(dir, &fdat);
1749     }
1750     FindClose(dir);
1751     sfree(findfile);
1752
1753     (void) scp_send_enddir();
1754
1755     scp_restore_remotepath(save_target);
1756 }
1757
1758 /*
1759  * Execute the sink part of the SCP protocol.
1760  */
1761 static void sink(char *targ, char *src)
1762 {
1763     char *destfname;
1764     int targisdir = 0;
1765     int exists;
1766     DWORD attr;
1767     HANDLE f;
1768     unsigned long received;
1769     int wrerror = 0;
1770     unsigned long stat_bytes;
1771     time_t stat_starttime, stat_lasttime;
1772     char *stat_name;
1773
1774     attr = GetFileAttributes(targ);
1775     if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
1776         targisdir = 1;
1777
1778     if (targetshouldbedirectory && !targisdir)
1779         bump("%s: Not a directory", targ);
1780
1781     scp_sink_init();
1782     while (1) {
1783         struct scp_sink_action act;
1784         if (scp_get_sink_action(&act))
1785             return;
1786
1787         if (act.action == SCP_SINK_ENDDIR)
1788             return;
1789
1790         if (act.action == SCP_SINK_RETRY)
1791             continue;
1792
1793         if (targisdir) {
1794             /*
1795              * Prevent the remote side from maliciously writing to
1796              * files outside the target area by sending a filename
1797              * containing `../'. In fact, it shouldn't be sending
1798              * filenames with any slashes or colons in at all; so
1799              * we'll find the last slash, backslash or colon in the
1800              * filename and use only the part after that. (And
1801              * warn!)
1802              * 
1803              * In addition, we also ensure here that if we're
1804              * copying a single file and the target is a directory
1805              * (common usage: `pscp host:filename .') the remote
1806              * can't send us a _different_ file name. We can
1807              * distinguish this case because `src' will be non-NULL
1808              * and the last component of that will fail to match
1809              * (the last component of) the name sent.
1810              * 
1811              * Well, not always; if `src' is a wildcard, we do
1812              * expect to get back filenames that don't correspond
1813              * exactly to it. Ideally in this case, we would like
1814              * to ensure that the returned filename actually
1815              * matches the wildcard pattern - but one of SCP's
1816              * protocol infelicities is that wildcard matching is
1817              * done at the server end _by the server's rules_ and
1818              * so in general this is infeasible. Hence, we only
1819              * accept filenames that don't correspond to `src' if
1820              * unsafe mode is enabled or we are using SFTP (which
1821              * resolves remote wildcards on the client side and can
1822              * be trusted).
1823              */
1824             char *striptarget, *stripsrc;
1825
1826             striptarget = stripslashes(act.name, 1);
1827             if (striptarget != act.name) {
1828                 tell_user(stderr, "warning: remote host sent a compound"
1829                           " pathname '%s'", act.name);
1830                 tell_user(stderr, "         renaming local file to '%s'",
1831                           striptarget);
1832             }
1833
1834             /*
1835              * Also check to see if the target filename is '.' or
1836              * '..', or indeed '...' and so on because Windows
1837              * appears to interpret those like '..'.
1838              */
1839             if (is_dots(striptarget)) {
1840                 bump("security violation: remote host attempted to write to"
1841                      " a '.' or '..' path!");
1842             }
1843
1844             if (src) {
1845                 stripsrc = stripslashes(src, 1);
1846                 if (strcmp(striptarget, stripsrc) &&
1847                     !using_sftp && !scp_unsafe_mode) {
1848                     tell_user(stderr, "warning: remote host tried to write "
1849                               "to a file called '%s'", striptarget);
1850                     tell_user(stderr, "         when we requested a file "
1851                               "called '%s'.", stripsrc);
1852                     tell_user(stderr, "         If this is a wildcard, "
1853                               "consider upgrading to SSH 2 or using");
1854                     tell_user(stderr, "         the '-unsafe' option. Renaming"
1855                               " of this file has been disallowed.");
1856                     /* Override the name the server provided with our own. */
1857                     striptarget = stripsrc;
1858                 }
1859             }
1860
1861             if (targ[0] != '\0')
1862                 destfname = dupcat(targ, "\\", striptarget, NULL);
1863             else
1864                 destfname = dupstr(striptarget);
1865         } else {
1866             /*
1867              * In this branch of the if, the target area is a
1868              * single file with an explicitly specified name in any
1869              * case, so there's no danger.
1870              */
1871             destfname = dupstr(targ);
1872         }
1873         attr = GetFileAttributes(destfname);
1874         exists = (attr != (DWORD) - 1);
1875
1876         if (act.action == SCP_SINK_DIR) {
1877             if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
1878                 run_err("%s: Not a directory", destfname);
1879                 continue;
1880             }
1881             if (!exists) {
1882                 if (!CreateDirectory(destfname, NULL)) {
1883                     run_err("%s: Cannot create directory", destfname);
1884                     continue;
1885                 }
1886             }
1887             sink(destfname, NULL);
1888             /* can we set the timestamp for directories ? */
1889             continue;
1890         }
1891
1892         f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
1893                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
1894         if (f == INVALID_HANDLE_VALUE) {
1895             run_err("%s: Cannot create file", destfname);
1896             continue;
1897         }
1898
1899         if (scp_accept_filexfer())
1900             return;
1901
1902         stat_bytes = 0;
1903         stat_starttime = time(NULL);
1904         stat_lasttime = 0;
1905         stat_name = stripslashes(destfname, 1);
1906
1907         received = 0;
1908         while (received < act.size) {
1909             char transbuf[4096];
1910             DWORD blksize, read, written;
1911             blksize = 4096;
1912             if (blksize > act.size - received)
1913                 blksize = act.size - received;
1914             read = scp_recv_filedata(transbuf, blksize);
1915             if (read <= 0)
1916                 bump("Lost connection");
1917             if (wrerror)
1918                 continue;
1919             if (!WriteFile(f, transbuf, read, &written, NULL) ||
1920                 written != read) {
1921                 wrerror = 1;
1922                 /* FIXME: in sftp we can actually abort the transfer */
1923                 if (statistics)
1924                     printf("\r%-25.25s | %50s\n",
1925                            stat_name,
1926                            "Write error.. waiting for end of file");
1927                 continue;
1928             }
1929             if (statistics) {
1930                 stat_bytes += read;
1931                 if (time(NULL) > stat_lasttime ||
1932                     received + read == act.size) {
1933                     stat_lasttime = time(NULL);
1934                     print_stats(stat_name, act.size, stat_bytes,
1935                                 stat_starttime, stat_lasttime);
1936                 }
1937             }
1938             received += read;
1939         }
1940         if (act.settime) {
1941             FILETIME actime, wrtime;
1942             TIME_POSIX_TO_WIN(act.atime, actime);
1943             TIME_POSIX_TO_WIN(act.mtime, wrtime);
1944             SetFileTime(f, NULL, &actime, &wrtime);
1945         }
1946
1947         CloseHandle(f);
1948         if (wrerror) {
1949             run_err("%s: Write error", destfname);
1950             continue;
1951         }
1952         (void) scp_finish_filerecv();
1953         sfree(destfname);
1954         sfree(act.buf);
1955     }
1956 }
1957
1958 /*
1959  * We will copy local files to a remote server.
1960  */
1961 static void toremote(int argc, char *argv[])
1962 {
1963     char *src, *targ, *host, *user;
1964     char *cmd;
1965     int i;
1966
1967     targ = argv[argc - 1];
1968
1969     /* Separate host from filename */
1970     host = targ;
1971     targ = colon(targ);
1972     if (targ == NULL)
1973         bump("targ == NULL in toremote()");
1974     *targ++ = '\0';
1975     if (*targ == '\0')
1976         targ = ".";
1977     /* Substitute "." for emtpy target */
1978
1979     /* Separate host and username */
1980     user = host;
1981     host = strrchr(host, '@');
1982     if (host == NULL) {
1983         host = user;
1984         user = NULL;
1985     } else {
1986         *host++ = '\0';
1987         if (*user == '\0')
1988             user = NULL;
1989     }
1990
1991     if (argc == 2) {
1992         /* Find out if the source filespec covers multiple files
1993            if so, we should set the targetshouldbedirectory flag */
1994         HANDLE fh;
1995         WIN32_FIND_DATA fdat;
1996         if (colon(argv[0]) != NULL)
1997             bump("%s: Remote to remote not supported", argv[0]);
1998         fh = FindFirstFile(argv[0], &fdat);
1999         if (fh == INVALID_HANDLE_VALUE)
2000             bump("%s: No such file or directory\n", argv[0]);
2001         if (FindNextFile(fh, &fdat))
2002             targetshouldbedirectory = 1;
2003         FindClose(fh);
2004     }
2005
2006     cmd = smalloc(strlen(targ) + 100);
2007     sprintf(cmd, "scp%s%s%s%s -t %s",
2008             verbose ? " -v" : "",
2009             recursive ? " -r" : "",
2010             preserve ? " -p" : "",
2011             targetshouldbedirectory ? " -d" : "", targ);
2012     do_cmd(host, user, cmd);
2013     sfree(cmd);
2014
2015     scp_source_setup(targ, targetshouldbedirectory);
2016
2017     for (i = 0; i < argc - 1; i++) {
2018         char *srcpath, *last;
2019         HANDLE dir;
2020         WIN32_FIND_DATA fdat;
2021         src = argv[i];
2022         if (colon(src) != NULL) {
2023             tell_user(stderr, "%s: Remote to remote not supported\n", src);
2024             errs++;
2025             continue;
2026         }
2027
2028         /*
2029          * Trim off the last pathname component of `src', to
2030          * provide the base pathname which will be prepended to
2031          * filenames returned from Find{First,Next}File.
2032          */
2033         srcpath = dupstr(src);
2034         last = stripslashes(srcpath, 1);
2035         *last = '\0';
2036
2037         dir = FindFirstFile(src, &fdat);
2038         if (dir == INVALID_HANDLE_VALUE) {
2039             run_err("%s: No such file or directory", src);
2040             continue;
2041         }
2042         do {
2043             char *filename;
2044             /*
2045              * Ensure that . and .. are never matched by wildcards,
2046              * but only by deliberate action.
2047              */
2048             if (!strcmp(fdat.cFileName, ".") ||
2049                 !strcmp(fdat.cFileName, "..")) {
2050                 /*
2051                  * Find*File has returned a special dir. We require
2052                  * that _either_ `src' ends in a backslash followed
2053                  * by that string, _or_ `src' is precisely that
2054                  * string.
2055                  */
2056                 int len = strlen(src), dlen = strlen(fdat.cFileName);
2057                 if (len == dlen && !strcmp(src, fdat.cFileName)) {
2058                     /* ok */ ;
2059                 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
2060                            !strcmp(src + len - dlen, fdat.cFileName)) {
2061                     /* ok */ ;
2062                 } else
2063                     continue;          /* ignore this one */
2064             }
2065             filename = dupcat(srcpath, fdat.cFileName, NULL);
2066             source(filename);
2067             sfree(filename);
2068         } while (FindNextFile(dir, &fdat));
2069         FindClose(dir);
2070         sfree(srcpath);
2071     }
2072 }
2073
2074 /*
2075  *  We will copy files from a remote server to the local machine.
2076  */
2077 static void tolocal(int argc, char *argv[])
2078 {
2079     char *src, *targ, *host, *user;
2080     char *cmd;
2081
2082     if (argc != 2)
2083         bump("More than one remote source not supported");
2084
2085     src = argv[0];
2086     targ = argv[1];
2087
2088     /* Separate host from filename */
2089     host = src;
2090     src = colon(src);
2091     if (src == NULL)
2092         bump("Local to local copy not supported");
2093     *src++ = '\0';
2094     if (*src == '\0')
2095         src = ".";
2096     /* Substitute "." for empty filename */
2097
2098     /* Separate username and hostname */
2099     user = host;
2100     host = strrchr(host, '@');
2101     if (host == NULL) {
2102         host = user;
2103         user = NULL;
2104     } else {
2105         *host++ = '\0';
2106         if (*user == '\0')
2107             user = NULL;
2108     }
2109
2110     cmd = smalloc(strlen(src) + 100);
2111     sprintf(cmd, "scp%s%s%s%s -f %s",
2112             verbose ? " -v" : "",
2113             recursive ? " -r" : "",
2114             preserve ? " -p" : "",
2115             targetshouldbedirectory ? " -d" : "", src);
2116     do_cmd(host, user, cmd);
2117     sfree(cmd);
2118
2119     if (scp_sink_setup(src, preserve, recursive))
2120         return;
2121
2122     sink(targ, src);
2123 }
2124
2125 /*
2126  *  We will issue a list command to get a remote directory.
2127  */
2128 static void get_dir_list(int argc, char *argv[])
2129 {
2130     char *src, *host, *user;
2131     char *cmd, *p, *q;
2132     char c;
2133
2134     src = argv[0];
2135
2136     /* Separate host from filename */
2137     host = src;
2138     src = colon(src);
2139     if (src == NULL)
2140         bump("Local to local copy not supported");
2141     *src++ = '\0';
2142     if (*src == '\0')
2143         src = ".";
2144     /* Substitute "." for empty filename */
2145
2146     /* Separate username and hostname */
2147     user = host;
2148     host = strrchr(host, '@');
2149     if (host == NULL) {
2150         host = user;
2151         user = NULL;
2152     } else {
2153         *host++ = '\0';
2154         if (*user == '\0')
2155             user = NULL;
2156     }
2157
2158     cmd = smalloc(4 * strlen(src) + 100);
2159     strcpy(cmd, "ls -la '");
2160     p = cmd + strlen(cmd);
2161     for (q = src; *q; q++) {
2162         if (*q == '\'') {
2163             *p++ = '\'';
2164             *p++ = '\\';
2165             *p++ = '\'';
2166             *p++ = '\'';
2167         } else {
2168             *p++ = *q;
2169         }
2170     }
2171     *p++ = '\'';
2172     *p = '\0';
2173
2174     do_cmd(host, user, cmd);
2175     sfree(cmd);
2176
2177     if (using_sftp) {
2178         scp_sftp_listdir(src);
2179     } else {
2180         while (ssh_scp_recv(&c, 1) > 0)
2181             tell_char(stdout, c);
2182     }
2183 }
2184
2185 /*
2186  *  Initialize the Win$ock driver.
2187  */
2188 static void init_winsock(void)
2189 {
2190     WORD winsock_ver;
2191     WSADATA wsadata;
2192
2193     winsock_ver = MAKEWORD(1, 1);
2194     if (WSAStartup(winsock_ver, &wsadata))
2195         bump("Unable to initialise WinSock");
2196     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
2197         bump("WinSock version is incompatible with 1.1");
2198 }
2199
2200 /*
2201  *  Short description of parameters.
2202  */
2203 static void usage(void)
2204 {
2205     printf("PuTTY Secure Copy client\n");
2206     printf("%s\n", ver);
2207     printf("Usage: pscp [options] [user@]host:source target\n");
2208     printf
2209         ("       pscp [options] source [source...] [user@]host:target\n");
2210     printf("       pscp [options] -ls user@host:filespec\n");
2211     printf("Options:\n");
2212     printf("  -p        preserve file attributes\n");
2213     printf("  -q        quiet, don't show statistics\n");
2214     printf("  -r        copy directories recursively\n");
2215     printf("  -v        show verbose messages\n");
2216     printf("  -P port   connect to specified port\n");
2217     printf("  -pw passw login with specified password\n");
2218     printf("  -unsafe   allow server-side wildcards (DANGEROUS)\n");
2219 #if 0
2220     /*
2221      * -gui is an internal option, used by GUI front ends to get
2222      * pscp to pass progress reports back to them. It's not an
2223      * ordinary user-accessible option, so it shouldn't be part of
2224      * the command-line help. The only people who need to know
2225      * about it are programmers, and they can read the source.
2226      */
2227     printf
2228         ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");
2229 #endif
2230     exit(1);
2231 }
2232
2233 /*
2234  *  Main program (no, really?)
2235  */
2236 int main(int argc, char *argv[])
2237 {
2238     int i;
2239
2240     default_protocol = PROT_TELNET;
2241
2242     flags = FLAG_STDERR;
2243     ssh_get_line = &get_line;
2244     init_winsock();
2245     sk_init();
2246
2247     for (i = 1; i < argc; i++) {
2248         if (argv[i][0] != '-')
2249             break;
2250         if (strcmp(argv[i], "-v") == 0)
2251             verbose = 1, flags |= FLAG_VERBOSE;
2252         else if (strcmp(argv[i], "-r") == 0)
2253             recursive = 1;
2254         else if (strcmp(argv[i], "-p") == 0)
2255             preserve = 1;
2256         else if (strcmp(argv[i], "-q") == 0)
2257             statistics = 0;
2258         else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
2259             usage();
2260         else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
2261             portnumber = atoi(argv[++i]);
2262         else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
2263             password = argv[++i];
2264         else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
2265             gui_hwnd = argv[++i];
2266             gui_mode = 1;
2267         } else if (strcmp(argv[i], "-ls") == 0)
2268             list = 1;
2269         else if (strcmp(argv[i], "-unsafe") == 0)
2270             scp_unsafe_mode = 1;
2271         else if (strcmp(argv[i], "--") == 0) {
2272             i++;
2273             break;
2274         } else
2275             usage();
2276     }
2277     argc -= i;
2278     argv += i;
2279     back = NULL;
2280
2281     if (list) {
2282         if (argc != 1)
2283             usage();
2284         get_dir_list(argc, argv);
2285
2286     } else {
2287
2288         if (argc < 2)
2289             usage();
2290         if (argc > 2)
2291             targetshouldbedirectory = 1;
2292
2293         if (colon(argv[argc - 1]) != NULL)
2294             toremote(argc, argv);
2295         else
2296             tolocal(argc, argv);
2297     }
2298
2299     if (back != NULL && back->socket() != NULL) {
2300         char ch;
2301         back->special(TS_EOF);
2302         ssh_scp_recv(&ch, 1);
2303     }
2304     WSACleanup();
2305     random_save_seed();
2306
2307     /* GUI Adaptation - August 2000 */
2308     if (gui_mode) {
2309         unsigned int msg_id = WM_RET_ERR_CNT;
2310         if (list)
2311             msg_id = WM_LS_RET_ERR_CNT;
2312         while (!PostMessage
2313                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
2314                 0 /*lParam */ ))SleepEx(1000, TRUE);
2315     }
2316     return (errs == 0 ? 0 : 1);
2317 }
2318
2319 /* end */