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