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