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