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