]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - scp.c
cd4fff7503a18543d002e90c31d646442a8514cd
[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.
689  */
690 static char *stripslashes(char *str)
691 {
692     char *p;
693
694     p = strrchr(str, '/');
695     if (p) str = p+1;
696
697     p = strrchr(str, '\\');
698     if (p) str = p+1;
699
700     return str;
701 }
702
703 /*
704  * Determine whether a string is entirely composed of dots.
705  */
706 static int is_dots(char *str)
707 {
708     return str[strspn(str, ".")] == '\0';
709 }
710
711 /*
712  *  Wait for a response from the other side.
713  *  Return 0 if ok, -1 if error.
714  */
715 static int response(void)
716 {
717     char ch, resp, rbuf[2048];
718     int p;
719
720     if (ssh_scp_recv(&resp, 1) <= 0)
721         bump("Lost connection");
722
723     p = 0;
724     switch (resp) {
725       case 0:                          /* ok */
726         return (0);
727       default:
728         rbuf[p++] = resp;
729         /* fallthrough */
730       case 1:                          /* error */
731       case 2:                          /* fatal error */
732         do {
733             if (ssh_scp_recv(&ch, 1) <= 0)
734                 bump("Protocol error: Lost connection");
735             rbuf[p++] = ch;
736         } while (p < sizeof(rbuf) && ch != '\n');
737         rbuf[p - 1] = '\0';
738         if (resp == 1)
739             tell_user(stderr, "%s\n", rbuf);
740         else
741             bump("%s", rbuf);
742         errs++;
743         return (-1);
744     }
745 }
746
747 int sftp_recvdata(char *buf, int len)
748 {
749     return ssh_scp_recv(buf, len);
750 }
751 int sftp_senddata(char *buf, int len)
752 {
753     back->send((unsigned char *) buf, len);
754     return 1;
755 }
756
757 /* ----------------------------------------------------------------------
758  * sftp-based replacement for the hacky `pscp -ls'.
759  */
760 static int sftp_ls_compare(const void *av, const void *bv)
761 {
762     const struct fxp_name *a = (const struct fxp_name *) av;
763     const struct fxp_name *b = (const struct fxp_name *) bv;
764     return strcmp(a->filename, b->filename);
765 }
766 void scp_sftp_listdir(char *dirname)
767 {
768     struct fxp_handle *dirh;
769     struct fxp_names *names;
770     struct fxp_name *ournames;
771     int nnames, namesize;
772     char *dir;
773     int i;
774
775     printf("Listing directory %s\n", dirname);
776
777     dirh = fxp_opendir(dirname);
778     if (dirh == NULL) {
779         printf("Unable to open %s: %s\n", dir, fxp_error());
780     } else {
781         nnames = namesize = 0;
782         ournames = NULL;
783
784         while (1) {
785
786             names = fxp_readdir(dirh);
787             if (names == NULL) {
788                 if (fxp_error_type() == SSH_FX_EOF)
789                     break;
790                 printf("Reading directory %s: %s\n", dir, fxp_error());
791                 break;
792             }
793             if (names->nnames == 0) {
794                 fxp_free_names(names);
795                 break;
796             }
797
798             if (nnames + names->nnames >= namesize) {
799                 namesize += names->nnames + 128;
800                 ournames =
801                     srealloc(ournames, namesize * sizeof(*ournames));
802             }
803
804             for (i = 0; i < names->nnames; i++)
805                 ournames[nnames++] = names->names[i];
806
807             names->nnames = 0;         /* prevent free_names */
808             fxp_free_names(names);
809         }
810         fxp_close(dirh);
811
812         /*
813          * Now we have our filenames. Sort them by actual file
814          * name, and then output the longname parts.
815          */
816         qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
817
818         /*
819          * And print them.
820          */
821         for (i = 0; i < nnames; i++)
822             printf("%s\n", ournames[i].longname);
823     }
824 }
825
826 /* ----------------------------------------------------------------------
827  * Helper routines that contain the actual SCP protocol elements,
828  * implemented both as SCP1 and SFTP.
829  */
830
831 static struct scp_sftp_dirstack {
832     struct scp_sftp_dirstack *next;
833     struct fxp_name *names;
834     int namepos, namelen;
835     char *dirpath;
836 } *scp_sftp_dirstack_head;
837 static char *scp_sftp_remotepath, *scp_sftp_currentname;
838 static int scp_sftp_targetisdir, scp_sftp_donethistarget;
839 static int scp_sftp_preserve, scp_sftp_recursive;
840 static unsigned long scp_sftp_mtime, scp_sftp_atime;
841 static int scp_has_times;
842 static struct fxp_handle *scp_sftp_filehandle;
843 static uint64 scp_sftp_fileoffset;
844
845 void scp_source_setup(char *target, int shouldbedir)
846 {
847     if (using_sftp) {
848         /*
849          * Find out whether the target filespec is in fact a
850          * directory.
851          */
852         struct fxp_attrs attrs;
853
854         if (!fxp_stat(target, &attrs) ||
855             !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))
856             scp_sftp_targetisdir = 0;
857         else
858             scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0;
859
860         if (shouldbedir && !scp_sftp_targetisdir) {
861             bump("pscp: remote filespec %s: not a directory\n", target);
862         }
863
864         scp_sftp_remotepath = dupstr(target);
865
866         scp_has_times = 0;
867     } else {
868         (void) response();
869     }
870 }
871
872 int scp_send_errmsg(char *str)
873 {
874     if (using_sftp) {
875         /* do nothing; we never need to send our errors to the server */
876     } else {
877         back->send("\001", 1);         /* scp protocol error prefix */
878         back->send(str, strlen(str));
879     }
880     return 0;                          /* can't fail */
881 }
882
883 int scp_send_filetimes(unsigned long mtime, unsigned long atime)
884 {
885     if (using_sftp) {
886         scp_sftp_mtime = mtime;
887         scp_sftp_atime = atime;
888         scp_has_times = 1;
889         return 0;
890     } else {
891         char buf[80];
892         sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
893         back->send(buf, strlen(buf));
894         return response();
895     }
896 }
897
898 int scp_send_filename(char *name, unsigned long size, int modes)
899 {
900     if (using_sftp) {
901         char *fullname;
902         if (scp_sftp_targetisdir) {
903             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
904         } else {
905             fullname = dupstr(scp_sftp_remotepath);
906         }
907         scp_sftp_filehandle =
908             fxp_open(fullname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
909         if (!scp_sftp_filehandle) {
910             tell_user(stderr, "pscp: unable to open %s: %s",
911                       fullname, fxp_error());
912             errs++;
913             return 1;
914         }
915         scp_sftp_fileoffset = uint64_make(0, 0);
916         sfree(fullname);
917         return 0;
918     } else {
919         char buf[40];
920         sprintf(buf, "C%04o %lu ", modes, size);
921         back->send(buf, strlen(buf));
922         back->send(name, strlen(name));
923         back->send("\n", 1);
924         return response();
925     }
926 }
927
928 int scp_send_filedata(char *data, int len)
929 {
930     if (using_sftp) {
931         if (!scp_sftp_filehandle) {
932             return 1;
933         }
934         if (!fxp_write(scp_sftp_filehandle, data, scp_sftp_fileoffset, len)) {
935             tell_user(stderr, "error while writing: %s\n", fxp_error());
936             errs++;
937             return 1;
938         }
939         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);
940         return 0;
941     } else {
942         int bufsize = back->send(data, len);
943
944         /*
945          * If the network transfer is backing up - that is, the
946          * remote site is not accepting data as fast as we can
947          * produce it - then we must loop on network events until
948          * we have space in the buffer again.
949          */
950         while (bufsize > MAX_SCP_BUFSIZE) {
951             if (!scp_process_network_event())
952                 return 1;
953             bufsize = back->sendbuffer();
954         }
955
956         return 0;
957     }
958 }
959
960 int scp_send_finish(void)
961 {
962     if (using_sftp) {
963         struct fxp_attrs attrs;
964         if (!scp_sftp_filehandle) {
965             return 1;
966         }
967         if (scp_has_times) {
968             attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;
969             attrs.atime = scp_sftp_atime;
970             attrs.mtime = scp_sftp_mtime;
971             if (!fxp_fsetstat(scp_sftp_filehandle, attrs)) {
972                 tell_user(stderr, "unable to set file times: %s\n", fxp_error());
973                 errs++;
974             }
975         }
976         fxp_close(scp_sftp_filehandle);
977         scp_has_times = 0;
978         return 0;
979     } else {
980         back->send("", 1);
981         return response();
982     }
983 }
984
985 char *scp_save_remotepath(void)
986 {
987     if (using_sftp)
988         return scp_sftp_remotepath;
989     else
990         return NULL;
991 }
992
993 void scp_restore_remotepath(char *data)
994 {
995     if (using_sftp)
996         scp_sftp_remotepath = data;
997 }
998
999 int scp_send_dirname(char *name, int modes)
1000 {
1001     if (using_sftp) {
1002         char *fullname;
1003         char const *err;
1004         struct fxp_attrs attrs;
1005         if (scp_sftp_targetisdir) {
1006             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
1007         } else {
1008             fullname = dupstr(scp_sftp_remotepath);
1009         }
1010
1011         /*
1012          * We don't worry about whether we managed to create the
1013          * directory, because if it exists already it's OK just to
1014          * use it. Instead, we will stat it afterwards, and if it
1015          * exists and is a directory we will assume we were either
1016          * successful or it didn't matter.
1017          */
1018         if (!fxp_mkdir(fullname))
1019             err = fxp_error();
1020         else
1021             err = "server reported no error";
1022         if (!fxp_stat(fullname, &attrs) ||
1023             !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
1024             !(attrs.permissions & 0040000)) {
1025             tell_user(stderr, "unable to create directory %s: %s",
1026                       fullname, err);
1027             errs++;
1028             return 1;
1029         }
1030
1031         scp_sftp_remotepath = fullname;
1032
1033         return 0;
1034     } else {
1035         char buf[40];
1036         sprintf(buf, "D%04o 0 ", modes);
1037         back->send(buf, strlen(buf));
1038         back->send(name, strlen(name));
1039         back->send("\n", 1);
1040         return response();
1041     }
1042 }
1043
1044 int scp_send_enddir(void)
1045 {
1046     if (using_sftp) {
1047         sfree(scp_sftp_remotepath);
1048         return 0;
1049     } else {
1050         back->send("E\n", 2);
1051         return response();
1052     }
1053 }
1054
1055 /*
1056  * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init.
1057  * That's bad. The difference is that scp_sink_setup is called once
1058  * right at the start, whereas scp_sink_init is called to
1059  * initialise every level of recursion in the protocol.
1060  */
1061 void scp_sink_setup(char *source, int preserve, int recursive)
1062 {
1063     if (using_sftp) {
1064         scp_sftp_remotepath = dupstr(source);
1065         scp_sftp_preserve = preserve;
1066         scp_sftp_recursive = recursive;
1067         scp_sftp_donethistarget = 0;
1068         scp_sftp_dirstack_head = NULL;
1069     }
1070 }
1071
1072 int scp_sink_init(void)
1073 {
1074     if (!using_sftp) {
1075         back->send("", 1);
1076     }
1077     return 0;
1078 }
1079
1080 #define SCP_SINK_FILE   1
1081 #define SCP_SINK_DIR    2
1082 #define SCP_SINK_ENDDIR 3
1083 struct scp_sink_action {
1084     int action;                        /* FILE, DIR, ENDDIR */
1085     char *buf;                         /* will need freeing after use */
1086     char *name;                        /* filename or dirname (not ENDDIR) */
1087     int mode;                          /* access mode (not ENDDIR) */
1088     unsigned long size;                /* file size (not ENDDIR) */
1089     int settime;                       /* 1 if atime and mtime are filled */
1090     unsigned long atime, mtime;        /* access times for the file */
1091 };
1092
1093 int scp_get_sink_action(struct scp_sink_action *act)
1094 {
1095     if (using_sftp) {
1096         char *fname;
1097         int must_free_fname;
1098         struct fxp_attrs attrs;
1099         int ret;
1100
1101         if (!scp_sftp_dirstack_head) {
1102             if (!scp_sftp_donethistarget) {
1103                 /*
1104                  * Simple case: we are only dealing with one file.
1105                  */
1106                 fname = scp_sftp_remotepath;
1107                 must_free_fname = 0;
1108                 scp_sftp_donethistarget = 1;
1109             } else {
1110                 /*
1111                  * Even simpler case: one file _which we've done_.
1112                  * Return 1 (finished).
1113                  */
1114                 return 1;
1115             }
1116         } else {
1117             /*
1118              * We're now in the middle of stepping through a list
1119              * of names returned from fxp_readdir(); so let's carry
1120              * on.
1121              */
1122             struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;
1123             while (head->namepos < head->namelen &&
1124                    is_dots(head->names[head->namepos].filename))
1125                 head->namepos++;       /* skip . and .. */
1126             if (head->namepos < head->namelen) {
1127                 fname = dupcat(head->dirpath, "/",
1128                                head->names[head->namepos++].filename,
1129                                NULL);
1130                 must_free_fname = 1;
1131             } else {
1132                 /*
1133                  * We've come to the end of the list; pop it off
1134                  * the stack and return an ENDDIR action.
1135                  */
1136                 
1137                 sfree(head->dirpath);
1138                 sfree(head->names);
1139                 scp_sftp_dirstack_head = head->next;
1140                 sfree(head);
1141
1142                 act->action = SCP_SINK_ENDDIR;
1143                 return 0;
1144             }
1145         }
1146
1147         /*
1148          * Now we have a filename. Stat it, and see if it's a file
1149          * or a directory.
1150          */
1151         ret = fxp_stat(fname, &attrs);
1152         if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
1153             tell_user(stderr, "unable to identify %s: %s", fname,
1154                       ret ? "file type not supplied" : fxp_error());
1155             errs++;
1156             return 1;
1157         }
1158
1159         if (attrs.permissions & 0040000) {
1160             struct scp_sftp_dirstack *newitem;
1161             struct fxp_handle *dirhandle;
1162             int nnames, namesize;
1163             struct fxp_name *ournames;
1164             struct fxp_names *names;
1165
1166             /*
1167              * It's a directory. If we're not in recursive
1168              * mode, this just merits a complaint.
1169              */
1170             if (!scp_sftp_recursive) {
1171                 tell_user(stderr, "pscp: %s: is a directory", fname);
1172                 errs++;
1173                 if (must_free_fname) sfree(fname);
1174                 return 1;
1175             }
1176
1177             /*
1178              * Otherwise, the fun begins. We must fxp_opendir() the
1179              * directory, slurp the filenames into memory, return
1180              * SCP_SINK_DIR, and set targetisdir. The next time
1181              * we're called, we will run through the list of
1182              * filenames one by one.
1183              * 
1184              * If targetisdir is _already_ set (meaning we're
1185              * already in the middle of going through another such
1186              * list), we must push the other (target,namelist) pair
1187              * on a stack.
1188              */
1189             dirhandle = fxp_opendir(fname);
1190             if (!dirhandle) {
1191                 tell_user(stderr, "scp: unable to open directory %s: %s",
1192                           fname, fxp_error());
1193                 if (must_free_fname) sfree(fname);
1194                 errs++;
1195                 return 1;
1196             }
1197             nnames = namesize = 0;
1198             ournames = NULL;
1199             while (1) {
1200                 int i;
1201
1202                 names = fxp_readdir(dirhandle);
1203                 if (names == NULL) {
1204                     if (fxp_error_type() == SSH_FX_EOF)
1205                         break;
1206                     tell_user(stderr, "scp: reading directory %s: %s\n",
1207                               fname, fxp_error());
1208                     if (must_free_fname) sfree(fname);
1209                     sfree(ournames);
1210                     errs++;
1211                     return 1;
1212                 }
1213                 if (names->nnames == 0) {
1214                     fxp_free_names(names);
1215                     break;
1216                 }
1217                 if (nnames + names->nnames >= namesize) {
1218                     namesize += names->nnames + 128;
1219                     ournames =
1220                         srealloc(ournames, namesize * sizeof(*ournames));
1221                 }
1222                 for (i = 0; i < names->nnames; i++)
1223                     ournames[nnames++] = names->names[i];
1224                 names->nnames = 0;             /* prevent free_names */
1225                 fxp_free_names(names);
1226             }
1227             fxp_close(dirhandle);
1228
1229             newitem = smalloc(sizeof(struct scp_sftp_dirstack));
1230             newitem->next = scp_sftp_dirstack_head;
1231             newitem->names = ournames;
1232             newitem->namepos = 0;
1233             newitem->namelen = nnames;
1234             if (must_free_fname)
1235                 newitem->dirpath = fname;
1236             else
1237                 newitem->dirpath = dupstr(fname);
1238             scp_sftp_dirstack_head = newitem;
1239
1240             act->action = SCP_SINK_DIR;
1241             act->buf = dupstr(stripslashes(fname));
1242             act->name = act->buf;
1243             act->size = 0;             /* duhh, it's a directory */
1244             act->mode = 07777 & attrs.permissions;
1245             if (scp_sftp_preserve &&
1246                 (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
1247                 act->atime = attrs.atime;
1248                 act->mtime = attrs.mtime;
1249                 act->settime = 1;
1250             } else
1251                 act->settime = 0;
1252             return 0;
1253
1254         } else {
1255             /*
1256              * It's a file. Return SCP_SINK_FILE.
1257              */
1258             act->action = SCP_SINK_FILE;
1259             act->buf = dupstr(stripslashes(fname));
1260             act->name = act->buf;
1261             if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
1262                 if (uint64_compare(attrs.size,
1263                                    uint64_make(0, ULONG_MAX)) > 0) {
1264                     act->size = ULONG_MAX;   /* *boggle* */
1265                 } else
1266                     act->size = attrs.size.lo;
1267             } else
1268                 act->size = ULONG_MAX;   /* no idea */
1269             act->mode = 07777 & attrs.permissions;
1270             if (scp_sftp_preserve &&
1271                 (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
1272                 act->atime = attrs.atime;
1273                 act->mtime = attrs.mtime;
1274                 act->settime = 1;
1275             } else
1276                 act->settime = 0;
1277             if (must_free_fname)
1278                 scp_sftp_currentname = fname;
1279             else
1280                 scp_sftp_currentname = dupstr(fname);
1281             return 0;
1282         }
1283
1284     } else {
1285         int done = 0;
1286         int i, bufsize;
1287         int action;
1288         char ch;
1289
1290         act->settime = 0;
1291         act->buf = NULL;
1292         bufsize = 0;
1293
1294         while (!done) {
1295             if (ssh_scp_recv(&ch, 1) <= 0)
1296                 return 1;
1297             if (ch == '\n')
1298                 bump("Protocol error: Unexpected newline");
1299             i = 0;
1300             action = ch;
1301             do {
1302                 if (ssh_scp_recv(&ch, 1) <= 0)
1303                     bump("Lost connection");
1304                 if (i >= bufsize) {
1305                     bufsize = i + 128;
1306                     act->buf = srealloc(act->buf, bufsize);
1307                 }
1308                 act->buf[i++] = ch;
1309             } while (ch != '\n');
1310             act->buf[i - 1] = '\0';
1311             switch (action) {
1312               case '\01':                      /* error */
1313                 tell_user(stderr, "%s\n", act->buf);
1314                 errs++;
1315                 continue;                      /* go round again */
1316               case '\02':                      /* fatal error */
1317                 bump("%s", act->buf);
1318               case 'E':
1319                 back->send("", 1);
1320                 act->action = SCP_SINK_ENDDIR;
1321                 return 0;
1322               case 'T':
1323                 if (sscanf(act->buf, "%ld %*d %ld %*d",
1324                            &act->mtime, &act->atime) == 2) {
1325                     act->settime = 1;
1326                     back->send("", 1);
1327                     continue;          /* go round again */
1328                 }
1329                 bump("Protocol error: Illegal time format");
1330               case 'C':
1331               case 'D':
1332                 act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
1333                 break;
1334               default:
1335                 bump("Protocol error: Expected control record");
1336             }
1337             /*
1338              * We will go round this loop only once, unless we hit
1339              * `continue' above.
1340              */
1341             done = 1;
1342         }
1343
1344         /*
1345          * If we get here, we must have seen SCP_SINK_FILE or
1346          * SCP_SINK_DIR.
1347          */
1348         if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
1349             bump("Protocol error: Illegal file descriptor format");
1350         act->name = act->buf + i;
1351         return 0;
1352     }
1353 }
1354
1355 int scp_accept_filexfer(void)
1356 {
1357     if (using_sftp) {
1358         scp_sftp_filehandle =
1359             fxp_open(scp_sftp_currentname, SSH_FXF_READ);
1360         if (!scp_sftp_filehandle) {
1361             tell_user(stderr, "pscp: unable to open %s: %s",
1362                       scp_sftp_currentname, fxp_error());
1363             errs++;
1364             return 1;
1365         }
1366         scp_sftp_fileoffset = uint64_make(0, 0);
1367         sfree(scp_sftp_currentname);
1368         return 0;
1369     } else {
1370         back->send("", 1);
1371         return 0;                      /* can't fail */
1372     }
1373 }
1374
1375 int scp_recv_filedata(char *data, int len)
1376 {
1377     if (using_sftp) {
1378         int actuallen = fxp_read(scp_sftp_filehandle, data,
1379                                  scp_sftp_fileoffset, len);
1380         if (actuallen == -1 && fxp_error_type() != SSH_FX_EOF) {
1381             tell_user(stderr, "pscp: error while reading: %s", fxp_error());
1382             errs++;
1383             return -1;
1384         }
1385         if (actuallen < 0)
1386             actuallen = 0;
1387
1388         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, actuallen);
1389
1390         return actuallen;
1391     } else {
1392         return ssh_scp_recv(data, len);
1393     }
1394 }
1395
1396 int scp_finish_filerecv(void)
1397 {
1398     if (using_sftp) {
1399         fxp_close(scp_sftp_filehandle);
1400         return 0;
1401     } else {
1402         back->send("", 1);
1403         return response();
1404     }
1405 }
1406
1407 /* ----------------------------------------------------------------------
1408  *  Send an error message to the other side and to the screen.
1409  *  Increment error counter.
1410  */
1411 static void run_err(const char *fmt, ...)
1412 {
1413     char str[2048];
1414     va_list ap;
1415     va_start(ap, fmt);
1416     errs++;
1417     strcpy(str, "scp: ");
1418     vsprintf(str + strlen(str), fmt, ap);
1419     strcat(str, "\n");
1420     scp_send_errmsg(str);
1421     tell_user(stderr, "%s", str);
1422     va_end(ap);
1423 }
1424
1425 /*
1426  *  Execute the source part of the SCP protocol.
1427  */
1428 static void source(char *src)
1429 {
1430     unsigned long size;
1431     char *last;
1432     HANDLE f;
1433     DWORD attr;
1434     unsigned long i;
1435     unsigned long stat_bytes;
1436     time_t stat_starttime, stat_lasttime;
1437
1438     attr = GetFileAttributes(src);
1439     if (attr == (DWORD) - 1) {
1440         run_err("%s: No such file or directory", src);
1441         return;
1442     }
1443
1444     if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1445         if (recursive) {
1446             /*
1447              * Avoid . and .. directories.
1448              */
1449             char *p;
1450             p = strrchr(src, '/');
1451             if (!p)
1452                 p = strrchr(src, '\\');
1453             if (!p)
1454                 p = src;
1455             else
1456                 p++;
1457             if (!strcmp(p, ".") || !strcmp(p, ".."))
1458                 /* skip . and .. */ ;
1459             else
1460                 rsource(src);
1461         } else {
1462             run_err("%s: not a regular file", src);
1463         }
1464         return;
1465     }
1466
1467     if ((last = strrchr(src, '/')) == NULL)
1468         last = src;
1469     else
1470         last++;
1471     if (strrchr(last, '\\') != NULL)
1472         last = strrchr(last, '\\') + 1;
1473     if (last == src && strchr(src, ':') != NULL)
1474         last = strchr(src, ':') + 1;
1475
1476     f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
1477                    OPEN_EXISTING, 0, 0);
1478     if (f == INVALID_HANDLE_VALUE) {
1479         run_err("%s: Cannot open file", src);
1480         return;
1481     }
1482
1483     if (preserve) {
1484         FILETIME actime, wrtime;
1485         unsigned long mtime, atime;
1486         GetFileTime(f, NULL, &actime, &wrtime);
1487         TIME_WIN_TO_POSIX(actime, atime);
1488         TIME_WIN_TO_POSIX(wrtime, mtime);
1489         if (scp_send_filetimes(mtime, atime))
1490             return;
1491     }
1492
1493     size = GetFileSize(f, NULL);
1494     if (verbose)
1495         tell_user(stderr, "Sending file %s, size=%lu", last, size);
1496     if (scp_send_filename(last, size, 0644))
1497         return;
1498
1499     stat_bytes = 0;
1500     stat_starttime = time(NULL);
1501     stat_lasttime = 0;
1502
1503     for (i = 0; i < size; i += 4096) {
1504         char transbuf[4096];
1505         DWORD j, k = 4096;
1506
1507         if (i + k > size)
1508             k = size - i;
1509         if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
1510             if (statistics)
1511                 printf("\n");
1512             bump("%s: Read error", src);
1513         }
1514         if (scp_send_filedata(transbuf, k))
1515             bump("%s: Network error occurred", src);
1516
1517         if (statistics) {
1518             stat_bytes += k;
1519             if (time(NULL) != stat_lasttime || i + k == size) {
1520                 stat_lasttime = time(NULL);
1521                 print_stats(last, size, stat_bytes,
1522                             stat_starttime, stat_lasttime);
1523             }
1524         }
1525
1526     }
1527     CloseHandle(f);
1528
1529     (void) scp_send_finish();
1530 }
1531
1532 /*
1533  *  Recursively send the contents of a directory.
1534  */
1535 static void rsource(char *src)
1536 {
1537     char *last, *findfile;
1538     char *save_target;
1539     HANDLE dir;
1540     WIN32_FIND_DATA fdat;
1541     int ok;
1542
1543     if ((last = strrchr(src, '/')) == NULL)
1544         last = src;
1545     else
1546         last++;
1547     if (strrchr(last, '\\') != NULL)
1548         last = strrchr(last, '\\') + 1;
1549     if (last == src && strchr(src, ':') != NULL)
1550         last = strchr(src, ':') + 1;
1551
1552     /* maybe send filetime */
1553
1554     save_target = scp_save_remotepath();
1555
1556     if (verbose)
1557         tell_user(stderr, "Entering directory: %s", last);
1558     if (scp_send_dirname(last, 0755))
1559         return;
1560
1561     findfile = dupcat(src, "/*", NULL);
1562     dir = FindFirstFile(findfile, &fdat);
1563     ok = (dir != INVALID_HANDLE_VALUE);
1564     while (ok) {
1565         if (strcmp(fdat.cFileName, ".") == 0 ||
1566             strcmp(fdat.cFileName, "..") == 0) {
1567             /* ignore . and .. */
1568         } else {
1569             char *foundfile = dupcat(src, "/", fdat.cFileName, NULL);
1570             source(foundfile);
1571             sfree(foundfile);
1572         }
1573         ok = FindNextFile(dir, &fdat);
1574     }
1575     FindClose(dir);
1576     sfree(findfile);
1577
1578     (void) scp_send_enddir();
1579
1580     scp_restore_remotepath(save_target);
1581 }
1582
1583 /*
1584  * Execute the sink part of the SCP protocol.
1585  */
1586 static void sink(char *targ, char *src)
1587 {
1588     char *destfname;
1589     char ch;
1590     int targisdir = 0;
1591     int settime;
1592     int exists;
1593     DWORD attr;
1594     HANDLE f;
1595     unsigned long received;
1596     int wrerror = 0;
1597     unsigned long stat_bytes;
1598     time_t stat_starttime, stat_lasttime;
1599     char *stat_name;
1600
1601     attr = GetFileAttributes(targ);
1602     if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
1603         targisdir = 1;
1604
1605     if (targetshouldbedirectory && !targisdir)
1606         bump("%s: Not a directory", targ);
1607
1608     scp_sink_init();
1609     while (1) {
1610         struct scp_sink_action act;
1611         if (scp_get_sink_action(&act))
1612             return;
1613
1614         if (act.action == SCP_SINK_ENDDIR)
1615             return;
1616
1617         if (targisdir) {
1618             /*
1619              * Prevent the remote side from maliciously writing to
1620              * files outside the target area by sending a filename
1621              * containing `../'. In fact, it shouldn't be sending
1622              * filenames with any slashes in at all; so we'll find
1623              * the last slash or backslash in the filename and use
1624              * only the part after that. (And warn!)
1625              * 
1626              * In addition, we also ensure here that if we're
1627              * copying a single file and the target is a directory
1628              * (common usage: `pscp host:filename .') the remote
1629              * can't send us a _different_ file name. We can
1630              * distinguish this case because `src' will be non-NULL
1631              * and the last component of that will fail to match
1632              * (the last component of) the name sent.
1633              * 
1634              * (Well, not always; if `src' is a wildcard, we do
1635              * expect to get back filenames that don't correspond
1636              * exactly to it. So we skip this check if `src'
1637              * contains a *, a ? or a []. This is non-ideal - we
1638              * would like to ensure that the returned filename
1639              * actually matches the wildcard pattern - but one of
1640              * SCP's protocol infelicities is that wildcard
1641              * matching is done at the server end _by the server's
1642              * rules_ and so in general this is infeasible. Live
1643              * with it, or upgrade to SFTP.)
1644              */
1645             char *striptarget, *stripsrc;
1646
1647             striptarget = stripslashes(act.name);
1648             if (striptarget != act.name) {
1649                 tell_user(stderr, "warning: remote host sent a compound"
1650                           " pathname - possibly malicious! (ignored)");
1651             }
1652
1653             /*
1654              * Also check to see if the target filename is '.' or
1655              * '..', or indeed '...' and so on because Windows
1656              * appears to interpret those like '..'.
1657              */
1658             if (is_dots(striptarget)) {
1659                 bump("security violation: remote host attempted to write to"
1660                      " a '.' or '..' path!");
1661             }
1662
1663             if (src) {
1664                 stripsrc = stripslashes(src);
1665                 if (!stripsrc[strcspn(stripsrc, "*?[]")] &&
1666                     strcmp(striptarget, stripsrc)) {
1667                     tell_user(stderr, "warning: remote host attempted to"
1668                               " write to a different filename: disallowing");
1669                     /* Override the name the server provided with our own. */
1670                     striptarget = stripsrc;
1671                 }
1672             }
1673
1674             if (targ[0] != '\0')
1675                 destfname = dupcat(targ, "\\", striptarget, NULL);
1676             else
1677                 destfname = dupstr(striptarget);
1678         } else {
1679             /*
1680              * In this branch of the if, the target area is a
1681              * single file with an explicitly specified name in any
1682              * case, so there's no danger.
1683              */
1684             destfname = dupstr(targ);
1685         }
1686         attr = GetFileAttributes(destfname);
1687         exists = (attr != (DWORD) - 1);
1688
1689         if (act.action == SCP_SINK_DIR) {
1690             if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
1691                 run_err("%s: Not a directory", destfname);
1692                 continue;
1693             }
1694             if (!exists) {
1695                 if (!CreateDirectory(destfname, NULL)) {
1696                     run_err("%s: Cannot create directory", destfname);
1697                     continue;
1698                 }
1699             }
1700             sink(destfname, NULL);
1701             /* can we set the timestamp for directories ? */
1702             continue;
1703         }
1704
1705         f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
1706                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
1707         if (f == INVALID_HANDLE_VALUE) {
1708             run_err("%s: Cannot create file", destfname);
1709             continue;
1710         }
1711
1712         if (scp_accept_filexfer())
1713             return;
1714
1715         stat_bytes = 0;
1716         stat_starttime = time(NULL);
1717         stat_lasttime = 0;
1718         stat_name = stripslashes(destfname);
1719
1720         received = 0;
1721         while (received < act.size) {
1722             char transbuf[4096];
1723             DWORD blksize, read, written;
1724             blksize = 4096;
1725             if (blksize > act.size - received)
1726                 blksize = act.size - received;
1727             read = scp_recv_filedata(transbuf, blksize);
1728             if (read <= 0)
1729                 bump("Lost connection");
1730             if (wrerror)
1731                 continue;
1732             if (!WriteFile(f, transbuf, read, &written, NULL) ||
1733                 written != read) {
1734                 wrerror = 1;
1735                 /* FIXME: in sftp we can actually abort the transfer */
1736                 if (statistics)
1737                     printf("\r%-25.25s | %50s\n",
1738                            stat_name,
1739                            "Write error.. waiting for end of file");
1740                 continue;
1741             }
1742             if (statistics) {
1743                 stat_bytes += read;
1744                 if (time(NULL) > stat_lasttime ||
1745                     received + read == act.size) {
1746                     stat_lasttime = time(NULL);
1747                     print_stats(stat_name, act.size, stat_bytes,
1748                                 stat_starttime, stat_lasttime);
1749                 }
1750             }
1751             received += read;
1752         }
1753         if (act.settime) {
1754             FILETIME actime, wrtime;
1755             TIME_POSIX_TO_WIN(act.atime, actime);
1756             TIME_POSIX_TO_WIN(act.mtime, wrtime);
1757             SetFileTime(f, NULL, &actime, &wrtime);
1758         }
1759
1760         CloseHandle(f);
1761         if (wrerror) {
1762             run_err("%s: Write error", destfname);
1763             continue;
1764         }
1765         (void) scp_finish_filerecv();
1766         sfree(destfname);
1767         sfree(act.name);
1768     }
1769 }
1770
1771 /*
1772  * We will copy local files to a remote server.
1773  */
1774 static void toremote(int argc, char *argv[])
1775 {
1776     char *src, *targ, *host, *user;
1777     char *cmd;
1778     int i;
1779
1780     targ = argv[argc - 1];
1781
1782     /* Separate host from filename */
1783     host = targ;
1784     targ = colon(targ);
1785     if (targ == NULL)
1786         bump("targ == NULL in toremote()");
1787     *targ++ = '\0';
1788     if (*targ == '\0')
1789         targ = ".";
1790     /* Substitute "." for emtpy target */
1791
1792     /* Separate host and username */
1793     user = host;
1794     host = strrchr(host, '@');
1795     if (host == NULL) {
1796         host = user;
1797         user = NULL;
1798     } else {
1799         *host++ = '\0';
1800         if (*user == '\0')
1801             user = NULL;
1802     }
1803
1804     if (argc == 2) {
1805         /* Find out if the source filespec covers multiple files
1806            if so, we should set the targetshouldbedirectory flag */
1807         HANDLE fh;
1808         WIN32_FIND_DATA fdat;
1809         if (colon(argv[0]) != NULL)
1810             bump("%s: Remote to remote not supported", argv[0]);
1811         fh = FindFirstFile(argv[0], &fdat);
1812         if (fh == INVALID_HANDLE_VALUE)
1813             bump("%s: No such file or directory\n", argv[0]);
1814         if (FindNextFile(fh, &fdat))
1815             targetshouldbedirectory = 1;
1816         FindClose(fh);
1817     }
1818
1819     cmd = smalloc(strlen(targ) + 100);
1820     sprintf(cmd, "scp%s%s%s%s -t %s",
1821             verbose ? " -v" : "",
1822             recursive ? " -r" : "",
1823             preserve ? " -p" : "",
1824             targetshouldbedirectory ? " -d" : "", targ);
1825     do_cmd(host, user, cmd);
1826     sfree(cmd);
1827
1828     scp_source_setup(targ, targetshouldbedirectory);
1829
1830     for (i = 0; i < argc - 1; i++) {
1831         char *srcpath, *last;
1832         HANDLE dir;
1833         WIN32_FIND_DATA fdat;
1834         src = argv[i];
1835         if (colon(src) != NULL) {
1836             tell_user(stderr, "%s: Remote to remote not supported\n", src);
1837             errs++;
1838             continue;
1839         }
1840
1841         /*
1842          * Trim off the last pathname component of `src', to
1843          * provide the base pathname which will be prepended to
1844          * filenames returned from Find{First,Next}File.
1845          */
1846         srcpath = dupstr(src);
1847         last = stripslashes(srcpath);
1848         if (last == srcpath) {
1849             last = strchr(srcpath, ':');
1850             if (last)
1851                 last++;
1852             else
1853                 last = srcpath;
1854         }
1855         *last = '\0';
1856
1857         dir = FindFirstFile(src, &fdat);
1858         if (dir == INVALID_HANDLE_VALUE) {
1859             run_err("%s: No such file or directory", src);
1860             continue;
1861         }
1862         do {
1863             char *last;
1864             char *filename;
1865             /*
1866              * Ensure that . and .. are never matched by wildcards,
1867              * but only by deliberate action.
1868              */
1869             if (!strcmp(fdat.cFileName, ".") ||
1870                 !strcmp(fdat.cFileName, "..")) {
1871                 /*
1872                  * Find*File has returned a special dir. We require
1873                  * that _either_ `src' ends in a backslash followed
1874                  * by that string, _or_ `src' is precisely that
1875                  * string.
1876                  */
1877                 int len = strlen(src), dlen = strlen(fdat.cFileName);
1878                 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1879                     /* ok */ ;
1880                 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1881                            !strcmp(src + len - dlen, fdat.cFileName)) {
1882                     /* ok */ ;
1883                 } else
1884                     continue;          /* ignore this one */
1885             }
1886             filename = dupcat(srcpath, fdat.cFileName, NULL);
1887             source(filename);
1888             sfree(filename);
1889         } while (FindNextFile(dir, &fdat));
1890         FindClose(dir);
1891         sfree(srcpath);
1892     }
1893 }
1894
1895 /*
1896  *  We will copy files from a remote server to the local machine.
1897  */
1898 static void tolocal(int argc, char *argv[])
1899 {
1900     char *src, *targ, *host, *user;
1901     char *cmd;
1902
1903     if (argc != 2)
1904         bump("More than one remote source not supported");
1905
1906     src = argv[0];
1907     targ = argv[1];
1908
1909     /* Separate host from filename */
1910     host = src;
1911     src = colon(src);
1912     if (src == NULL)
1913         bump("Local to local copy not supported");
1914     *src++ = '\0';
1915     if (*src == '\0')
1916         src = ".";
1917     /* Substitute "." for empty filename */
1918
1919     /* Separate username and hostname */
1920     user = host;
1921     host = strrchr(host, '@');
1922     if (host == NULL) {
1923         host = user;
1924         user = NULL;
1925     } else {
1926         *host++ = '\0';
1927         if (*user == '\0')
1928             user = NULL;
1929     }
1930
1931     cmd = smalloc(strlen(src) + 100);
1932     sprintf(cmd, "scp%s%s%s%s -f %s",
1933             verbose ? " -v" : "",
1934             recursive ? " -r" : "",
1935             preserve ? " -p" : "",
1936             targetshouldbedirectory ? " -d" : "", src);
1937     do_cmd(host, user, cmd);
1938     sfree(cmd);
1939
1940     scp_sink_setup(src, preserve, recursive);
1941
1942     sink(targ, src);
1943 }
1944
1945 /*
1946  *  We will issue a list command to get a remote directory.
1947  */
1948 static void get_dir_list(int argc, char *argv[])
1949 {
1950     char *src, *host, *user;
1951     char *cmd, *p, *q;
1952     char c;
1953
1954     src = argv[0];
1955
1956     /* Separate host from filename */
1957     host = src;
1958     src = colon(src);
1959     if (src == NULL)
1960         bump("Local to local copy not supported");
1961     *src++ = '\0';
1962     if (*src == '\0')
1963         src = ".";
1964     /* Substitute "." for empty filename */
1965
1966     /* Separate username and hostname */
1967     user = host;
1968     host = strrchr(host, '@');
1969     if (host == NULL) {
1970         host = user;
1971         user = NULL;
1972     } else {
1973         *host++ = '\0';
1974         if (*user == '\0')
1975             user = NULL;
1976     }
1977
1978     cmd = smalloc(4 * strlen(src) + 100);
1979     strcpy(cmd, "ls -la '");
1980     p = cmd + strlen(cmd);
1981     for (q = src; *q; q++) {
1982         if (*q == '\'') {
1983             *p++ = '\'';
1984             *p++ = '\\';
1985             *p++ = '\'';
1986             *p++ = '\'';
1987         } else {
1988             *p++ = *q;
1989         }
1990     }
1991     *p++ = '\'';
1992     *p = '\0';
1993
1994     do_cmd(host, user, cmd);
1995     sfree(cmd);
1996
1997     if (using_sftp) {
1998         scp_sftp_listdir(src);
1999     } else {
2000         while (ssh_scp_recv(&c, 1) > 0)
2001             tell_char(stdout, c);
2002     }
2003 }
2004
2005 /*
2006  *  Initialize the Win$ock driver.
2007  */
2008 static void init_winsock(void)
2009 {
2010     WORD winsock_ver;
2011     WSADATA wsadata;
2012
2013     winsock_ver = MAKEWORD(1, 1);
2014     if (WSAStartup(winsock_ver, &wsadata))
2015         bump("Unable to initialise WinSock");
2016     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
2017         bump("WinSock version is incompatible with 1.1");
2018 }
2019
2020 /*
2021  *  Short description of parameters.
2022  */
2023 static void usage(void)
2024 {
2025     printf("PuTTY Secure Copy client\n");
2026     printf("%s\n", ver);
2027     printf("Usage: pscp [options] [user@]host:source target\n");
2028     printf
2029         ("       pscp [options] source [source...] [user@]host:target\n");
2030     printf("       pscp [options] -ls user@host:filespec\n");
2031     printf("Options:\n");
2032     printf("  -p        preserve file attributes\n");
2033     printf("  -q        quiet, don't show statistics\n");
2034     printf("  -r        copy directories recursively\n");
2035     printf("  -v        show verbose messages\n");
2036     printf("  -P port   connect to specified port\n");
2037     printf("  -pw passw login with specified password\n");
2038 #if 0
2039     /*
2040      * -gui is an internal option, used by GUI front ends to get
2041      * pscp to pass progress reports back to them. It's not an
2042      * ordinary user-accessible option, so it shouldn't be part of
2043      * the command-line help. The only people who need to know
2044      * about it are programmers, and they can read the source.
2045      */
2046     printf
2047         ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");
2048 #endif
2049     exit(1);
2050 }
2051
2052 /*
2053  *  Main program (no, really?)
2054  */
2055 int main(int argc, char *argv[])
2056 {
2057     int i;
2058
2059     default_protocol = PROT_TELNET;
2060
2061     flags = FLAG_STDERR;
2062     ssh_get_line = &get_line;
2063     init_winsock();
2064     sk_init();
2065
2066     for (i = 1; i < argc; i++) {
2067         if (argv[i][0] != '-')
2068             break;
2069         if (strcmp(argv[i], "-v") == 0)
2070             verbose = 1, flags |= FLAG_VERBOSE;
2071         else if (strcmp(argv[i], "-r") == 0)
2072             recursive = 1;
2073         else if (strcmp(argv[i], "-p") == 0)
2074             preserve = 1;
2075         else if (strcmp(argv[i], "-q") == 0)
2076             statistics = 0;
2077         else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
2078             usage();
2079         else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
2080             portnumber = atoi(argv[++i]);
2081         else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
2082             password = argv[++i];
2083         else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
2084             gui_hwnd = argv[++i];
2085             gui_mode = 1;
2086         } else if (strcmp(argv[i], "-ls") == 0)
2087             list = 1;
2088         else if (strcmp(argv[i], "--") == 0) {
2089             i++;
2090             break;
2091         } else
2092             usage();
2093     }
2094     argc -= i;
2095     argv += i;
2096     back = NULL;
2097
2098     if (list) {
2099         if (argc != 1)
2100             usage();
2101         get_dir_list(argc, argv);
2102
2103     } else {
2104
2105         if (argc < 2)
2106             usage();
2107         if (argc > 2)
2108             targetshouldbedirectory = 1;
2109
2110         if (colon(argv[argc - 1]) != NULL)
2111             toremote(argc, argv);
2112         else
2113             tolocal(argc, argv);
2114     }
2115
2116     if (back != NULL && back->socket() != NULL) {
2117         char ch;
2118         back->special(TS_EOF);
2119         ssh_scp_recv(&ch, 1);
2120     }
2121     WSACleanup();
2122     random_save_seed();
2123
2124     /* GUI Adaptation - August 2000 */
2125     if (gui_mode) {
2126         unsigned int msg_id = WM_RET_ERR_CNT;
2127         if (list)
2128             msg_id = WM_LS_RET_ERR_CNT;
2129         while (!PostMessage
2130                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
2131                 0 /*lParam */ ))SleepEx(1000, TRUE);
2132     }
2133     return (errs == 0 ? 0 : 1);
2134 }
2135
2136 /* end */