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