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