]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - scp.c
Oops. ^X^S comes _before_ `cvs commit'. Two more diagnostics gone :-)
[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             char *striptarget, *stripsrc;
1129
1130             striptarget = stripslashes(act.name);
1131             if (striptarget != act.name) {
1132                 tell_user(stderr, "warning: remote host sent a compound"
1133                           " pathname - possibly malicious! (ignored)");
1134             }
1135
1136             /*
1137              * Also check to see if the target filename is '.' or
1138              * '..', or indeed '...' and so on because Windows
1139              * appears to interpret those like '..'.
1140              */
1141             if (striptarget[strspn(striptarget, ".")] == '\0') {
1142                 bump("security violation: remote host attempted to write to"
1143                      " a '.' or '..' path!");
1144             }
1145
1146             if (src) {
1147                 stripsrc = stripslashes(src);
1148                 if (strcmp(striptarget, stripsrc)) {
1149                     tell_user(stderr, "warning: remote host attempted to"
1150                               " write to a different filename: disallowing");
1151                 }
1152                 /* Override the name the server provided with our own. */
1153                 striptarget = stripsrc;
1154             }
1155
1156             if (targ[0] != '\0')
1157                 destfname = dupcat(targ, "\\", striptarget, NULL);
1158             else
1159                 destfname = dupstr(striptarget);
1160         } else {
1161             /*
1162              * In this branch of the if, the target area is a
1163              * single file with an explicitly specified name in any
1164              * case, so there's no danger.
1165              */
1166             destfname = dupstr(targ);
1167         }
1168         attr = GetFileAttributes(destfname);
1169         exists = (attr != (DWORD) - 1);
1170
1171         if (act.action == SCP_SINK_DIR) {
1172             if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
1173                 run_err("%s: Not a directory", destfname);
1174                 continue;
1175             }
1176             if (!exists) {
1177                 if (!CreateDirectory(destfname, NULL)) {
1178                     run_err("%s: Cannot create directory", destfname);
1179                     continue;
1180                 }
1181             }
1182             sink(destfname, NULL);
1183             /* can we set the timestamp for directories ? */
1184             continue;
1185         }
1186
1187         f = CreateFile(destfname, GENERIC_WRITE, 0, NULL,
1188                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
1189         if (f == INVALID_HANDLE_VALUE) {
1190             run_err("%s: Cannot create file", destfname);
1191             continue;
1192         }
1193
1194         if (scp_accept_filexfer())
1195             return;
1196
1197         stat_bytes = 0;
1198         stat_starttime = time(NULL);
1199         stat_lasttime = 0;
1200         stat_name = stripslashes(destfname);
1201
1202         received = 0;
1203         while (received < act.size) {
1204             char transbuf[4096];
1205             DWORD blksize, read, written;
1206             blksize = 4096;
1207             if (blksize > act.size - received)
1208                 blksize = act.size - received;
1209             read = scp_recv_filedata(transbuf, blksize);
1210             if (read <= 0)
1211                 bump("Lost connection");
1212             if (wrerror)
1213                 continue;
1214             if (!WriteFile(f, transbuf, read, &written, NULL) ||
1215                 written != read) {
1216                 wrerror = 1;
1217                 /* FIXME: in sftp we can actually abort the transfer */
1218                 if (statistics)
1219                     printf("\r%-25.25s | %50s\n",
1220                            stat_name,
1221                            "Write error.. waiting for end of file");
1222                 continue;
1223             }
1224             if (statistics) {
1225                 stat_bytes += read;
1226                 if (time(NULL) > stat_lasttime ||
1227                     received + read == act.size) {
1228                     stat_lasttime = time(NULL);
1229                     print_stats(stat_name, act.size, stat_bytes,
1230                                 stat_starttime, stat_lasttime);
1231                 }
1232             }
1233             received += read;
1234         }
1235         if (act.settime) {
1236             FILETIME actime, wrtime;
1237             TIME_POSIX_TO_WIN(act.atime, actime);
1238             TIME_POSIX_TO_WIN(act.mtime, wrtime);
1239             SetFileTime(f, NULL, &actime, &wrtime);
1240         }
1241
1242         CloseHandle(f);
1243         if (wrerror) {
1244             run_err("%s: Write error", destfname);
1245             continue;
1246         }
1247         (void) scp_finish_filerecv();
1248         sfree(destfname);
1249         sfree(act.name);
1250     }
1251 }
1252
1253 /*
1254  * We will copy local files to a remote server.
1255  */
1256 static void toremote(int argc, char *argv[])
1257 {
1258     char *src, *targ, *host, *user;
1259     char *cmd;
1260     int i;
1261
1262     targ = argv[argc - 1];
1263
1264     /* Separate host from filename */
1265     host = targ;
1266     targ = colon(targ);
1267     if (targ == NULL)
1268         bump("targ == NULL in toremote()");
1269     *targ++ = '\0';
1270     if (*targ == '\0')
1271         targ = ".";
1272     /* Substitute "." for emtpy target */
1273
1274     /* Separate host and username */
1275     user = host;
1276     host = strrchr(host, '@');
1277     if (host == NULL) {
1278         host = user;
1279         user = NULL;
1280     } else {
1281         *host++ = '\0';
1282         if (*user == '\0')
1283             user = NULL;
1284     }
1285
1286     if (argc == 2) {
1287         /* Find out if the source filespec covers multiple files
1288            if so, we should set the targetshouldbedirectory flag */
1289         HANDLE fh;
1290         WIN32_FIND_DATA fdat;
1291         if (colon(argv[0]) != NULL)
1292             bump("%s: Remote to remote not supported", argv[0]);
1293         fh = FindFirstFile(argv[0], &fdat);
1294         if (fh == INVALID_HANDLE_VALUE)
1295             bump("%s: No such file or directory\n", argv[0]);
1296         if (FindNextFile(fh, &fdat))
1297             targetshouldbedirectory = 1;
1298         FindClose(fh);
1299     }
1300
1301     cmd = smalloc(strlen(targ) + 100);
1302     sprintf(cmd, "scp%s%s%s%s -t %s",
1303             verbose ? " -v" : "",
1304             recursive ? " -r" : "",
1305             preserve ? " -p" : "",
1306             targetshouldbedirectory ? " -d" : "", targ);
1307     do_cmd(host, user, cmd);
1308     sfree(cmd);
1309
1310     (void) response();
1311
1312     for (i = 0; i < argc - 1; i++) {
1313         char *srcpath, *last;
1314         HANDLE dir;
1315         WIN32_FIND_DATA fdat;
1316         src = argv[i];
1317         if (colon(src) != NULL) {
1318             tell_user(stderr, "%s: Remote to remote not supported\n", src);
1319             errs++;
1320             continue;
1321         }
1322
1323         /*
1324          * Trim off the last pathname component of `src', to
1325          * provide the base pathname which will be prepended to
1326          * filenames returned from Find{First,Next}File.
1327          */
1328         srcpath = dupstr(src);
1329         last = stripslashes(srcpath);
1330         if (last == srcpath) {
1331             last = strchr(srcpath, ':');
1332             if (last)
1333                 last++;
1334             else
1335                 last = srcpath;
1336         }
1337         *last = '\0';
1338
1339         dir = FindFirstFile(src, &fdat);
1340         if (dir == INVALID_HANDLE_VALUE) {
1341             run_err("%s: No such file or directory", src);
1342             continue;
1343         }
1344         do {
1345             char *last;
1346             char *filename;
1347             /*
1348              * Ensure that . and .. are never matched by wildcards,
1349              * but only by deliberate action.
1350              */
1351             if (!strcmp(fdat.cFileName, ".") ||
1352                 !strcmp(fdat.cFileName, "..")) {
1353                 /*
1354                  * Find*File has returned a special dir. We require
1355                  * that _either_ `src' ends in a backslash followed
1356                  * by that string, _or_ `src' is precisely that
1357                  * string.
1358                  */
1359                 int len = strlen(src), dlen = strlen(fdat.cFileName);
1360                 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1361                     /* ok */ ;
1362                 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1363                            !strcmp(src + len - dlen, fdat.cFileName)) {
1364                     /* ok */ ;
1365                 } else
1366                     continue;          /* ignore this one */
1367             }
1368             filename = dupcat(srcpath, fdat.cFileName, NULL);
1369             source(filename);
1370             sfree(filename);
1371         } while (FindNextFile(dir, &fdat));
1372         FindClose(dir);
1373         sfree(srcpath);
1374     }
1375 }
1376
1377 /*
1378  *  We will copy files from a remote server to the local machine.
1379  */
1380 static void tolocal(int argc, char *argv[])
1381 {
1382     char *src, *targ, *host, *user;
1383     char *cmd;
1384
1385     if (argc != 2)
1386         bump("More than one remote source not supported");
1387
1388     src = argv[0];
1389     targ = argv[1];
1390
1391     /* Separate host from filename */
1392     host = src;
1393     src = colon(src);
1394     if (src == NULL)
1395         bump("Local to local copy not supported");
1396     *src++ = '\0';
1397     if (*src == '\0')
1398         src = ".";
1399     /* Substitute "." for empty filename */
1400
1401     /* Separate username and hostname */
1402     user = host;
1403     host = strrchr(host, '@');
1404     if (host == NULL) {
1405         host = user;
1406         user = NULL;
1407     } else {
1408         *host++ = '\0';
1409         if (*user == '\0')
1410             user = NULL;
1411     }
1412
1413     cmd = smalloc(strlen(src) + 100);
1414     sprintf(cmd, "scp%s%s%s%s -f %s",
1415             verbose ? " -v" : "",
1416             recursive ? " -r" : "",
1417             preserve ? " -p" : "",
1418             targetshouldbedirectory ? " -d" : "", src);
1419     do_cmd(host, user, cmd);
1420     sfree(cmd);
1421
1422     sink(targ, src);
1423 }
1424
1425 /*
1426  *  We will issue a list command to get a remote directory.
1427  */
1428 static void get_dir_list(int argc, char *argv[])
1429 {
1430     char *src, *host, *user;
1431     char *cmd, *p, *q;
1432     char c;
1433
1434     src = argv[0];
1435
1436     /* Separate host from filename */
1437     host = src;
1438     src = colon(src);
1439     if (src == NULL)
1440         bump("Local to local copy not supported");
1441     *src++ = '\0';
1442     if (*src == '\0')
1443         src = ".";
1444     /* Substitute "." for empty filename */
1445
1446     /* Separate username and hostname */
1447     user = host;
1448     host = strrchr(host, '@');
1449     if (host == NULL) {
1450         host = user;
1451         user = NULL;
1452     } else {
1453         *host++ = '\0';
1454         if (*user == '\0')
1455             user = NULL;
1456     }
1457
1458     cmd = smalloc(4 * strlen(src) + 100);
1459     strcpy(cmd, "ls -la '");
1460     p = cmd + strlen(cmd);
1461     for (q = src; *q; q++) {
1462         if (*q == '\'') {
1463             *p++ = '\'';
1464             *p++ = '\\';
1465             *p++ = '\'';
1466             *p++ = '\'';
1467         } else {
1468             *p++ = *q;
1469         }
1470     }
1471     *p++ = '\'';
1472     *p = '\0';
1473
1474     do_cmd(host, user, cmd);
1475     sfree(cmd);
1476
1477     while (ssh_scp_recv(&c, 1) > 0)
1478         tell_char(stdout, c);
1479 }
1480
1481 /*
1482  *  Initialize the Win$ock driver.
1483  */
1484 static void init_winsock(void)
1485 {
1486     WORD winsock_ver;
1487     WSADATA wsadata;
1488
1489     winsock_ver = MAKEWORD(1, 1);
1490     if (WSAStartup(winsock_ver, &wsadata))
1491         bump("Unable to initialise WinSock");
1492     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
1493         bump("WinSock version is incompatible with 1.1");
1494 }
1495
1496 /*
1497  *  Short description of parameters.
1498  */
1499 static void usage(void)
1500 {
1501     printf("PuTTY Secure Copy client\n");
1502     printf("%s\n", ver);
1503     printf("Usage: pscp [options] [user@]host:source target\n");
1504     printf
1505         ("       pscp [options] source [source...] [user@]host:target\n");
1506     printf("       pscp [options] -ls user@host:filespec\n");
1507     printf("Options:\n");
1508     printf("  -p        preserve file attributes\n");
1509     printf("  -q        quiet, don't show statistics\n");
1510     printf("  -r        copy directories recursively\n");
1511     printf("  -v        show verbose messages\n");
1512     printf("  -P port   connect to specified port\n");
1513     printf("  -pw passw login with specified password\n");
1514 #if 0
1515     /*
1516      * -gui is an internal option, used by GUI front ends to get
1517      * pscp to pass progress reports back to them. It's not an
1518      * ordinary user-accessible option, so it shouldn't be part of
1519      * the command-line help. The only people who need to know
1520      * about it are programmers, and they can read the source.
1521      */
1522     printf
1523         ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");
1524 #endif
1525     exit(1);
1526 }
1527
1528 /*
1529  *  Main program (no, really?)
1530  */
1531 int main(int argc, char *argv[])
1532 {
1533     int i;
1534
1535     default_protocol = PROT_TELNET;
1536
1537     flags = FLAG_STDERR;
1538     ssh_get_line = &get_line;
1539     init_winsock();
1540     sk_init();
1541
1542     for (i = 1; i < argc; i++) {
1543         if (argv[i][0] != '-')
1544             break;
1545         if (strcmp(argv[i], "-v") == 0)
1546             verbose = 1, flags |= FLAG_VERBOSE;
1547         else if (strcmp(argv[i], "-r") == 0)
1548             recursive = 1;
1549         else if (strcmp(argv[i], "-p") == 0)
1550             preserve = 1;
1551         else if (strcmp(argv[i], "-q") == 0)
1552             statistics = 0;
1553         else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
1554             usage();
1555         else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
1556             portnumber = atoi(argv[++i]);
1557         else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
1558             password = argv[++i];
1559         else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
1560             gui_hwnd = argv[++i];
1561             gui_mode = 1;
1562         } else if (strcmp(argv[i], "-ls") == 0)
1563             list = 1;
1564         else if (strcmp(argv[i], "--") == 0) {
1565             i++;
1566             break;
1567         } else
1568             usage();
1569     }
1570     argc -= i;
1571     argv += i;
1572     back = NULL;
1573
1574     if (list) {
1575         if (argc != 1)
1576             usage();
1577         get_dir_list(argc, argv);
1578
1579     } else {
1580
1581         if (argc < 2)
1582             usage();
1583         if (argc > 2)
1584             targetshouldbedirectory = 1;
1585
1586         if (colon(argv[argc - 1]) != NULL)
1587             toremote(argc, argv);
1588         else
1589             tolocal(argc, argv);
1590     }
1591
1592     if (back != NULL && back->socket() != NULL) {
1593         char ch;
1594         back->special(TS_EOF);
1595         ssh_scp_recv(&ch, 1);
1596     }
1597     WSACleanup();
1598     random_save_seed();
1599
1600     /* GUI Adaptation - August 2000 */
1601     if (gui_mode) {
1602         unsigned int msg_id = WM_RET_ERR_CNT;
1603         if (list)
1604             msg_id = WM_LS_RET_ERR_CNT;
1605         while (!PostMessage
1606                ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
1607                 0 /*lParam */ ))SleepEx(1000, TRUE);
1608     }
1609     return (errs == 0 ? 0 : 1);
1610 }
1611
1612 /* end */