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