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