]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - scp.c
Arrgh; yet again I make my security checking too draconian to
[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 <time.h>
23 #include <assert.h>
24 /* GUI Adaptation - Sept 2000 */
25 #include <winuser.h>
26 #include <winbase.h>
27
28 #define PUTTY_DO_GLOBALS
29 #include "putty.h"
30 #include "winstuff.h"
31 #include "storage.h"
32
33 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
34         ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
35 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
36         ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
37
38 /* GUI Adaptation - Sept 2000 */
39 #define   WM_APP_BASE           0x8000
40 #define   WM_STD_OUT_CHAR       ( WM_APP_BASE+400 )
41 #define   WM_STD_ERR_CHAR       ( WM_APP_BASE+401 )
42 #define   WM_STATS_CHAR         ( WM_APP_BASE+402 )
43 #define   WM_STATS_SIZE         ( WM_APP_BASE+403 )
44 #define   WM_STATS_PERCENT      ( WM_APP_BASE+404 )
45 #define   WM_STATS_ELAPSED      ( WM_APP_BASE+405 )
46 #define   WM_RET_ERR_CNT        ( WM_APP_BASE+406 )
47 #define   WM_LS_RET_ERR_CNT     ( WM_APP_BASE+407 )
48
49 static int list = 0;
50 static int verbose = 0;
51 static int recursive = 0;
52 static int preserve = 0;
53 static int targetshouldbedirectory = 0;
54 static int statistics = 1;
55 static int portnumber = 0;
56 static int prev_stats_len = 0;
57 static char *password = NULL;
58 static int errs = 0;
59 /* GUI Adaptation - Sept 2000 */
60 #define NAME_STR_MAX 2048
61 static char statname[NAME_STR_MAX + 1];
62 static unsigned long statsize = 0;
63 static int statperct = 0;
64 static unsigned long statelapsed = 0;
65 static int gui_mode = 0;
66 static char *gui_hwnd = NULL;
67
68 static void source(char *src);
69 static void rsource(char *src);
70 static void sink(char *targ, char *src);
71 /* GUI Adaptation - Sept 2000 */
72 static void tell_char(FILE * stream, char c);
73 static void tell_str(FILE * stream, char *str);
74 static void tell_user(FILE * stream, char *fmt, ...);
75 static void gui_update_stats(char *name, unsigned long size,
76                              int percentage, unsigned long elapsed);
77
78 /*
79  * The maximum amount of queued data we accept before we stop and
80  * wait for the server to process some.
81  */
82 #define MAX_SCP_BUFSIZE 16384
83
84 void logevent(char *string)
85 {
86 }
87
88 void ldisc_send(char *buf, int len)
89 {
90     /*
91      * This is only here because of the calls to ldisc_send(NULL,
92      * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
93      * as an ldisc. So if we get called with any real data, I want
94      * to know about it.
95      */
96     assert(len == 0);
97 }
98
99 void verify_ssh_host_key(char *host, int port, char *keytype,
100                          char *keystr, char *fingerprint)
101 {
102     int ret;
103     HANDLE hin;
104     DWORD savemode, i;
105
106     static const char absentmsg[] =
107         "The server's host key is not cached in the registry. You\n"
108         "have no guarantee that the server is the computer you\n"
109         "think it is.\n"
110         "The server's key fingerprint is:\n"
111         "%s\n"
112         "If you trust this host, enter \"y\" to add the key to\n"
113         "PuTTY's cache and carry on connecting.\n"
114         "If you want to carry on connecting just once, without\n"
115         "adding the key to the cache, enter \"n\".\n"
116         "If you do not trust this host, press Return to abandon the\n"
117         "connection.\n"
118         "Store key in cache? (y/n) ";
119
120     static const char wrongmsg[] =
121         "WARNING - POTENTIAL SECURITY BREACH!\n"
122         "The server's host key does not match the one PuTTY has\n"
123         "cached in the registry. This means that either the\n"
124         "server administrator has changed the host key, or you\n"
125         "have actually connected to another computer pretending\n"
126         "to be the server.\n"
127         "The new key fingerprint is:\n"
128         "%s\n"
129         "If you were expecting this change and trust the new key,\n"
130         "enter \"y\" to update PuTTY's cache and continue connecting.\n"
131         "If you want to carry on connecting but without updating\n"
132         "the cache, enter \"n\".\n"
133         "If you want to abandon the connection completely, press\n"
134         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
135         "safe choice.\n"
136         "Update cached key? (y/n, Return cancels connection) ";
137
138     static const char abandoned[] = "Connection abandoned.\n";
139
140     char line[32];
141
142     /*
143      * Verify the key against the registry.
144      */
145     ret = verify_host_key(host, port, keytype, keystr);
146
147     if (ret == 0)                      /* success - key matched OK */
148         return;
149
150     if (ret == 2) {                    /* key was different */
151         fprintf(stderr, wrongmsg, fingerprint);
152         fflush(stderr);
153     }
154     if (ret == 1) {                    /* key was absent */
155         fprintf(stderr, absentmsg, fingerprint);
156         fflush(stderr);
157     }
158
159     hin = GetStdHandle(STD_INPUT_HANDLE);
160     GetConsoleMode(hin, &savemode);
161     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
162                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
163     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
164     SetConsoleMode(hin, savemode);
165
166     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
167         if (line[0] == 'y' || line[0] == 'Y')
168             store_host_key(host, port, keytype, keystr);
169     } else {
170         fprintf(stderr, abandoned);
171         exit(0);
172     }
173 }
174
175 /*
176  * Ask whether the selected cipher is acceptable (since it was
177  * below the configured 'warn' threshold).
178  * cs: 0 = both ways, 1 = client->server, 2 = server->client
179  */
180 void askcipher(char *ciphername, int cs)
181 {
182     HANDLE hin;
183     DWORD savemode, i;
184
185     static const char msg[] =
186         "The first %scipher supported by the server is\n"
187         "%s, which is below the configured warning threshold.\n"
188         "Continue with connection? (y/n) ";
189     static const char abandoned[] = "Connection abandoned.\n";
190
191     char line[32];
192
193     fprintf(stderr, msg,
194             (cs == 0) ? "" :
195             (cs == 1) ? "client-to-server " :
196                         "server-to-client ",
197             ciphername);
198     fflush(stderr);
199
200     hin = GetStdHandle(STD_INPUT_HANDLE);
201     GetConsoleMode(hin, &savemode);
202     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
203                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
204     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
205     SetConsoleMode(hin, savemode);
206
207     if (line[0] == 'y' || line[0] == 'Y') {
208         return;
209     } else {
210         fprintf(stderr, abandoned);
211         exit(0);
212     }
213 }
214
215 /* GUI Adaptation - Sept 2000 */
216 static void send_msg(HWND h, UINT message, WPARAM wParam)
217 {
218     while (!PostMessage(h, message, wParam, 0))
219         SleepEx(1000, TRUE);
220 }
221
222 static void tell_char(FILE * stream, char c)
223 {
224     if (!gui_mode)
225         fputc(c, stream);
226     else {
227         unsigned int msg_id = WM_STD_OUT_CHAR;
228         if (stream == stderr)
229             msg_id = WM_STD_ERR_CHAR;
230         send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
231     }
232 }
233
234 static void tell_str(FILE * stream, char *str)
235 {
236     unsigned int i;
237
238     for (i = 0; i < strlen(str); ++i)
239         tell_char(stream, str[i]);
240 }
241
242 static void tell_user(FILE * stream, char *fmt, ...)
243 {
244     char str[0x100];                   /* Make the size big enough */
245     va_list ap;
246     va_start(ap, fmt);
247     vsprintf(str, fmt, ap);
248     va_end(ap);
249     strcat(str, "\n");
250     tell_str(stream, str);
251 }
252
253 static void gui_update_stats(char *name, unsigned long size,
254                              int percentage, unsigned long elapsed)
255 {
256     unsigned int i;
257
258     if (strcmp(name, statname) != 0) {
259         for (i = 0; i < strlen(name); ++i)
260             send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
261                      (WPARAM) name[i]);
262         send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
263         strcpy(statname, name);
264     }
265     if (statsize != size) {
266         send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
267         statsize = size;
268     }
269     if (statelapsed != elapsed) {
270         send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
271                  (WPARAM) elapsed);
272         statelapsed = elapsed;
273     }
274     if (statperct != percentage) {
275         send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
276                  (WPARAM) percentage);
277         statperct = percentage;
278     }
279 }
280
281 /*
282  *  Print an error message and perform a fatal exit.
283  */
284 void fatalbox(char *fmt, ...)
285 {
286     char str[0x100];                   /* Make the size big enough */
287     va_list ap;
288     va_start(ap, fmt);
289     strcpy(str, "Fatal: ");
290     vsprintf(str + strlen(str), fmt, ap);
291     va_end(ap);
292     strcat(str, "\n");
293     tell_str(stderr, str);
294     errs++;
295
296     if (gui_mode) {
297         unsigned int msg_id = WM_RET_ERR_CNT;
298         if (list)
299             msg_id = WM_LS_RET_ERR_CNT;
300         while (!PostMessage
301                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
302                 0 /*lParam */ ))SleepEx(1000, TRUE);
303     }
304
305     exit(1);
306 }
307 void connection_fatal(char *fmt, ...)
308 {
309     char str[0x100];                   /* Make the size big enough */
310     va_list ap;
311     va_start(ap, fmt);
312     strcpy(str, "Fatal: ");
313     vsprintf(str + strlen(str), fmt, ap);
314     va_end(ap);
315     strcat(str, "\n");
316     tell_str(stderr, str);
317     errs++;
318
319     if (gui_mode) {
320         unsigned int msg_id = WM_RET_ERR_CNT;
321         if (list)
322             msg_id = WM_LS_RET_ERR_CNT;
323         while (!PostMessage
324                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
325                 0 /*lParam */ ))SleepEx(1000, TRUE);
326     }
327
328     exit(1);
329 }
330
331 /*
332  * Be told what socket we're supposed to be using.
333  */
334 static SOCKET scp_ssh_socket;
335 char *do_select(SOCKET skt, int startup)
336 {
337     if (startup)
338         scp_ssh_socket = skt;
339     else
340         scp_ssh_socket = INVALID_SOCKET;
341     return NULL;
342 }
343 extern int select_result(WPARAM, LPARAM);
344
345 /*
346  * Receive a block of data from the SSH link. Block until all data
347  * is available.
348  *
349  * To do this, we repeatedly call the SSH protocol module, with our
350  * own trap in from_backend() to catch the data that comes back. We
351  * do this until we have enough data.
352  */
353
354 static unsigned char *outptr;          /* where to put the data */
355 static unsigned outlen;                /* how much data required */
356 static unsigned char *pending = NULL;  /* any spare data */
357 static unsigned pendlen = 0, pendsize = 0;      /* length and phys. size of buffer */
358 int from_backend(int is_stderr, char *data, int datalen)
359 {
360     unsigned char *p = (unsigned char *) data;
361     unsigned len = (unsigned) datalen;
362
363     /*
364      * stderr data is just spouted to local stderr and otherwise
365      * ignored.
366      */
367     if (is_stderr) {
368         fwrite(data, 1, len, stderr);
369         return 0;
370     }
371
372     inbuf_head = 0;
373
374     /*
375      * If this is before the real session begins, just return.
376      */
377     if (!outptr)
378         return 0;
379
380     if (outlen > 0) {
381         unsigned used = outlen;
382         if (used > len)
383             used = len;
384         memcpy(outptr, p, used);
385         outptr += used;
386         outlen -= used;
387         p += used;
388         len -= used;
389     }
390
391     if (len > 0) {
392         if (pendsize < pendlen + len) {
393             pendsize = pendlen + len + 4096;
394             pending = (pending ? srealloc(pending, pendsize) :
395                        smalloc(pendsize));
396             if (!pending)
397                 fatalbox("Out of memory");
398         }
399         memcpy(pending + pendlen, p, len);
400         pendlen += len;
401     }
402
403     return 0;
404 }
405 static int scp_process_network_event(void)
406 {
407     fd_set readfds;
408
409     FD_ZERO(&readfds);
410     FD_SET(scp_ssh_socket, &readfds);
411     if (select(1, &readfds, NULL, NULL, NULL) < 0)
412         return 0;                      /* doom */
413     select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
414     return 1;
415 }
416 static int ssh_scp_recv(unsigned char *buf, int len)
417 {
418     outptr = buf;
419     outlen = len;
420
421     /*
422      * See if the pending-input block contains some of what we
423      * need.
424      */
425     if (pendlen > 0) {
426         unsigned pendused = pendlen;
427         if (pendused > outlen)
428             pendused = outlen;
429         memcpy(outptr, pending, pendused);
430         memmove(pending, pending + pendused, pendlen - pendused);
431         outptr += pendused;
432         outlen -= pendused;
433         pendlen -= pendused;
434         if (pendlen == 0) {
435             pendsize = 0;
436             sfree(pending);
437             pending = NULL;
438         }
439         if (outlen == 0)
440             return len;
441     }
442
443     while (outlen > 0) {
444         if (!scp_process_network_event())
445             return 0;                  /* doom */
446     }
447
448     return len;
449 }
450
451 /*
452  * Loop through the ssh connection and authentication process.
453  */
454 static void ssh_scp_init(void)
455 {
456     if (scp_ssh_socket == INVALID_SOCKET)
457         return;
458     while (!back->sendok()) {
459         fd_set readfds;
460         FD_ZERO(&readfds);
461         FD_SET(scp_ssh_socket, &readfds);
462         if (select(1, &readfds, NULL, NULL, NULL) < 0)
463             return;                    /* doom */
464         select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
465     }
466 }
467
468 /*
469  *  Print an error message and exit after closing the SSH link.
470  */
471 static void bump(char *fmt, ...)
472 {
473     char str[0x100];                   /* Make the size big enough */
474     va_list ap;
475     va_start(ap, fmt);
476     strcpy(str, "Fatal: ");
477     vsprintf(str + strlen(str), fmt, ap);
478     va_end(ap);
479     strcat(str, "\n");
480     tell_str(stderr, str);
481     errs++;
482
483     if (back != NULL && back->socket() != NULL) {
484         char ch;
485         back->special(TS_EOF);
486         ssh_scp_recv(&ch, 1);
487     }
488
489     if (gui_mode) {
490         unsigned int msg_id = WM_RET_ERR_CNT;
491         if (list)
492             msg_id = WM_LS_RET_ERR_CNT;
493         while (!PostMessage
494                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
495                 0 /*lParam */ ))SleepEx(1000, TRUE);
496     }
497
498     exit(1);
499 }
500
501 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
502 {
503     HANDLE hin, hout;
504     DWORD savemode, newmode, i;
505
506     if (is_pw && password) {
507         static int tried_once = 0;
508
509         if (tried_once) {
510             return 0;
511         } else {
512             strncpy(str, password, maxlen);
513             str[maxlen - 1] = '\0';
514             tried_once = 1;
515             return 1;
516         }
517     }
518
519     /* GUI Adaptation - Sept 2000 */
520     if (gui_mode) {
521         if (maxlen > 0)
522             str[0] = '\0';
523     } else {
524         hin = GetStdHandle(STD_INPUT_HANDLE);
525         hout = GetStdHandle(STD_OUTPUT_HANDLE);
526         if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
527             bump("Cannot get standard input/output handles");
528
529         GetConsoleMode(hin, &savemode);
530         newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
531         if (is_pw)
532             newmode &= ~ENABLE_ECHO_INPUT;
533         else
534             newmode |= ENABLE_ECHO_INPUT;
535         SetConsoleMode(hin, newmode);
536
537         WriteFile(hout, prompt, strlen(prompt), &i, NULL);
538         ReadFile(hin, str, maxlen - 1, &i, NULL);
539
540         SetConsoleMode(hin, savemode);
541
542         if ((int) i > maxlen)
543             i = maxlen - 1;
544         else
545             i = i - 2;
546         str[i] = '\0';
547
548         if (is_pw)
549             WriteFile(hout, "\r\n", 2, &i, NULL);
550     }
551
552     return 1;
553 }
554
555 /*
556  *  Open an SSH connection to user@host and execute cmd.
557  */
558 static void do_cmd(char *host, char *user, char *cmd)
559 {
560     char *err, *realhost;
561     DWORD namelen;
562
563     if (host == NULL || host[0] == '\0')
564         bump("Empty host name");
565
566     /* Try to load settings for this host */
567     do_defaults(host, &cfg);
568     if (cfg.host[0] == '\0') {
569         /* No settings for this host; use defaults */
570         do_defaults(NULL, &cfg);
571         strncpy(cfg.host, host, sizeof(cfg.host) - 1);
572         cfg.host[sizeof(cfg.host) - 1] = '\0';
573         cfg.port = 22;
574     }
575
576     /* Set username */
577     if (user != NULL && user[0] != '\0') {
578         strncpy(cfg.username, user, sizeof(cfg.username) - 1);
579         cfg.username[sizeof(cfg.username) - 1] = '\0';
580     } else if (cfg.username[0] == '\0') {
581         namelen = 0;
582         if (GetUserName(user, &namelen) == FALSE)
583             bump("Empty user name");
584         user = smalloc(namelen * sizeof(char));
585         GetUserName(user, &namelen);
586         if (verbose)
587             tell_user(stderr, "Guessing user name: %s", user);
588         strncpy(cfg.username, user, sizeof(cfg.username) - 1);
589         cfg.username[sizeof(cfg.username) - 1] = '\0';
590         free(user);
591     }
592
593     if (cfg.protocol != PROT_SSH)
594         cfg.port = 22;
595
596     if (portnumber)
597         cfg.port = portnumber;
598
599     strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
600     cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
601     cfg.nopty = TRUE;
602
603     back = &ssh_backend;
604
605     err = back->init(cfg.host, cfg.port, &realhost);
606     if (err != NULL)
607         bump("ssh_init: %s", err);
608     ssh_scp_init();
609     if (verbose && realhost != NULL)
610         tell_user(stderr, "Connected to %s\n", realhost);
611     sfree(realhost);
612 }
613
614 /*
615  *  Update statistic information about current file.
616  */
617 static void print_stats(char *name, unsigned long size, unsigned long done,
618                         time_t start, time_t now)
619 {
620     float ratebs;
621     unsigned long eta;
622     char etastr[10];
623     int pct;
624     int len;
625
626     /* GUI Adaptation - Sept 2000 */
627     if (gui_mode)
628         gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
629                          (unsigned long) difftime(now, start));
630     else {
631         if (now > start)
632             ratebs = (float) done / (now - start);
633         else
634             ratebs = (float) done;
635
636         if (ratebs < 1.0)
637             eta = size - done;
638         else
639             eta = (unsigned long) ((size - done) / ratebs);
640         sprintf(etastr, "%02ld:%02ld:%02ld",
641                 eta / 3600, (eta % 3600) / 60, eta % 60);
642
643         pct = (int) (100.0 * (float) done / size);
644
645         len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
646                      name, done / 1024, ratebs / 1024.0, etastr, pct);
647         if (len < prev_stats_len)
648             printf("%*s", prev_stats_len - len, "");
649         prev_stats_len = len;
650
651         if (done == size)
652             printf("\n");
653     }
654 }
655
656 /*
657  *  Find a colon in str and return a pointer to the colon.
658  *  This is used to separate hostname from filename.
659  */
660 static char *colon(char *str)
661 {
662     /* We ignore a leading colon, since the hostname cannot be
663        empty. We also ignore a colon as second character because
664        of filenames like f:myfile.txt. */
665     if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
666         return (NULL);
667     while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
668         str++;
669     if (*str == ':')
670         return (str);
671     else
672         return (NULL);
673 }
674
675 /*
676  * Return a pointer to the portion of str that comes after the last
677  * slash or backslash.
678  */
679 static char *stripslashes(char *str)
680 {
681     char *p;
682
683     p = strrchr(str, '/');
684     if (p) str = p+1;
685
686     p = strrchr(str, '\\');
687     if (p) str = p+1;
688
689     return str;
690 }
691
692 /*
693  *  Wait for a response from the other side.
694  *  Return 0 if ok, -1 if error.
695  */
696 static int response(void)
697 {
698     char ch, resp, rbuf[2048];
699     int p;
700
701     if (ssh_scp_recv(&resp, 1) <= 0)
702         bump("Lost connection");
703
704     p = 0;
705     switch (resp) {
706       case 0:                          /* ok */
707         return (0);
708       default:
709         rbuf[p++] = resp;
710         /* fallthrough */
711       case 1:                          /* error */
712       case 2:                          /* fatal error */
713         do {
714             if (ssh_scp_recv(&ch, 1) <= 0)
715                 bump("Protocol error: Lost connection");
716             rbuf[p++] = ch;
717         } while (p < sizeof(rbuf) && ch != '\n');
718         rbuf[p - 1] = '\0';
719         if (resp == 1)
720             tell_user(stderr, "%s\n", rbuf);
721         else
722             bump("%s", rbuf);
723         errs++;
724         return (-1);
725     }
726 }
727
728 /* ----------------------------------------------------------------------
729  * Helper routines that contain the actual SCP protocol elements,
730  * so they can be switched to use SFTP.
731  */
732
733 int scp_send_errmsg(char *str)
734 {
735     back->send("\001", 1);             /* scp protocol error prefix */
736     back->send(str, strlen(str));
737     return 0;                          /* can't fail */
738 }
739
740 int scp_send_filetimes(unsigned long mtime, unsigned long atime)
741 {
742     char buf[80];
743     sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
744     back->send(buf, strlen(buf));
745     return response();
746 }
747
748 int scp_send_filename(char *name, unsigned long size, int modes)
749 {
750     char buf[40];
751     sprintf(buf, "C%04o %lu ", modes, size);
752     back->send(buf, strlen(buf));
753     back->send(name, strlen(name));
754     back->send("\n", 1);
755     return response();
756 }
757
758 int scp_send_filedata(char *data, int len)
759 {
760     int bufsize = back->send(data, len);
761
762     /*
763      * If the network transfer is backing up - that is, the remote
764      * site is not accepting data as fast as we can produce it -
765      * then we must loop on network events until we have space in
766      * the buffer again.
767      */
768     while (bufsize > MAX_SCP_BUFSIZE) {
769         if (!scp_process_network_event())
770             return 1;
771         bufsize = back->sendbuffer();
772     }
773
774     return 0;
775 }
776
777 int scp_send_finish(void)
778 {
779     back->send("", 1);
780     return response();
781 }
782
783 int scp_send_dirname(char *name, int modes)
784 {
785     char buf[40];
786     sprintf(buf, "D%04o 0 ", modes);
787     back->send(buf, strlen(buf));
788     back->send(name, strlen(name));
789     back->send("\n", 1);
790     return response();
791 }
792
793 int scp_send_enddir(void)
794 {
795     back->send("E\n", 2);
796     return response();
797 }
798
799 int scp_sink_init(void)
800 {
801     back->send("", 1);
802     return 0;
803 }
804
805 #define SCP_SINK_FILE   1
806 #define SCP_SINK_DIR    2
807 #define SCP_SINK_ENDDIR 3
808 struct scp_sink_action {
809     int action;                        /* FILE, DIR, ENDDIR */
810     char *buf;                         /* will need freeing after use */
811     char *name;                        /* filename or dirname (not ENDDIR) */
812     int mode;                          /* access mode (not ENDDIR) */
813     unsigned long size;                /* file size (not ENDDIR) */
814     int settime;                       /* 1 if atime and mtime are filled */
815     unsigned long atime, mtime;        /* access times for the file */
816 };
817
818 int scp_get_sink_action(struct scp_sink_action *act)
819 {
820     int done = 0;
821     int i, bufsize;
822     int action;
823     char ch;
824
825     act->settime = 0;
826     act->buf = NULL;
827     bufsize = 0;
828
829     while (!done) {
830         if (ssh_scp_recv(&ch, 1) <= 0)
831             return 1;
832         if (ch == '\n')
833             bump("Protocol error: Unexpected newline");
834         i = 0;
835         action = ch;
836         do {
837             if (ssh_scp_recv(&ch, 1) <= 0)
838                 bump("Lost connection");
839             if (i >= bufsize) {
840                 bufsize = i + 128;
841                 act->buf = srealloc(act->buf, bufsize);
842             }
843             act->buf[i++] = ch;
844         } while (ch != '\n');
845         act->buf[i - 1] = '\0';
846         switch (action) {
847           case '\01':                  /* error */
848             tell_user(stderr, "%s\n", act->buf);
849             errs++;
850             continue;                  /* go round again */
851           case '\02':                  /* fatal error */
852             bump("%s", act->buf);
853           case 'E':
854             back->send("", 1);
855             act->action = SCP_SINK_ENDDIR;
856             return 0;
857           case 'T':
858             if (sscanf(act->buf, "%ld %*d %ld %*d",
859                        &act->mtime, &act->atime) == 2) {
860                 act->settime = 1;
861                 back->send("", 1);
862                 continue;              /* go round again */
863             }
864             bump("Protocol error: Illegal time format");
865           case 'C':
866           case 'D':
867             act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
868             break;
869           default:
870             bump("Protocol error: Expected control record");
871         }
872         /*
873          * We will go round this loop only once, unless we hit
874          * `continue' above.
875          */
876         done = 1;
877     }
878
879     /*
880      * If we get here, we must have seen SCP_SINK_FILE or
881      * SCP_SINK_DIR.
882      */
883     if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
884         bump("Protocol error: Illegal file descriptor format");
885     act->name = act->buf + i;
886     return 0;
887 }
888
889 int scp_accept_filexfer(void)
890 {
891     back->send("", 1);
892     return 0;                          /* can't fail */
893 }
894
895 int scp_recv_filedata(char *data, int len)
896 {
897     return ssh_scp_recv(data, len);
898 }
899
900 int scp_finish_filerecv(void)
901 {
902     back->send("", 1);
903     return response();
904 }
905
906 /* ----------------------------------------------------------------------
907  *  Send an error message to the other side and to the screen.
908  *  Increment error counter.
909  */
910 static void run_err(const char *fmt, ...)
911 {
912     char str[2048];
913     va_list ap;
914     va_start(ap, fmt);
915     errs++;
916     strcpy(str, "scp: ");
917     vsprintf(str + strlen(str), fmt, ap);
918     strcat(str, "\n");
919     scp_send_errmsg(str);
920     tell_user(stderr, "%s", str);
921     va_end(ap);
922 }
923
924 /*
925  *  Execute the source part of the SCP protocol.
926  */
927 static void source(char *src)
928 {
929     unsigned long size;
930     char *last;
931     HANDLE f;
932     DWORD attr;
933     unsigned long i;
934     unsigned long stat_bytes;
935     time_t stat_starttime, stat_lasttime;
936
937     attr = GetFileAttributes(src);
938     if (attr == (DWORD) - 1) {
939         run_err("%s: No such file or directory", src);
940         return;
941     }
942
943     if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
944         if (recursive) {
945             /*
946              * Avoid . and .. directories.
947              */
948             char *p;
949             p = strrchr(src, '/');
950             if (!p)
951                 p = strrchr(src, '\\');
952             if (!p)
953                 p = src;
954             else
955                 p++;
956             if (!strcmp(p, ".") || !strcmp(p, ".."))
957                 /* skip . and .. */ ;
958             else
959                 rsource(src);
960         } else {
961             run_err("%s: not a regular file", src);
962         }
963         return;
964     }
965
966     if ((last = strrchr(src, '/')) == NULL)
967         last = src;
968     else
969         last++;
970     if (strrchr(last, '\\') != NULL)
971         last = strrchr(last, '\\') + 1;
972     if (last == src && strchr(src, ':') != NULL)
973         last = strchr(src, ':') + 1;
974
975     f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
976                    OPEN_EXISTING, 0, 0);
977     if (f == INVALID_HANDLE_VALUE) {
978         run_err("%s: Cannot open file", src);
979         return;
980     }
981
982     if (preserve) {
983         FILETIME actime, wrtime;
984         unsigned long mtime, atime;
985         GetFileTime(f, NULL, &actime, &wrtime);
986         TIME_WIN_TO_POSIX(actime, atime);
987         TIME_WIN_TO_POSIX(wrtime, mtime);
988         if (scp_send_filetimes(mtime, atime))
989             return;
990     }
991
992     size = GetFileSize(f, NULL);
993     if (verbose)
994         tell_user(stderr, "Sending file %s, size=%lu", last, size);
995     if (scp_send_filename(last, size, 0644))
996         return;
997
998     stat_bytes = 0;
999     stat_starttime = time(NULL);
1000     stat_lasttime = 0;
1001
1002     for (i = 0; i < size; i += 4096) {
1003         char transbuf[4096];
1004         DWORD j, k = 4096;
1005
1006         if (i + k > size)
1007             k = size - i;
1008         if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
1009             if (statistics)
1010                 printf("\n");
1011             bump("%s: Read error", src);
1012         }
1013         if (scp_send_filedata(transbuf, k))
1014             bump("%s: Network error occurred", src);
1015
1016         if (statistics) {
1017             stat_bytes += k;
1018             if (time(NULL) != stat_lasttime || i + k == size) {
1019                 stat_lasttime = time(NULL);
1020                 print_stats(last, size, stat_bytes,
1021                             stat_starttime, stat_lasttime);
1022             }
1023         }
1024
1025     }
1026     CloseHandle(f);
1027
1028     (void) scp_send_finish();
1029 }
1030
1031 /*
1032  *  Recursively send the contents of a directory.
1033  */
1034 static void rsource(char *src)
1035 {
1036     char *last, *findfile;
1037     HANDLE dir;
1038     WIN32_FIND_DATA fdat;
1039     int ok;
1040
1041     if ((last = strrchr(src, '/')) == NULL)
1042         last = src;
1043     else
1044         last++;
1045     if (strrchr(last, '\\') != NULL)
1046         last = strrchr(last, '\\') + 1;
1047     if (last == src && strchr(src, ':') != NULL)
1048         last = strchr(src, ':') + 1;
1049
1050     /* maybe send filetime */
1051
1052     if (verbose)
1053         tell_user(stderr, "Entering directory: %s", last);
1054     if (scp_send_dirname(last, 0755))
1055         return;
1056
1057     findfile = dupcat(src, "/*", NULL);
1058     dir = FindFirstFile(findfile, &fdat);
1059     ok = (dir != INVALID_HANDLE_VALUE);
1060     while (ok) {
1061         if (strcmp(fdat.cFileName, ".") == 0 ||
1062             strcmp(fdat.cFileName, "..") == 0) {
1063             /* ignore . and .. */
1064         } else {
1065             char *foundfile = dupcat(src, "/", fdat.cFileName);
1066             source(foundfile);
1067             sfree(foundfile);
1068         }
1069         ok = FindNextFile(dir, &fdat);
1070     }
1071     FindClose(dir);
1072     sfree(findfile);
1073
1074     (void) scp_send_enddir();
1075 }
1076
1077 /*
1078  * Execute the sink part of the SCP protocol.
1079  */
1080 static void sink(char *targ, char *src)
1081 {
1082     char *destfname;
1083     char ch;
1084     int targisdir = 0;
1085     int settime;
1086     int exists;
1087     DWORD attr;
1088     HANDLE f;
1089     unsigned long received;
1090     int wrerror = 0;
1091     unsigned long stat_bytes;
1092     time_t stat_starttime, stat_lasttime;
1093     char *stat_name;
1094
1095     attr = GetFileAttributes(targ);
1096     if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
1097         targisdir = 1;
1098
1099     if (targetshouldbedirectory && !targisdir)
1100         bump("%s: Not a directory", targ);
1101
1102     scp_sink_init();
1103     while (1) {
1104         struct scp_sink_action act;
1105         if (scp_get_sink_action(&act))
1106             return;
1107
1108         if (act.action == SCP_SINK_ENDDIR)
1109             return;
1110
1111         if (targisdir) {
1112             /*
1113              * Prevent the remote side from maliciously writing to
1114              * files outside the target area by sending a filename
1115              * containing `../'. In fact, it shouldn't be sending
1116              * filenames with any slashes in at all; so we'll find
1117              * the last slash or backslash in the filename and use
1118              * only the part after that. (And warn!)
1119              * 
1120              * In addition, we also ensure here that if we're
1121              * copying a single file and the target is a directory
1122              * (common usage: `pscp host:filename .') the remote
1123              * can't send us a _different_ file name. We can
1124              * distinguish this case because `src' will be non-NULL
1125              * and the last component of that will fail to match
1126              * (the last component of) the name sent.
1127              * 
1128              * (Well, not always; if `src' is a wildcard, we do
1129              * expect to get back filenames that don't correspond
1130              * exactly to it. So we skip this check if `src'
1131              * contains a *, a ? or a []. This is non-ideal - we
1132              * would like to ensure that the returned filename
1133              * actually matches the wildcard pattern - but one of
1134              * SCP's protocol infelicities is that wildcard
1135              * matching is done at the server end _by the server's
1136              * rules_ and so in general this is infeasible. Live
1137              * with it, or upgrade to SFTP.)
1138              */
1139             char *striptarget, *stripsrc;
1140
1141             striptarget = stripslashes(act.name);
1142             if (striptarget != act.name) {
1143                 tell_user(stderr, "warning: remote host sent a compound"
1144                           " pathname - possibly malicious! (ignored)");
1145             }
1146
1147             /*
1148              * Also check to see if the target filename is '.' or
1149              * '..', or indeed '...' and so on because Windows
1150              * appears to interpret those like '..'.
1151              */
1152             if (striptarget[strspn(striptarget, ".")] == '\0') {
1153                 bump("security violation: remote host attempted to write to"
1154                      " a '.' or '..' path!");
1155             }
1156
1157             if (src) {
1158                 stripsrc = stripslashes(src);
1159                 if (!stripsrc[strcspn(stripsrc, "*?[]")] &&
1160                     strcmp(striptarget, stripsrc)) {
1161                     tell_user(stderr, "warning: remote host attempted to"
1162                               " write to a different filename: disallowing");
1163                     /* Override the name the server provided with our own. */
1164                     striptarget = stripsrc;
1165                 }
1166             }
1167
1168             if (targ[0] != '\0')
1169                 destfname = dupcat(targ, "\\", striptarget, NULL);
1170             else
1171                 destfname = dupstr(striptarget);
1172         } else {
1173             /*
1174              * In this branch of the if, the target area is a
1175              * single file with an explicitly specified name in any
1176              * case, so there's no danger.
1177              */
1178             destfname = dupstr(targ);
1179         }
1180         attr = GetFileAttributes(destfname);
1181         exists = (attr != (DWORD) - 1);
1182
1183         if (act.action == SCP_SINK_DIR) {
1184             if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
1185                 run_err("%s: Not a directory", destfname);
1186                 continue;
1187             }
1188             if (!exists) {
1189                 if (!CreateDirectory(destfname, NULL)) {
1190                     run_err("%s: Cannot create directory", destfname);
1191                     continue;
1192                 }
1193             }
1194             sink(destfname, NULL);
1195             /* can we set the timestamp for directories ? */
1196             continue;
1197         }
1198
1199         f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
1200                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
1201         if (f == INVALID_HANDLE_VALUE) {
1202             run_err("%s: Cannot create file", destfname);
1203             continue;
1204         }
1205
1206         if (scp_accept_filexfer())
1207             return;
1208
1209         stat_bytes = 0;
1210         stat_starttime = time(NULL);
1211         stat_lasttime = 0;
1212         stat_name = stripslashes(destfname);
1213
1214         received = 0;
1215         while (received < act.size) {
1216             char transbuf[4096];
1217             DWORD blksize, read, written;
1218             blksize = 4096;
1219             if (blksize > act.size - received)
1220                 blksize = act.size - received;
1221             read = scp_recv_filedata(transbuf, blksize);
1222             if (read <= 0)
1223                 bump("Lost connection");
1224             if (wrerror)
1225                 continue;
1226             if (!WriteFile(f, transbuf, read, &written, NULL) ||
1227                 written != read) {
1228                 wrerror = 1;
1229                 /* FIXME: in sftp we can actually abort the transfer */
1230                 if (statistics)
1231                     printf("\r%-25.25s | %50s\n",
1232                            stat_name,
1233                            "Write error.. waiting for end of file");
1234                 continue;
1235             }
1236             if (statistics) {
1237                 stat_bytes += read;
1238                 if (time(NULL) > stat_lasttime ||
1239                     received + read == act.size) {
1240                     stat_lasttime = time(NULL);
1241                     print_stats(stat_name, act.size, stat_bytes,
1242                                 stat_starttime, stat_lasttime);
1243                 }
1244             }
1245             received += read;
1246         }
1247         if (act.settime) {
1248             FILETIME actime, wrtime;
1249             TIME_POSIX_TO_WIN(act.atime, actime);
1250             TIME_POSIX_TO_WIN(act.mtime, wrtime);
1251             SetFileTime(f, NULL, &actime, &wrtime);
1252         }
1253
1254         CloseHandle(f);
1255         if (wrerror) {
1256             run_err("%s: Write error", destfname);
1257             continue;
1258         }
1259         (void) scp_finish_filerecv();
1260         sfree(destfname);
1261         sfree(act.name);
1262     }
1263 }
1264
1265 /*
1266  * We will copy local files to a remote server.
1267  */
1268 static void toremote(int argc, char *argv[])
1269 {
1270     char *src, *targ, *host, *user;
1271     char *cmd;
1272     int i;
1273
1274     targ = argv[argc - 1];
1275
1276     /* Separate host from filename */
1277     host = targ;
1278     targ = colon(targ);
1279     if (targ == NULL)
1280         bump("targ == NULL in toremote()");
1281     *targ++ = '\0';
1282     if (*targ == '\0')
1283         targ = ".";
1284     /* Substitute "." for emtpy target */
1285
1286     /* Separate host and username */
1287     user = host;
1288     host = strrchr(host, '@');
1289     if (host == NULL) {
1290         host = user;
1291         user = NULL;
1292     } else {
1293         *host++ = '\0';
1294         if (*user == '\0')
1295             user = NULL;
1296     }
1297
1298     if (argc == 2) {
1299         /* Find out if the source filespec covers multiple files
1300            if so, we should set the targetshouldbedirectory flag */
1301         HANDLE fh;
1302         WIN32_FIND_DATA fdat;
1303         if (colon(argv[0]) != NULL)
1304             bump("%s: Remote to remote not supported", argv[0]);
1305         fh = FindFirstFile(argv[0], &fdat);
1306         if (fh == INVALID_HANDLE_VALUE)
1307             bump("%s: No such file or directory\n", argv[0]);
1308         if (FindNextFile(fh, &fdat))
1309             targetshouldbedirectory = 1;
1310         FindClose(fh);
1311     }
1312
1313     cmd = smalloc(strlen(targ) + 100);
1314     sprintf(cmd, "scp%s%s%s%s -t %s",
1315             verbose ? " -v" : "",
1316             recursive ? " -r" : "",
1317             preserve ? " -p" : "",
1318             targetshouldbedirectory ? " -d" : "", targ);
1319     do_cmd(host, user, cmd);
1320     sfree(cmd);
1321
1322     (void) response();
1323
1324     for (i = 0; i < argc - 1; i++) {
1325         char *srcpath, *last;
1326         HANDLE dir;
1327         WIN32_FIND_DATA fdat;
1328         src = argv[i];
1329         if (colon(src) != NULL) {
1330             tell_user(stderr, "%s: Remote to remote not supported\n", src);
1331             errs++;
1332             continue;
1333         }
1334
1335         /*
1336          * Trim off the last pathname component of `src', to
1337          * provide the base pathname which will be prepended to
1338          * filenames returned from Find{First,Next}File.
1339          */
1340         srcpath = dupstr(src);
1341         last = stripslashes(srcpath);
1342         if (last == srcpath) {
1343             last = strchr(srcpath, ':');
1344             if (last)
1345                 last++;
1346             else
1347                 last = srcpath;
1348         }
1349         *last = '\0';
1350
1351         dir = FindFirstFile(src, &fdat);
1352         if (dir == INVALID_HANDLE_VALUE) {
1353             run_err("%s: No such file or directory", src);
1354             continue;
1355         }
1356         do {
1357             char *last;
1358             char *filename;
1359             /*
1360              * Ensure that . and .. are never matched by wildcards,
1361              * but only by deliberate action.
1362              */
1363             if (!strcmp(fdat.cFileName, ".") ||
1364                 !strcmp(fdat.cFileName, "..")) {
1365                 /*
1366                  * Find*File has returned a special dir. We require
1367                  * that _either_ `src' ends in a backslash followed
1368                  * by that string, _or_ `src' is precisely that
1369                  * string.
1370                  */
1371                 int len = strlen(src), dlen = strlen(fdat.cFileName);
1372                 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1373                     /* ok */ ;
1374                 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1375                            !strcmp(src + len - dlen, fdat.cFileName)) {
1376                     /* ok */ ;
1377                 } else
1378                     continue;          /* ignore this one */
1379             }
1380             filename = dupcat(srcpath, fdat.cFileName, NULL);
1381             source(filename);
1382             sfree(filename);
1383         } while (FindNextFile(dir, &fdat));
1384         FindClose(dir);
1385         sfree(srcpath);
1386     }
1387 }
1388
1389 /*
1390  *  We will copy files from a remote server to the local machine.
1391  */
1392 static void tolocal(int argc, char *argv[])
1393 {
1394     char *src, *targ, *host, *user;
1395     char *cmd;
1396
1397     if (argc != 2)
1398         bump("More than one remote source not supported");
1399
1400     src = argv[0];
1401     targ = argv[1];
1402
1403     /* Separate host from filename */
1404     host = src;
1405     src = colon(src);
1406     if (src == NULL)
1407         bump("Local to local copy not supported");
1408     *src++ = '\0';
1409     if (*src == '\0')
1410         src = ".";
1411     /* Substitute "." for empty filename */
1412
1413     /* Separate username and hostname */
1414     user = host;
1415     host = strrchr(host, '@');
1416     if (host == NULL) {
1417         host = user;
1418         user = NULL;
1419     } else {
1420         *host++ = '\0';
1421         if (*user == '\0')
1422             user = NULL;
1423     }
1424
1425     cmd = smalloc(strlen(src) + 100);
1426     sprintf(cmd, "scp%s%s%s%s -f %s",
1427             verbose ? " -v" : "",
1428             recursive ? " -r" : "",
1429             preserve ? " -p" : "",
1430             targetshouldbedirectory ? " -d" : "", src);
1431     do_cmd(host, user, cmd);
1432     sfree(cmd);
1433
1434     sink(targ, src);
1435 }
1436
1437 /*
1438  *  We will issue a list command to get a remote directory.
1439  */
1440 static void get_dir_list(int argc, char *argv[])
1441 {
1442     char *src, *host, *user;
1443     char *cmd, *p, *q;
1444     char c;
1445
1446     src = argv[0];
1447
1448     /* Separate host from filename */
1449     host = src;
1450     src = colon(src);
1451     if (src == NULL)
1452         bump("Local to local copy not supported");
1453     *src++ = '\0';
1454     if (*src == '\0')
1455         src = ".";
1456     /* Substitute "." for empty filename */
1457
1458     /* Separate username and hostname */
1459     user = host;
1460     host = strrchr(host, '@');
1461     if (host == NULL) {
1462         host = user;
1463         user = NULL;
1464     } else {
1465         *host++ = '\0';
1466         if (*user == '\0')
1467             user = NULL;
1468     }
1469
1470     cmd = smalloc(4 * strlen(src) + 100);
1471     strcpy(cmd, "ls -la '");
1472     p = cmd + strlen(cmd);
1473     for (q = src; *q; q++) {
1474         if (*q == '\'') {
1475             *p++ = '\'';
1476             *p++ = '\\';
1477             *p++ = '\'';
1478             *p++ = '\'';
1479         } else {
1480             *p++ = *q;
1481         }
1482     }
1483     *p++ = '\'';
1484     *p = '\0';
1485
1486     do_cmd(host, user, cmd);
1487     sfree(cmd);
1488
1489     while (ssh_scp_recv(&c, 1) > 0)
1490         tell_char(stdout, c);
1491 }
1492
1493 /*
1494  *  Initialize the Win$ock driver.
1495  */
1496 static void init_winsock(void)
1497 {
1498     WORD winsock_ver;
1499     WSADATA wsadata;
1500
1501     winsock_ver = MAKEWORD(1, 1);
1502     if (WSAStartup(winsock_ver, &wsadata))
1503         bump("Unable to initialise WinSock");
1504     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
1505         bump("WinSock version is incompatible with 1.1");
1506 }
1507
1508 /*
1509  *  Short description of parameters.
1510  */
1511 static void usage(void)
1512 {
1513     printf("PuTTY Secure Copy client\n");
1514     printf("%s\n", ver);
1515     printf("Usage: pscp [options] [user@]host:source target\n");
1516     printf
1517         ("       pscp [options] source [source...] [user@]host:target\n");
1518     printf("       pscp [options] -ls user@host:filespec\n");
1519     printf("Options:\n");
1520     printf("  -p        preserve file attributes\n");
1521     printf("  -q        quiet, don't show statistics\n");
1522     printf("  -r        copy directories recursively\n");
1523     printf("  -v        show verbose messages\n");
1524     printf("  -P port   connect to specified port\n");
1525     printf("  -pw passw login with specified password\n");
1526 #if 0
1527     /*
1528      * -gui is an internal option, used by GUI front ends to get
1529      * pscp to pass progress reports back to them. It's not an
1530      * ordinary user-accessible option, so it shouldn't be part of
1531      * the command-line help. The only people who need to know
1532      * about it are programmers, and they can read the source.
1533      */
1534     printf
1535         ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");
1536 #endif
1537     exit(1);
1538 }
1539
1540 /*
1541  *  Main program (no, really?)
1542  */
1543 int main(int argc, char *argv[])
1544 {
1545     int i;
1546
1547     default_protocol = PROT_TELNET;
1548
1549     flags = FLAG_STDERR;
1550     ssh_get_line = &get_line;
1551     init_winsock();
1552     sk_init();
1553
1554     for (i = 1; i < argc; i++) {
1555         if (argv[i][0] != '-')
1556             break;
1557         if (strcmp(argv[i], "-v") == 0)
1558             verbose = 1, flags |= FLAG_VERBOSE;
1559         else if (strcmp(argv[i], "-r") == 0)
1560             recursive = 1;
1561         else if (strcmp(argv[i], "-p") == 0)
1562             preserve = 1;
1563         else if (strcmp(argv[i], "-q") == 0)
1564             statistics = 0;
1565         else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
1566             usage();
1567         else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
1568             portnumber = atoi(argv[++i]);
1569         else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
1570             password = argv[++i];
1571         else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
1572             gui_hwnd = argv[++i];
1573             gui_mode = 1;
1574         } else if (strcmp(argv[i], "-ls") == 0)
1575             list = 1;
1576         else if (strcmp(argv[i], "--") == 0) {
1577             i++;
1578             break;
1579         } else
1580             usage();
1581     }
1582     argc -= i;
1583     argv += i;
1584     back = NULL;
1585
1586     if (list) {
1587         if (argc != 1)
1588             usage();
1589         get_dir_list(argc, argv);
1590
1591     } else {
1592
1593         if (argc < 2)
1594             usage();
1595         if (argc > 2)
1596             targetshouldbedirectory = 1;
1597
1598         if (colon(argv[argc - 1]) != NULL)
1599             toremote(argc, argv);
1600         else
1601             tolocal(argc, argv);
1602     }
1603
1604     if (back != NULL && back->socket() != NULL) {
1605         char ch;
1606         back->special(TS_EOF);
1607         ssh_scp_recv(&ch, 1);
1608     }
1609     WSACleanup();
1610     random_save_seed();
1611
1612     /* GUI Adaptation - August 2000 */
1613     if (gui_mode) {
1614         unsigned int msg_id = WM_RET_ERR_CNT;
1615         if (list)
1616             msg_id = WM_LS_RET_ERR_CNT;
1617         while (!PostMessage
1618                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
1619                 0 /*lParam */ ))SleepEx(1000, TRUE);
1620     }
1621     return (errs == 0 ? 0 : 1);
1622 }
1623
1624 /* end */