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