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