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