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