Add the `pwd' command in PSFTP.
[u/mdw/putty] / scp.c
CommitLineData
07d9aa13 1/*
2 * scp.c - Scp (Secure Copy) client for PuTTY.
fb09bf1c 3 * Joris van Rantwijk, Simon Tatham
07d9aa13 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.
cc87246d 7 *
8 * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000
07d9aa13 9 */
10
07d9aa13 11#include <windows.h>
4d331a77 12#ifndef AUTO_WINSOCK
13#ifdef WINSOCK_TWO
14#include <winsock2.h>
15#else
07d9aa13 16#include <winsock.h>
4d331a77 17#endif
18#endif
07d9aa13 19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
fd5e5847 22#include <limits.h>
07d9aa13 23#include <time.h>
feb7fdfe 24#include <assert.h>
07d9aa13 25
26#define PUTTY_DO_GLOBALS
27#include "putty.h"
fd5e5847 28#include "ssh.h"
29#include "sftp.h"
8c3cd914 30#include "winstuff.h"
a9422f39 31#include "storage.h"
07d9aa13 32
07d9aa13 33#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
c51a56e2 34 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
07d9aa13 35#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
c51a56e2 36 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
07d9aa13 37
cc87246d 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
2bc6a386 49static int list = 0;
fb09bf1c 50static int verbose = 0;
07d9aa13 51static int recursive = 0;
52static int preserve = 0;
53static int targetshouldbedirectory = 0;
54static int statistics = 1;
b8a19193 55static int portnumber = 0;
b1daf518 56static int prev_stats_len = 0;
cd1f39ab 57static int scp_unsafe_mode = 0;
b8a19193 58static char *password = NULL;
07d9aa13 59static int errs = 0;
cc87246d 60/* GUI Adaptation - Sept 2000 */
61#define NAME_STR_MAX 2048
32874aea 62static char statname[NAME_STR_MAX + 1];
cc87246d 63static unsigned long statsize = 0;
64static int statperct = 0;
90a14a09 65static unsigned long statelapsed = 0;
cc87246d 66static int gui_mode = 0;
67static char *gui_hwnd = NULL;
fd5e5847 68static int using_sftp = 0;
07d9aa13 69
70static void source(char *src);
71static void rsource(char *src);
ca2d5943 72static void sink(char *targ, char *src);
cc87246d 73/* GUI Adaptation - Sept 2000 */
32874aea 74static void tell_char(FILE * stream, char c);
75static void tell_str(FILE * stream, char *str);
76static void tell_user(FILE * stream, char *fmt, ...);
90a14a09 77static void gui_update_stats(char *name, unsigned long size,
32874aea 78 int percentage, unsigned long elapsed);
07d9aa13 79
5471d09a 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
32874aea 86void logevent(char *string)
87{
88}
a9422f39 89
32874aea 90void ldisc_send(char *buf, int len)
91{
feb7fdfe 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
a9422f39 101void verify_ssh_host_key(char *host, int port, char *keytype,
32874aea 102 char *keystr, char *fingerprint)
103{
a9422f39 104 int ret;
d0718310 105 HANDLE hin;
106 DWORD savemode, i;
a9422f39 107
108 static const char absentmsg[] =
32874aea 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"
d0718310 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) ";
a9422f39 121
122 static const char wrongmsg[] =
32874aea 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"
d0718310 132 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
32874aea 133 "If you want to carry on connecting but without updating\n"
d0718310 134 "the cache, enter \"n\".\n"
32874aea 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) ";
a9422f39 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
32874aea 149 if (ret == 0) /* success - key matched OK */
150 return;
d0718310 151
32874aea 152 if (ret == 2) { /* key was different */
153 fprintf(stderr, wrongmsg, fingerprint);
b4453f49 154 fflush(stderr);
a9422f39 155 }
32874aea 156 if (ret == 1) { /* key was absent */
157 fprintf(stderr, absentmsg, fingerprint);
d0718310 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')
32874aea 170 store_host_key(host, port, keytype, keystr);
d0718310 171 } else {
172 fprintf(stderr, abandoned);
173 exit(0);
a9422f39 174 }
175}
fb09bf1c 176
ca20bfcf 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
cc87246d 217/* GUI Adaptation - Sept 2000 */
75cab814 218static void send_msg(HWND h, UINT message, WPARAM wParam)
cc87246d 219{
32874aea 220 while (!PostMessage(h, message, wParam, 0))
221 SleepEx(1000, TRUE);
cc87246d 222}
223
32874aea 224static void tell_char(FILE * stream, char c)
cc87246d 225{
226 if (!gui_mode)
227 fputc(c, stream);
32874aea 228 else {
cc87246d 229 unsigned int msg_id = WM_STD_OUT_CHAR;
32874aea 230 if (stream == stderr)
231 msg_id = WM_STD_ERR_CHAR;
232 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
cc87246d 233 }
234}
235
32874aea 236static void tell_str(FILE * stream, char *str)
cc87246d 237{
238 unsigned int i;
239
32874aea 240 for (i = 0; i < strlen(str); ++i)
cc87246d 241 tell_char(stream, str[i]);
242}
243
32874aea 244static void tell_user(FILE * stream, char *fmt, ...)
cc87246d 245{
32874aea 246 char str[0x100]; /* Make the size big enough */
cc87246d 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
32874aea 255static void gui_update_stats(char *name, unsigned long size,
256 int percentage, unsigned long elapsed)
cc87246d 257{
258 unsigned int i;
259
32874aea 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);
cc87246d 266 }
32874aea 267 if (statsize != size) {
268 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
cc87246d 269 statsize = size;
270 }
32874aea 271 if (statelapsed != elapsed) {
272 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
273 (WPARAM) elapsed);
cc87246d 274 statelapsed = elapsed;
275 }
32874aea 276 if (statperct != percentage) {
277 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
278 (WPARAM) percentage);
cc87246d 279 statperct = percentage;
280 }
281}
282
fb09bf1c 283/*
07d9aa13 284 * Print an error message and perform a fatal exit.
285 */
286void fatalbox(char *fmt, ...)
287{
32874aea 288 char str[0x100]; /* Make the size big enough */
c51a56e2 289 va_list ap;
290 va_start(ap, fmt);
120e4b40 291 strcpy(str, "Fatal: ");
32874aea 292 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 293 va_end(ap);
cc87246d 294 strcat(str, "\n");
295 tell_str(stderr, str);
2bc6a386 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 }
cc87246d 306
c51a56e2 307 exit(1);
07d9aa13 308}
8d5de777 309void connection_fatal(char *fmt, ...)
310{
32874aea 311 char str[0x100]; /* Make the size big enough */
8d5de777 312 va_list ap;
313 va_start(ap, fmt);
120e4b40 314 strcpy(str, "Fatal: ");
32874aea 315 vsprintf(str + strlen(str), fmt, ap);
8d5de777 316 va_end(ap);
317 strcat(str, "\n");
318 tell_str(stderr, str);
2bc6a386 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 }
8d5de777 329
330 exit(1);
331}
07d9aa13 332
07d9aa13 333/*
8df7a775 334 * Be told what socket we're supposed to be using.
335 */
336static SOCKET scp_ssh_socket;
32874aea 337char *do_select(SOCKET skt, int startup)
338{
8df7a775 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/*
3bdaf79d 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
fe50e814 352 * own trap in from_backend() to catch the data that comes back. We
353 * do this until we have enough data.
3bdaf79d 354 */
8df7a775 355
32874aea 356static unsigned char *outptr; /* where to put the data */
357static unsigned outlen; /* how much data required */
3bdaf79d 358static unsigned char *pending = NULL; /* any spare data */
32874aea 359static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
5471d09a 360int from_backend(int is_stderr, char *data, int datalen)
32874aea 361{
362 unsigned char *p = (unsigned char *) data;
363 unsigned len = (unsigned) datalen;
fe50e814 364
3bdaf79d 365 /*
fe50e814 366 * stderr data is just spouted to local stderr and otherwise
367 * ignored.
3bdaf79d 368 */
fe50e814 369 if (is_stderr) {
370 fwrite(data, 1, len, stderr);
5471d09a 371 return 0;
fe50e814 372 }
3bdaf79d 373
374 inbuf_head = 0;
375
376 /*
377 * If this is before the real session begins, just return.
378 */
379 if (!outptr)
5471d09a 380 return 0;
3bdaf79d 381
382 if (outlen > 0) {
32874aea 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;
3bdaf79d 391 }
392
393 if (len > 0) {
32874aea 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;
3bdaf79d 403 }
5471d09a 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;
3bdaf79d 417}
32874aea 418static int ssh_scp_recv(unsigned char *buf, int len)
419{
3bdaf79d 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) {
32874aea 428 unsigned pendused = pendlen;
429 if (pendused > outlen)
430 pendused = outlen;
3bdaf79d 431 memcpy(outptr, pending, pendused);
32874aea 432 memmove(pending, pending + pendused, pendlen - pendused);
3bdaf79d 433 outptr += pendused;
434 outlen -= pendused;
32874aea 435 pendlen -= pendused;
436 if (pendlen == 0) {
437 pendsize = 0;
438 sfree(pending);
439 pending = NULL;
440 }
441 if (outlen == 0)
442 return len;
3bdaf79d 443 }
444
445 while (outlen > 0) {
5471d09a 446 if (!scp_process_network_event())
32874aea 447 return 0; /* doom */
3bdaf79d 448 }
449
450 return len;
451}
452
453/*
454 * Loop through the ssh connection and authentication process.
455 */
32874aea 456static void ssh_scp_init(void)
457{
8df7a775 458 if (scp_ssh_socket == INVALID_SOCKET)
3bdaf79d 459 return;
460 while (!back->sendok()) {
32874aea 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);
3bdaf79d 467 }
fd5e5847 468 using_sftp = !ssh_fallback_cmd;
3bdaf79d 469}
470
471/*
07d9aa13 472 * Print an error message and exit after closing the SSH link.
473 */
474static void bump(char *fmt, ...)
475{
32874aea 476 char str[0x100]; /* Make the size big enough */
c51a56e2 477 va_list ap;
478 va_start(ap, fmt);
120e4b40 479 strcpy(str, "Fatal: ");
32874aea 480 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 481 va_end(ap);
cc87246d 482 strcat(str, "\n");
483 tell_str(stderr, str);
2bc6a386 484 errs++;
cc87246d 485
eba78553 486 if (back != NULL && back->socket() != NULL) {
c51a56e2 487 char ch;
3bdaf79d 488 back->special(TS_EOF);
fb09bf1c 489 ssh_scp_recv(&ch, 1);
c51a56e2 490 }
2bc6a386 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
c51a56e2 501 exit(1);
07d9aa13 502}
503
fa17a66e 504static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
07d9aa13 505{
c51a56e2 506 HANDLE hin, hout;
fa17a66e 507 DWORD savemode, newmode, i;
b8a19193 508
fa17a66e 509 if (is_pw && password) {
32874aea 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 }
b8a19193 520 }
07d9aa13 521
cc87246d 522 /* GUI Adaptation - Sept 2000 */
523 if (gui_mode) {
32874aea 524 if (maxlen > 0)
525 str[0] = '\0';
cc87246d 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");
07d9aa13 531
cc87246d 532 GetConsoleMode(hin, &savemode);
32874aea 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);
07d9aa13 539
cc87246d 540 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
32874aea 541 ReadFile(hin, str, maxlen - 1, &i, NULL);
07d9aa13 542
cc87246d 543 SetConsoleMode(hin, savemode);
07d9aa13 544
32874aea 545 if ((int) i > maxlen)
546 i = maxlen - 1;
547 else
548 i = i - 2;
cc87246d 549 str[i] = '\0';
07d9aa13 550
fa17a66e 551 if (is_pw)
32874aea 552 WriteFile(hout, "\r\n", 2, &i, NULL);
cc87246d 553 }
85ee8208 554
555 return 1;
07d9aa13 556}
557
07d9aa13 558/*
559 * Open an SSH connection to user@host and execute cmd.
560 */
561static void do_cmd(char *host, char *user, char *cmd)
562{
c51a56e2 563 char *err, *realhost;
f5e6a5c6 564 DWORD namelen;
c51a56e2 565
566 if (host == NULL || host[0] == '\0')
567 bump("Empty host name");
568
569 /* Try to load settings for this host */
a9422f39 570 do_defaults(host, &cfg);
c51a56e2 571 if (cfg.host[0] == '\0') {
572 /* No settings for this host; use defaults */
32874aea 573 do_defaults(NULL, &cfg);
574 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
575 cfg.host[sizeof(cfg.host) - 1] = '\0';
c51a56e2 576 cfg.port = 22;
577 }
578
579 /* Set username */
580 if (user != NULL && user[0] != '\0') {
32874aea 581 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
582 cfg.username[sizeof(cfg.username) - 1] = '\0';
c51a56e2 583 } else if (cfg.username[0] == '\0') {
f5e6a5c6 584 namelen = 0;
585 if (GetUserName(user, &namelen) == FALSE)
586 bump("Empty user name");
93558b21 587 user = smalloc(namelen * sizeof(char));
f5e6a5c6 588 GetUserName(user, &namelen);
32874aea 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';
f5e6a5c6 593 free(user);
c51a56e2 594 }
595
596 if (cfg.protocol != PROT_SSH)
597 cfg.port = 22;
598
ed89e8a5 599 if (portnumber)
600 cfg.port = portnumber;
601
fd5e5847 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;
3bdaf79d 610 cfg.nopty = TRUE;
611
612 back = &ssh_backend;
613
8df7a775 614 err = back->init(cfg.host, cfg.port, &realhost);
c51a56e2 615 if (err != NULL)
616 bump("ssh_init: %s", err);
3bdaf79d 617 ssh_scp_init();
c51a56e2 618 if (verbose && realhost != NULL)
cc87246d 619 tell_user(stderr, "Connected to %s\n", realhost);
6e1ebb76 620 sfree(realhost);
07d9aa13 621}
622
07d9aa13 623/*
624 * Update statistic information about current file.
625 */
626static void print_stats(char *name, unsigned long size, unsigned long done,
32874aea 627 time_t start, time_t now)
07d9aa13 628{
c51a56e2 629 float ratebs;
630 unsigned long eta;
631 char etastr[10];
632 int pct;
b1daf518 633 int len;
c51a56e2 634
cc87246d 635 /* GUI Adaptation - Sept 2000 */
636 if (gui_mode)
32874aea 637 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
638 (unsigned long) difftime(now, start));
cc87246d 639 else {
640 if (now > start)
641 ratebs = (float) done / (now - start);
642 else
643 ratebs = (float) done;
c51a56e2 644
cc87246d 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);
c51a56e2 651
5578d18e 652 pct = (int) (100 * (done * 1.0 / size));
c51a56e2 653
b1daf518 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;
c51a56e2 659
cc87246d 660 if (done == size)
661 printf("\n");
662 }
07d9aa13 663}
664
07d9aa13 665/*
666 * Find a colon in str and return a pointer to the colon.
39ddf0ff 667 * This is used to separate hostname from filename.
07d9aa13 668 */
32874aea 669static char *colon(char *str)
07d9aa13 670{
c51a56e2 671 /* We ignore a leading colon, since the hostname cannot be
32874aea 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] == ':')
c51a56e2 675 return (NULL);
32874aea 676 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
c51a56e2 677 str++;
678 if (*str == ':')
679 return (str);
680 else
681 return (NULL);
07d9aa13 682}
683
07d9aa13 684/*
03f64569 685 * Return a pointer to the portion of str that comes after the last
b3dcd9b2 686 * slash (or backslash or colon, if `local' is TRUE).
03f64569 687 */
4eb24e3a 688static char *stripslashes(char *str, int local)
03f64569 689{
690 char *p;
691
b3dcd9b2 692 if (local) {
693 p = strchr(str, ':');
694 if (p) str = p+1;
695 }
696
03f64569 697 p = strrchr(str, '/');
698 if (p) str = p+1;
699
4eb24e3a 700 if (local) {
701 p = strrchr(str, '\\');
702 if (p) str = p+1;
703 }
03f64569 704
705 return str;
706}
707
708/*
fd5e5847 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/*
07d9aa13 717 * Wait for a response from the other side.
718 * Return 0 if ok, -1 if error.
719 */
720static int response(void)
721{
c51a56e2 722 char ch, resp, rbuf[2048];
723 int p;
724
fb09bf1c 725 if (ssh_scp_recv(&resp, 1) <= 0)
c51a56e2 726 bump("Lost connection");
727
728 p = 0;
729 switch (resp) {
32874aea 730 case 0: /* ok */
c51a56e2 731 return (0);
732 default:
733 rbuf[p++] = resp;
734 /* fallthrough */
32874aea 735 case 1: /* error */
736 case 2: /* fatal error */
c51a56e2 737 do {
fb09bf1c 738 if (ssh_scp_recv(&ch, 1) <= 0)
c51a56e2 739 bump("Protocol error: Lost connection");
740 rbuf[p++] = ch;
741 } while (p < sizeof(rbuf) && ch != '\n');
32874aea 742 rbuf[p - 1] = '\0';
c51a56e2 743 if (resp == 1)
cc87246d 744 tell_user(stderr, "%s\n", rbuf);
c51a56e2 745 else
746 bump("%s", rbuf);
747 errs++;
748 return (-1);
749 }
07d9aa13 750}
751
fd5e5847 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;
fd5e5847 777 int i;
778
779 printf("Listing directory %s\n", dirname);
780
781 dirh = fxp_opendir(dirname);
782 if (dirh == NULL) {
cdcbdf3b 783 printf("Unable to open %s: %s\n", dirname, fxp_error());
fd5e5847 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;
cdcbdf3b 794 printf("Reading directory %s: %s\n", dirname, fxp_error());
fd5e5847 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
120e4b40 830/* ----------------------------------------------------------------------
831 * Helper routines that contain the actual SCP protocol elements,
fd5e5847 832 * implemented both as SCP1 and SFTP.
120e4b40 833 */
834
fd5e5847 835static struct scp_sftp_dirstack {
836 struct scp_sftp_dirstack *next;
837 struct fxp_name *names;
838 int namepos, namelen;
839 char *dirpath;
4eb24e3a 840 char *wildcard;
825ec8ee 841 int matched_something; /* wildcard match set was non-empty */
fd5e5847 842} *scp_sftp_dirstack_head;
843static char *scp_sftp_remotepath, *scp_sftp_currentname;
4eb24e3a 844static char *scp_sftp_wildcard;
fd5e5847 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
120e4b40 879int scp_send_errmsg(char *str)
880{
fd5e5847 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 }
120e4b40 887 return 0; /* can't fail */
888}
889
890int scp_send_filetimes(unsigned long mtime, unsigned long atime)
891{
fd5e5847 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 }
120e4b40 903}
904
905int scp_send_filename(char *name, unsigned long size, int modes)
906{
fd5e5847 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 }
120e4b40 933}
934
935int scp_send_filedata(char *data, int len)
936{
fd5e5847 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);
120e4b40 950
fd5e5847 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) {
120e4b40 972 return 1;
fd5e5847 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();
120e4b40 989 }
fd5e5847 990}
120e4b40 991
fd5e5847 992char *scp_save_remotepath(void)
993{
994 if (using_sftp)
995 return scp_sftp_remotepath;
996 else
997 return NULL;
120e4b40 998}
999
fd5e5847 1000void scp_restore_remotepath(char *data)
120e4b40 1001{
fd5e5847 1002 if (using_sftp)
1003 scp_sftp_remotepath = data;
120e4b40 1004}
1005
1006int scp_send_dirname(char *name, int modes)
1007{
fd5e5847 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 }
120e4b40 1049}
1050
1051int scp_send_enddir(void)
1052{
fd5e5847 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 */
4eb24e3a 1068int scp_sink_setup(char *source, int preserve, int recursive)
fd5e5847 1069{
1070 if (using_sftp) {
4eb24e3a 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';
6b18a524 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(".");
4eb24e3a 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 }
fd5e5847 1135 scp_sftp_preserve = preserve;
1136 scp_sftp_recursive = recursive;
1137 scp_sftp_donethistarget = 0;
1138 scp_sftp_dirstack_head = NULL;
1139 }
4eb24e3a 1140 return 0;
120e4b40 1141}
1142
1143int scp_sink_init(void)
1144{
fd5e5847 1145 if (!using_sftp) {
1146 back->send("", 1);
1147 }
120e4b40 1148 return 0;
1149}
1150
1151#define SCP_SINK_FILE 1
1152#define SCP_SINK_DIR 2
1153#define SCP_SINK_ENDDIR 3
4eb24e3a 1154#define SCP_SINK_RETRY 4 /* not an action; just try again */
120e4b40 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{
fd5e5847 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 &&
4eb24e3a 1196 (is_dots(head->names[head->namepos].filename) ||
1197 (head->wildcard &&
1198 !wc_match(head->wildcard,
1199 head->names[head->namepos].filename))))
fd5e5847 1200 head->namepos++; /* skip . and .. */
1201 if (head->namepos < head->namelen) {
825ec8ee 1202 head->matched_something = 1;
fd5e5847 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
4eb24e3a 1210 * the stack and return an ENDDIR action (or RETRY
1211 * if this was a wildcard match).
fd5e5847 1212 */
4eb24e3a 1213 if (head->wildcard) {
1214 act->action = SCP_SINK_RETRY;
825ec8ee 1215 if (!head->matched_something) {
1216 tell_user(stderr, "pscp: wildcard '%s' matched "
1217 "no files", head->wildcard);
1218 errs++;
1219 }
4eb24e3a 1220 sfree(head->wildcard);
825ec8ee 1221
4eb24e3a 1222 } else {
1223 act->action = SCP_SINK_ENDDIR;
1224 }
1225
fd5e5847 1226 sfree(head->dirpath);
1227 sfree(head->names);
1228 scp_sftp_dirstack_head = head->next;
1229 sfree(head);
1230
fd5e5847 1231 return 0;
1232 }
1233 }
cd1f39ab 1234
fd5e5847 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++;
120e4b40 1244 return 1;
fd5e5847 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 /*
37dfb97a 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.
fd5e5847 1265 */
4eb24e3a 1266 if (!scp_sftp_recursive && !scp_sftp_wildcard) {
fd5e5847 1267 tell_user(stderr, "pscp: %s: is a directory", fname);
1268 errs++;
1269 if (must_free_fname) sfree(fname);
37dfb97a 1270 if (scp_sftp_dirstack_head) {
1271 act->action = SCP_SINK_RETRY;
1272 return 0;
1273 } else {
1274 return 1;
1275 }
120e4b40 1276 }
fd5e5847 1277
1278 /*
1279 * Otherwise, the fun begins. We must fxp_opendir() the
1280 * directory, slurp the filenames into memory, return
4eb24e3a 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.
fd5e5847 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);
4eb24e3a 1340 if (scp_sftp_wildcard) {
1341 newitem->wildcard = scp_sftp_wildcard;
825ec8ee 1342 newitem->matched_something = 0;
4eb24e3a 1343 scp_sftp_wildcard = NULL;
1344 } else {
1345 newitem->wildcard = NULL;
1346 }
fd5e5847 1347 scp_sftp_dirstack_head = newitem;
1348
4eb24e3a 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 }
120e4b40 1365 return 0;
fd5e5847 1366
1367 } else {
1368 /*
1369 * It's a file. Return SCP_SINK_FILE.
1370 */
1371 act->action = SCP_SINK_FILE;
4eb24e3a 1372 act->buf = dupstr(stripslashes(fname, 0));
fd5e5847 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;
120e4b40 1387 act->settime = 1;
fd5e5847 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':
120e4b40 1432 back->send("", 1);
fd5e5847 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");
120e4b40 1449 }
fd5e5847 1450 /*
1451 * We will go round this loop only once, unless we hit
1452 * `continue' above.
1453 */
1454 done = 1;
120e4b40 1455 }
fd5e5847 1456
120e4b40 1457 /*
fd5e5847 1458 * If we get here, we must have seen SCP_SINK_FILE or
1459 * SCP_SINK_DIR.
120e4b40 1460 */
fd5e5847 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;
120e4b40 1465 }
120e4b40 1466}
1467
1468int scp_accept_filexfer(void)
1469{
fd5e5847 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 }
120e4b40 1486}
1487
1488int scp_recv_filedata(char *data, int len)
1489{
fd5e5847 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 }
120e4b40 1507}
1508
1509int scp_finish_filerecv(void)
1510{
fd5e5847 1511 if (using_sftp) {
1512 fxp_close(scp_sftp_filehandle);
1513 return 0;
1514 } else {
1515 back->send("", 1);
1516 return response();
1517 }
120e4b40 1518}
1519
1520/* ----------------------------------------------------------------------
07d9aa13 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{
c51a56e2 1526 char str[2048];
1527 va_list ap;
1528 va_start(ap, fmt);
1529 errs++;
9520eba8 1530 strcpy(str, "scp: ");
32874aea 1531 vsprintf(str + strlen(str), fmt, ap);
c51a56e2 1532 strcat(str, "\n");
120e4b40 1533 scp_send_errmsg(str);
32874aea 1534 tell_user(stderr, "%s", str);
c51a56e2 1535 va_end(ap);
07d9aa13 1536}
1537
07d9aa13 1538/*
1539 * Execute the source part of the SCP protocol.
1540 */
1541static void source(char *src)
1542{
c51a56e2 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);
32874aea 1552 if (attr == (DWORD) - 1) {
c51a56e2 1553 run_err("%s: No such file or directory", src);
1554 return;
1555 }
1556
1557 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
7f1f80de 1558 if (recursive) {
32874aea 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 {
c51a56e2 1575 run_err("%s: not a regular file", src);
32874aea 1576 }
c51a56e2 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) {
486543a1 1592 run_err("%s: Cannot open file", src);
c51a56e2 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);
120e4b40 1602 if (scp_send_filetimes(mtime, atime))
c51a56e2 1603 return;
1604 }
1605
1606 size = GetFileSize(f, NULL);
c51a56e2 1607 if (verbose)
120e4b40 1608 tell_user(stderr, "Sending file %s, size=%lu", last, size);
1609 if (scp_send_filename(last, size, 0644))
c51a56e2 1610 return;
1611
2d466ffd 1612 stat_bytes = 0;
1613 stat_starttime = time(NULL);
1614 stat_lasttime = 0;
c51a56e2 1615
1616 for (i = 0; i < size; i += 4096) {
1617 char transbuf[4096];
1618 DWORD j, k = 4096;
5471d09a 1619
32874aea 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");
c51a56e2 1625 bump("%s: Read error", src);
07d9aa13 1626 }
120e4b40 1627 if (scp_send_filedata(transbuf, k))
1628 bump("%s: Network error occurred", src);
1629
c51a56e2 1630 if (statistics) {
1631 stat_bytes += k;
32874aea 1632 if (time(NULL) != stat_lasttime || i + k == size) {
c51a56e2 1633 stat_lasttime = time(NULL);
1634 print_stats(last, size, stat_bytes,
1635 stat_starttime, stat_lasttime);
1636 }
07d9aa13 1637 }
5471d09a 1638
c51a56e2 1639 }
1640 CloseHandle(f);
07d9aa13 1641
120e4b40 1642 (void) scp_send_finish();
07d9aa13 1643}
1644
07d9aa13 1645/*
1646 * Recursively send the contents of a directory.
1647 */
1648static void rsource(char *src)
1649{
03f64569 1650 char *last, *findfile;
fd5e5847 1651 char *save_target;
c51a56e2 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
fd5e5847 1667 save_target = scp_save_remotepath();
1668
c51a56e2 1669 if (verbose)
120e4b40 1670 tell_user(stderr, "Entering directory: %s", last);
1671 if (scp_send_dirname(last, 0755))
c51a56e2 1672 return;
1673
03f64569 1674 findfile = dupcat(src, "/*", NULL);
1675 dir = FindFirstFile(findfile, &fdat);
c51a56e2 1676 ok = (dir != INVALID_HANDLE_VALUE);
1677 while (ok) {
1678 if (strcmp(fdat.cFileName, ".") == 0 ||
1679 strcmp(fdat.cFileName, "..") == 0) {
03f64569 1680 /* ignore . and .. */
c51a56e2 1681 } else {
fd5e5847 1682 char *foundfile = dupcat(src, "/", fdat.cFileName, NULL);
03f64569 1683 source(foundfile);
1684 sfree(foundfile);
07d9aa13 1685 }
c51a56e2 1686 ok = FindNextFile(dir, &fdat);
1687 }
1688 FindClose(dir);
03f64569 1689 sfree(findfile);
07d9aa13 1690
120e4b40 1691 (void) scp_send_enddir();
fd5e5847 1692
1693 scp_restore_remotepath(save_target);
07d9aa13 1694}
1695
07d9aa13 1696/*
03f64569 1697 * Execute the sink part of the SCP protocol.
07d9aa13 1698 */
ca2d5943 1699static void sink(char *targ, char *src)
07d9aa13 1700{
03f64569 1701 char *destfname;
c51a56e2 1702 int targisdir = 0;
c51a56e2 1703 int exists;
1704 DWORD attr;
1705 HANDLE f;
120e4b40 1706 unsigned long received;
c51a56e2 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);
32874aea 1713 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
c51a56e2 1714 targisdir = 1;
1715
1716 if (targetshouldbedirectory && !targisdir)
1717 bump("%s: Not a directory", targ);
1718
120e4b40 1719 scp_sink_init();
c51a56e2 1720 while (1) {
120e4b40 1721 struct scp_sink_action act;
1722 if (scp_get_sink_action(&act))
c51a56e2 1723 return;
07d9aa13 1724
120e4b40 1725 if (act.action == SCP_SINK_ENDDIR)
1726 return;
03f64569 1727
4eb24e3a 1728 if (act.action == SCP_SINK_RETRY)
1729 continue;
1730
c51a56e2 1731 if (targisdir) {
03f64569 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
b3dcd9b2 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!)
03f64569 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.
4eeae4a3 1748 *
cd1f39ab 1749 * Well, not always; if `src' is a wildcard, we do
4eeae4a3 1750 * expect to get back filenames that don't correspond
cd1f39ab 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).
03f64569 1761 */
1762 char *striptarget, *stripsrc;
1763
4eb24e3a 1764 striptarget = stripslashes(act.name, 1);
03f64569 1765 if (striptarget != act.name) {
1766 tell_user(stderr, "warning: remote host sent a compound"
b3dcd9b2 1767 " pathname '%s'", act.name);
1768 tell_user(stderr, " renaming local file to '%s'",
1769 striptarget);
03f64569 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 */
fd5e5847 1777 if (is_dots(striptarget)) {
03f64569 1778 bump("security violation: remote host attempted to write to"
1779 " a '.' or '..' path!");
1780 }
1781
1782 if (src) {
4eb24e3a 1783 stripsrc = stripslashes(src, 1);
cd1f39ab 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.");
4eeae4a3 1794 /* Override the name the server provided with our own. */
1795 striptarget = stripsrc;
03f64569 1796 }
03f64569 1797 }
1798
c51a56e2 1799 if (targ[0] != '\0')
03f64569 1800 destfname = dupcat(targ, "\\", striptarget, NULL);
1801 else
1802 destfname = dupstr(striptarget);
c51a56e2 1803 } else {
03f64569 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);
c51a56e2 1810 }
03f64569 1811 attr = GetFileAttributes(destfname);
32874aea 1812 exists = (attr != (DWORD) - 1);
c51a56e2 1813
120e4b40 1814 if (act.action == SCP_SINK_DIR) {
c51a56e2 1815 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
03f64569 1816 run_err("%s: Not a directory", destfname);
c51a56e2 1817 continue;
1818 }
1819 if (!exists) {
03f64569 1820 if (!CreateDirectory(destfname, NULL)) {
1821 run_err("%s: Cannot create directory", destfname);
c51a56e2 1822 continue;
1823 }
1824 }
03f64569 1825 sink(destfname, NULL);
c51a56e2 1826 /* can we set the timestamp for directories ? */
1827 continue;
1828 }
07d9aa13 1829
03f64569 1830 f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
c51a56e2 1831 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
1832 if (f == INVALID_HANDLE_VALUE) {
03f64569 1833 run_err("%s: Cannot create file", destfname);
c51a56e2 1834 continue;
1835 }
07d9aa13 1836
120e4b40 1837 if (scp_accept_filexfer())
1838 return;
07d9aa13 1839
2d466ffd 1840 stat_bytes = 0;
1841 stat_starttime = time(NULL);
1842 stat_lasttime = 0;
4eb24e3a 1843 stat_name = stripslashes(destfname, 1);
07d9aa13 1844
120e4b40 1845 received = 0;
1846 while (received < act.size) {
c51a56e2 1847 char transbuf[4096];
120e4b40 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)
c51a56e2 1854 bump("Lost connection");
32874aea 1855 if (wrerror)
1856 continue;
120e4b40 1857 if (!WriteFile(f, transbuf, read, &written, NULL) ||
1858 written != read) {
c51a56e2 1859 wrerror = 1;
120e4b40 1860 /* FIXME: in sftp we can actually abort the transfer */
c51a56e2 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) {
120e4b40 1868 stat_bytes += read;
1869 if (time(NULL) > stat_lasttime ||
1870 received + read == act.size) {
c51a56e2 1871 stat_lasttime = time(NULL);
120e4b40 1872 print_stats(stat_name, act.size, stat_bytes,
c51a56e2 1873 stat_starttime, stat_lasttime);
07d9aa13 1874 }
c51a56e2 1875 }
120e4b40 1876 received += read;
c51a56e2 1877 }
120e4b40 1878 if (act.settime) {
c51a56e2 1879 FILETIME actime, wrtime;
120e4b40 1880 TIME_POSIX_TO_WIN(act.atime, actime);
1881 TIME_POSIX_TO_WIN(act.mtime, wrtime);
c51a56e2 1882 SetFileTime(f, NULL, &actime, &wrtime);
07d9aa13 1883 }
07d9aa13 1884
c51a56e2 1885 CloseHandle(f);
1886 if (wrerror) {
03f64569 1887 run_err("%s: Write error", destfname);
c51a56e2 1888 continue;
1889 }
120e4b40 1890 (void) scp_finish_filerecv();
03f64569 1891 sfree(destfname);
d4aa8594 1892 sfree(act.buf);
c51a56e2 1893 }
1894}
07d9aa13 1895
1896/*
120e4b40 1897 * We will copy local files to a remote server.
07d9aa13 1898 */
1899static void toremote(int argc, char *argv[])
1900{
c51a56e2 1901 char *src, *targ, *host, *user;
1902 char *cmd;
1903 int i;
1904
32874aea 1905 targ = argv[argc - 1];
c51a56e2 1906
39ddf0ff 1907 /* Separate host from filename */
c51a56e2 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
39ddf0ff 1917 /* Separate host and username */
c51a56e2 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
32874aea 1931 if so, we should set the targetshouldbedirectory flag */
c51a56e2 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" : "",
32874aea 1949 targetshouldbedirectory ? " -d" : "", targ);
c51a56e2 1950 do_cmd(host, user, cmd);
1951 sfree(cmd);
1952
fd5e5847 1953 scp_source_setup(targ, targetshouldbedirectory);
c51a56e2 1954
1955 for (i = 0; i < argc - 1; i++) {
03f64569 1956 char *srcpath, *last;
c51a56e2 1957 HANDLE dir;
1958 WIN32_FIND_DATA fdat;
1959 src = argv[i];
1960 if (colon(src) != NULL) {
cc87246d 1961 tell_user(stderr, "%s: Remote to remote not supported\n", src);
c51a56e2 1962 errs++;
1963 continue;
07d9aa13 1964 }
03f64569 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);
4eb24e3a 1972 last = stripslashes(srcpath, 1);
03f64569 1973 *last = '\0';
03f64569 1974
c51a56e2 1975 dir = FindFirstFile(src, &fdat);
1976 if (dir == INVALID_HANDLE_VALUE) {
1977 run_err("%s: No such file or directory", src);
1978 continue;
07d9aa13 1979 }
c51a56e2 1980 do {
03f64569 1981 char *filename;
7f266ffb 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)) {
32874aea 1996 /* ok */ ;
1997 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1998 !strcmp(src + len - dlen, fdat.cFileName)) {
1999 /* ok */ ;
7f266ffb 2000 } else
2001 continue; /* ignore this one */
2002 }
03f64569 2003 filename = dupcat(srcpath, fdat.cFileName, NULL);
2004 source(filename);
2005 sfree(filename);
c51a56e2 2006 } while (FindNextFile(dir, &fdat));
2007 FindClose(dir);
03f64569 2008 sfree(srcpath);
c51a56e2 2009 }
07d9aa13 2010}
2011
07d9aa13 2012/*
2013 * We will copy files from a remote server to the local machine.
2014 */
2015static void tolocal(int argc, char *argv[])
2016{
c51a56e2 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
39ddf0ff 2026 /* Separate host from filename */
c51a56e2 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
39ddf0ff 2036 /* Separate username and hostname */
c51a56e2 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" : "",
32874aea 2053 targetshouldbedirectory ? " -d" : "", src);
c51a56e2 2054 do_cmd(host, user, cmd);
2055 sfree(cmd);
2056
4eb24e3a 2057 if (scp_sink_setup(src, preserve, recursive))
2058 return;
fd5e5847 2059
ca2d5943 2060 sink(targ, src);
07d9aa13 2061}
2062
07d9aa13 2063/*
39ddf0ff 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
32874aea 2096 cmd = smalloc(4 * strlen(src) + 100);
39ddf0ff 2097 strcpy(cmd, "ls -la '");
2098 p = cmd + strlen(cmd);
2099 for (q = src; *q; q++) {
2100 if (*q == '\'') {
32874aea 2101 *p++ = '\'';
2102 *p++ = '\\';
2103 *p++ = '\'';
2104 *p++ = '\'';
39ddf0ff 2105 } else {
2106 *p++ = *q;
2107 }
2108 }
2109 *p++ = '\'';
2110 *p = '\0';
cc87246d 2111
39ddf0ff 2112 do_cmd(host, user, cmd);
2113 sfree(cmd);
2114
fd5e5847 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 }
39ddf0ff 2121}
2122
2123/*
07d9aa13 2124 * Initialize the Win$ock driver.
2125 */
996c8c3b 2126static void init_winsock(void)
07d9aa13 2127{
c51a56e2 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");
32874aea 2134 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
c51a56e2 2135 bump("WinSock version is incompatible with 1.1");
07d9aa13 2136}
2137
07d9aa13 2138/*
2139 * Short description of parameters.
2140 */
996c8c3b 2141static void usage(void)
07d9aa13 2142{
c51a56e2 2143 printf("PuTTY Secure Copy client\n");
2144 printf("%s\n", ver);
a3e55ea1 2145 printf("Usage: pscp [options] [user@]host:source target\n");
32874aea 2146 printf
2147 (" pscp [options] source [source...] [user@]host:target\n");
a3e55ea1 2148 printf(" pscp [options] -ls user@host:filespec\n");
b8a19193 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");
cd1f39ab 2156 printf(" -unsafe allow server-side wildcards (DANGEROUS)\n");
ee8b0370 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 */
32874aea 2165 printf
2166 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
ee8b0370 2167#endif
c51a56e2 2168 exit(1);
07d9aa13 2169}
2170
07d9aa13 2171/*
2172 * Main program (no, really?)
2173 */
2174int main(int argc, char *argv[])
2175{
c51a56e2 2176 int i;
2177
fb09bf1c 2178 default_protocol = PROT_TELNET;
2179
67779be7 2180 flags = FLAG_STDERR;
fa17a66e 2181 ssh_get_line = &get_line;
c51a56e2 2182 init_winsock();
8df7a775 2183 sk_init();
c51a56e2 2184
2185 for (i = 1; i < argc; i++) {
2186 if (argv[i][0] != '-')
2187 break;
2188 if (strcmp(argv[i], "-v") == 0)
4017be6d 2189 verbose = 1, flags |= FLAG_VERBOSE;
c51a56e2 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;
32874aea 2196 else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
c51a56e2 2197 usage();
32874aea 2198 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
b8a19193 2199 portnumber = atoi(argv[++i]);
32874aea 2200 else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
b8a19193 2201 password = argv[++i];
32874aea 2202 else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
cc87246d 2203 gui_hwnd = argv[++i];
2204 gui_mode = 1;
2205 } else if (strcmp(argv[i], "-ls") == 0)
32874aea 2206 list = 1;
cd1f39ab 2207 else if (strcmp(argv[i], "-unsafe") == 0)
2208 scp_unsafe_mode = 1;
32874aea 2209 else if (strcmp(argv[i], "--") == 0) {
2210 i++;
2211 break;
2212 } else
c51a56e2 2213 usage();
2214 }
2215 argc -= i;
2216 argv += i;
eba78553 2217 back = NULL;
c51a56e2 2218
39ddf0ff 2219 if (list) {
2220 if (argc != 1)
2221 usage();
2222 get_dir_list(argc, argv);
c51a56e2 2223
39ddf0ff 2224 } else {
2225
2226 if (argc < 2)
2227 usage();
2228 if (argc > 2)
2229 targetshouldbedirectory = 1;
2230
32874aea 2231 if (colon(argv[argc - 1]) != NULL)
39ddf0ff 2232 toremote(argc, argv);
2233 else
2234 tolocal(argc, argv);
2235 }
c51a56e2 2236
eba78553 2237 if (back != NULL && back->socket() != NULL) {
c51a56e2 2238 char ch;
3bdaf79d 2239 back->special(TS_EOF);
fb09bf1c 2240 ssh_scp_recv(&ch, 1);
c51a56e2 2241 }
2242 WSACleanup();
2243 random_save_seed();
07d9aa13 2244
cc87246d 2245 /* GUI Adaptation - August 2000 */
2246 if (gui_mode) {
2247 unsigned int msg_id = WM_RET_ERR_CNT;
32874aea 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);
cc87246d 2253 }
c51a56e2 2254 return (errs == 0 ? 0 : 1);
07d9aa13 2255}
2256
2257/* end */