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