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