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