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