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