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