2 * scp.c - Scp (Secure Copy) client for PuTTY.
3 * Joris van Rantwijk, Simon Tatham
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.
8 * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000
24 /* GUI Adaptation - Sept 2000 */
28 #define PUTTY_DO_GLOBALS
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))
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 )
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;
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;
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);
78 void logevent(char *string)
82 void ldisc_send(char *buf, int len)
85 * This is only here because of the calls to ldisc_send(NULL,
86 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
87 * as an ldisc. So if we get called with any real data, I want
93 void verify_ssh_host_key(char *host, int port, char *keytype,
94 char *keystr, char *fingerprint)
100 static const char absentmsg[] =
101 "The server's host key is not cached in the registry. You\n"
102 "have no guarantee that the server is the computer you\n"
104 "The server's key fingerprint is:\n"
106 "If you trust this host, enter \"y\" to add the key to\n"
107 "PuTTY's cache and carry on connecting.\n"
108 "If you want to carry on connecting just once, without\n"
109 "adding the key to the cache, enter \"n\".\n"
110 "If you do not trust this host, press Return to abandon the\n"
112 "Store key in cache? (y/n) ";
114 static const char wrongmsg[] =
115 "WARNING - POTENTIAL SECURITY BREACH!\n"
116 "The server's host key does not match the one PuTTY has\n"
117 "cached in the registry. This means that either the\n"
118 "server administrator has changed the host key, or you\n"
119 "have actually connected to another computer pretending\n"
120 "to be the server.\n"
121 "The new key fingerprint is:\n"
123 "If you were expecting this change and trust the new key,\n"
124 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
125 "If you want to carry on connecting but without updating\n"
126 "the cache, enter \"n\".\n"
127 "If you want to abandon the connection completely, press\n"
128 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
130 "Update cached key? (y/n, Return cancels connection) ";
132 static const char abandoned[] = "Connection abandoned.\n";
137 * Verify the key against the registry.
139 ret = verify_host_key(host, port, keytype, keystr);
141 if (ret == 0) /* success - key matched OK */
144 if (ret == 2) { /* key was different */
145 fprintf(stderr, wrongmsg, fingerprint);
148 if (ret == 1) { /* key was absent */
149 fprintf(stderr, absentmsg, fingerprint);
153 hin = GetStdHandle(STD_INPUT_HANDLE);
154 GetConsoleMode(hin, &savemode);
155 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
156 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
157 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
158 SetConsoleMode(hin, savemode);
160 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
161 if (line[0] == 'y' || line[0] == 'Y')
162 store_host_key(host, port, keytype, keystr);
164 fprintf(stderr, abandoned);
169 /* GUI Adaptation - Sept 2000 */
170 static void send_msg(HWND h, UINT message, WPARAM wParam)
172 while (!PostMessage(h, message, wParam, 0))
176 static void tell_char(FILE * stream, char c)
181 unsigned int msg_id = WM_STD_OUT_CHAR;
182 if (stream == stderr)
183 msg_id = WM_STD_ERR_CHAR;
184 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
188 static void tell_str(FILE * stream, char *str)
192 for (i = 0; i < strlen(str); ++i)
193 tell_char(stream, str[i]);
196 static void tell_user(FILE * stream, char *fmt, ...)
198 char str[0x100]; /* Make the size big enough */
201 vsprintf(str, fmt, ap);
204 tell_str(stream, str);
207 static void gui_update_stats(char *name, unsigned long size,
208 int percentage, unsigned long elapsed)
212 if (strcmp(name, statname) != 0) {
213 for (i = 0; i < strlen(name); ++i)
214 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
216 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
217 strcpy(statname, name);
219 if (statsize != size) {
220 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
223 if (statelapsed != elapsed) {
224 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
226 statelapsed = elapsed;
228 if (statperct != percentage) {
229 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
230 (WPARAM) percentage);
231 statperct = percentage;
236 * Print an error message and perform a fatal exit.
238 void fatalbox(char *fmt, ...)
240 char str[0x100]; /* Make the size big enough */
243 strcpy(str, "Fatal:");
244 vsprintf(str + strlen(str), fmt, ap);
247 tell_str(stderr, str);
251 unsigned int msg_id = WM_RET_ERR_CNT;
253 msg_id = WM_LS_RET_ERR_CNT;
255 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
256 0 /*lParam */ ))SleepEx(1000, TRUE);
261 void connection_fatal(char *fmt, ...)
263 char str[0x100]; /* Make the size big enough */
266 strcpy(str, "Fatal:");
267 vsprintf(str + strlen(str), fmt, ap);
270 tell_str(stderr, str);
274 unsigned int msg_id = WM_RET_ERR_CNT;
276 msg_id = WM_LS_RET_ERR_CNT;
278 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
279 0 /*lParam */ ))SleepEx(1000, TRUE);
286 * Be told what socket we're supposed to be using.
288 static SOCKET scp_ssh_socket;
289 char *do_select(SOCKET skt, int startup)
292 scp_ssh_socket = skt;
294 scp_ssh_socket = INVALID_SOCKET;
297 extern int select_result(WPARAM, LPARAM);
300 * Receive a block of data from the SSH link. Block until all data
303 * To do this, we repeatedly call the SSH protocol module, with our
304 * own trap in from_backend() to catch the data that comes back. We
305 * do this until we have enough data.
308 static unsigned char *outptr; /* where to put the data */
309 static unsigned outlen; /* how much data required */
310 static unsigned char *pending = NULL; /* any spare data */
311 static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
312 void from_backend(int is_stderr, char *data, int datalen)
314 unsigned char *p = (unsigned char *) data;
315 unsigned len = (unsigned) datalen;
318 * stderr data is just spouted to local stderr and otherwise
322 fwrite(data, 1, len, stderr);
329 * If this is before the real session begins, just return.
335 unsigned used = outlen;
338 memcpy(outptr, p, used);
346 if (pendsize < pendlen + len) {
347 pendsize = pendlen + len + 4096;
348 pending = (pending ? srealloc(pending, pendsize) :
351 fatalbox("Out of memory");
353 memcpy(pending + pendlen, p, len);
357 static int ssh_scp_recv(unsigned char *buf, int len)
363 * See if the pending-input block contains some of what we
367 unsigned pendused = pendlen;
368 if (pendused > outlen)
370 memcpy(outptr, pending, pendused);
371 memmove(pending, pending + pendused, pendlen - pendused);
388 FD_SET(scp_ssh_socket, &readfds);
389 if (select(1, &readfds, NULL, NULL, NULL) < 0)
391 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
398 * Loop through the ssh connection and authentication process.
400 static void ssh_scp_init(void)
402 if (scp_ssh_socket == INVALID_SOCKET)
404 while (!back->sendok()) {
407 FD_SET(scp_ssh_socket, &readfds);
408 if (select(1, &readfds, NULL, NULL, NULL) < 0)
410 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
415 * Print an error message and exit after closing the SSH link.
417 static void bump(char *fmt, ...)
419 char str[0x100]; /* Make the size big enough */
422 strcpy(str, "Fatal:");
423 vsprintf(str + strlen(str), fmt, ap);
426 tell_str(stderr, str);
429 if (back != NULL && back->socket() != NULL) {
431 back->special(TS_EOF);
432 ssh_scp_recv(&ch, 1);
436 unsigned int msg_id = WM_RET_ERR_CNT;
438 msg_id = WM_LS_RET_ERR_CNT;
440 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
441 0 /*lParam */ ))SleepEx(1000, TRUE);
447 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
450 DWORD savemode, newmode, i;
452 if (is_pw && password) {
453 static int tried_once = 0;
458 strncpy(str, password, maxlen);
459 str[maxlen - 1] = '\0';
465 /* GUI Adaptation - Sept 2000 */
470 hin = GetStdHandle(STD_INPUT_HANDLE);
471 hout = GetStdHandle(STD_OUTPUT_HANDLE);
472 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
473 bump("Cannot get standard input/output handles");
475 GetConsoleMode(hin, &savemode);
476 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
478 newmode &= ~ENABLE_ECHO_INPUT;
480 newmode |= ENABLE_ECHO_INPUT;
481 SetConsoleMode(hin, newmode);
483 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
484 ReadFile(hin, str, maxlen - 1, &i, NULL);
486 SetConsoleMode(hin, savemode);
488 if ((int) i > maxlen)
495 WriteFile(hout, "\r\n", 2, &i, NULL);
502 * Open an SSH connection to user@host and execute cmd.
504 static void do_cmd(char *host, char *user, char *cmd)
506 char *err, *realhost;
509 if (host == NULL || host[0] == '\0')
510 bump("Empty host name");
512 /* Try to load settings for this host */
513 do_defaults(host, &cfg);
514 if (cfg.host[0] == '\0') {
515 /* No settings for this host; use defaults */
516 do_defaults(NULL, &cfg);
517 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
518 cfg.host[sizeof(cfg.host) - 1] = '\0';
523 if (user != NULL && user[0] != '\0') {
524 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
525 cfg.username[sizeof(cfg.username) - 1] = '\0';
526 } else if (cfg.username[0] == '\0') {
528 if (GetUserName(user, &namelen) == FALSE)
529 bump("Empty user name");
530 user = smalloc(namelen * sizeof(char));
531 GetUserName(user, &namelen);
533 tell_user(stderr, "Guessing user name: %s", user);
534 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
535 cfg.username[sizeof(cfg.username) - 1] = '\0';
539 if (cfg.protocol != PROT_SSH)
543 cfg.port = portnumber;
545 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
546 cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
551 err = back->init(cfg.host, cfg.port, &realhost);
553 bump("ssh_init: %s", err);
555 if (verbose && realhost != NULL)
556 tell_user(stderr, "Connected to %s\n", realhost);
561 * Update statistic information about current file.
563 static void print_stats(char *name, unsigned long size, unsigned long done,
564 time_t start, time_t now)
572 /* GUI Adaptation - Sept 2000 */
574 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
575 (unsigned long) difftime(now, start));
578 ratebs = (float) done / (now - start);
580 ratebs = (float) done;
585 eta = (unsigned long) ((size - done) / ratebs);
586 sprintf(etastr, "%02ld:%02ld:%02ld",
587 eta / 3600, (eta % 3600) / 60, eta % 60);
589 pct = (int) (100.0 * (float) done / size);
591 len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
592 name, done / 1024, ratebs / 1024.0, etastr, pct);
593 if (len < prev_stats_len)
594 printf("%*s", prev_stats_len - len, "");
595 prev_stats_len = len;
603 * Find a colon in str and return a pointer to the colon.
604 * This is used to separate hostname from filename.
606 static char *colon(char *str)
608 /* We ignore a leading colon, since the hostname cannot be
609 empty. We also ignore a colon as second character because
610 of filenames like f:myfile.txt. */
611 if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
613 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
622 * Wait for a response from the other side.
623 * Return 0 if ok, -1 if error.
625 static int response(void)
627 char ch, resp, rbuf[2048];
630 if (ssh_scp_recv(&resp, 1) <= 0)
631 bump("Lost connection");
641 case 2: /* fatal error */
643 if (ssh_scp_recv(&ch, 1) <= 0)
644 bump("Protocol error: Lost connection");
646 } while (p < sizeof(rbuf) && ch != '\n');
649 tell_user(stderr, "%s\n", rbuf);
658 * Send an error message to the other side and to the screen.
659 * Increment error counter.
661 static void run_err(const char *fmt, ...)
667 strcpy(str, "scp: ");
668 vsprintf(str + strlen(str), fmt, ap);
670 back->send("\001", 1); /* scp protocol error prefix */
671 back->send(str, strlen(str));
672 tell_user(stderr, "%s", str);
677 * Execute the source part of the SCP protocol.
679 static void source(char *src)
687 unsigned long stat_bytes;
688 time_t stat_starttime, stat_lasttime;
690 attr = GetFileAttributes(src);
691 if (attr == (DWORD) - 1) {
692 run_err("%s: No such file or directory", src);
696 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
699 * Avoid . and .. directories.
702 p = strrchr(src, '/');
704 p = strrchr(src, '\\');
709 if (!strcmp(p, ".") || !strcmp(p, ".."))
710 /* skip . and .. */ ;
714 run_err("%s: not a regular file", src);
719 if ((last = strrchr(src, '/')) == NULL)
723 if (strrchr(last, '\\') != NULL)
724 last = strrchr(last, '\\') + 1;
725 if (last == src && strchr(src, ':') != NULL)
726 last = strchr(src, ':') + 1;
728 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
729 OPEN_EXISTING, 0, 0);
730 if (f == INVALID_HANDLE_VALUE) {
731 run_err("%s: Cannot open file", src);
736 FILETIME actime, wrtime;
737 unsigned long mtime, atime;
738 GetFileTime(f, NULL, &actime, &wrtime);
739 TIME_WIN_TO_POSIX(actime, atime);
740 TIME_WIN_TO_POSIX(wrtime, mtime);
741 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
742 back->send(buf, strlen(buf));
747 size = GetFileSize(f, NULL);
748 sprintf(buf, "C0644 %lu %s\n", size, last);
750 tell_user(stderr, "Sending file modes: %s", buf);
751 back->send(buf, strlen(buf));
756 stat_starttime = time(NULL);
759 for (i = 0; i < size; i += 4096) {
764 if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
767 bump("%s: Read error", src);
769 back->send(transbuf, k);
772 if (time(NULL) != stat_lasttime || i + k == size) {
773 stat_lasttime = time(NULL);
774 print_stats(last, size, stat_bytes,
775 stat_starttime, stat_lasttime);
786 * Recursively send the contents of a directory.
788 static void rsource(char *src)
793 WIN32_FIND_DATA fdat;
796 if ((last = strrchr(src, '/')) == NULL)
800 if (strrchr(last, '\\') != NULL)
801 last = strrchr(last, '\\') + 1;
802 if (last == src && strchr(src, ':') != NULL)
803 last = strchr(src, ':') + 1;
805 /* maybe send filetime */
807 sprintf(buf, "D0755 0 %s\n", last);
809 tell_user(stderr, "Entering directory: %s", buf);
810 back->send(buf, strlen(buf));
814 sprintf(buf, "%s/*", src);
815 dir = FindFirstFile(buf, &fdat);
816 ok = (dir != INVALID_HANDLE_VALUE);
818 if (strcmp(fdat.cFileName, ".") == 0 ||
819 strcmp(fdat.cFileName, "..") == 0) {
820 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
821 run_err("%s/%s: Name too long", src, fdat.cFileName);
823 sprintf(buf, "%s/%s", src, fdat.cFileName);
826 ok = FindNextFile(dir, &fdat);
831 back->send(buf, strlen(buf));
836 * Execute the sink part of the SCP protocol.
838 static void sink(char *targ, char *src)
848 unsigned long mtime, atime;
850 unsigned long size, i;
852 unsigned long stat_bytes;
853 time_t stat_starttime, stat_lasttime;
856 attr = GetFileAttributes(targ);
857 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
860 if (targetshouldbedirectory && !targisdir)
861 bump("%s: Not a directory", targ);
867 if (ssh_scp_recv(&ch, 1) <= 0)
870 bump("Protocol error: Unexpected newline");
874 if (ssh_scp_recv(&ch, 1) <= 0)
875 bump("Lost connection");
877 } while (i < sizeof(buf) && ch != '\n');
880 case '\01': /* error */
881 tell_user(stderr, "%s\n", buf + 1);
884 case '\02': /* fatal error */
890 if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
895 bump("Protocol error: Illegal time format");
900 bump("Protocol error: Expected control record");
903 if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
904 bump("Protocol error: Illegal file descriptor format");
905 /* Security fix: ensure the file ends up where we asked for it. */
912 p = namebuf + strlen(namebuf);
913 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
918 strcpy(namebuf, targ);
920 attr = GetFileAttributes(namebuf);
921 exists = (attr != (DWORD) - 1);
924 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
925 run_err("%s: Not a directory", namebuf);
929 if (!CreateDirectory(namebuf, NULL)) {
930 run_err("%s: Cannot create directory", namebuf);
935 /* can we set the timestamp for directories ? */
939 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
940 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
941 if (f == INVALID_HANDLE_VALUE) {
942 run_err("%s: Cannot create file", namebuf);
949 stat_starttime = time(NULL);
951 if ((stat_name = strrchr(namebuf, '/')) == NULL)
955 if (strrchr(stat_name, '\\') != NULL)
956 stat_name = strrchr(stat_name, '\\') + 1;
958 for (i = 0; i < size; i += 4096) {
963 if (ssh_scp_recv(transbuf, k) == 0)
964 bump("Lost connection");
967 if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
970 printf("\r%-25.25s | %50s\n",
972 "Write error.. waiting for end of file");
977 if (time(NULL) > stat_lasttime || i + k == size) {
978 stat_lasttime = time(NULL);
979 print_stats(stat_name, size, stat_bytes,
980 stat_starttime, stat_lasttime);
987 FILETIME actime, wrtime;
988 TIME_POSIX_TO_WIN(atime, actime);
989 TIME_POSIX_TO_WIN(mtime, wrtime);
990 SetFileTime(f, NULL, &actime, &wrtime);
995 run_err("%s: Write error", namebuf);
1003 * We will copy local files to a remote server.
1005 static void toremote(int argc, char *argv[])
1007 char *src, *targ, *host, *user;
1011 targ = argv[argc - 1];
1013 /* Separate host from filename */
1017 bump("targ == NULL in toremote()");
1021 /* Substitute "." for emtpy target */
1023 /* Separate host and username */
1025 host = strrchr(host, '@');
1036 /* Find out if the source filespec covers multiple files
1037 if so, we should set the targetshouldbedirectory flag */
1039 WIN32_FIND_DATA fdat;
1040 if (colon(argv[0]) != NULL)
1041 bump("%s: Remote to remote not supported", argv[0]);
1042 fh = FindFirstFile(argv[0], &fdat);
1043 if (fh == INVALID_HANDLE_VALUE)
1044 bump("%s: No such file or directory\n", argv[0]);
1045 if (FindNextFile(fh, &fdat))
1046 targetshouldbedirectory = 1;
1050 cmd = smalloc(strlen(targ) + 100);
1051 sprintf(cmd, "scp%s%s%s%s -t %s",
1052 verbose ? " -v" : "",
1053 recursive ? " -r" : "",
1054 preserve ? " -p" : "",
1055 targetshouldbedirectory ? " -d" : "", targ);
1056 do_cmd(host, user, cmd);
1061 for (i = 0; i < argc - 1; i++) {
1063 WIN32_FIND_DATA fdat;
1065 if (colon(src) != NULL) {
1066 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1070 dir = FindFirstFile(src, &fdat);
1071 if (dir == INVALID_HANDLE_VALUE) {
1072 run_err("%s: No such file or directory", src);
1079 * Ensure that . and .. are never matched by wildcards,
1080 * but only by deliberate action.
1082 if (!strcmp(fdat.cFileName, ".") ||
1083 !strcmp(fdat.cFileName, "..")) {
1085 * Find*File has returned a special dir. We require
1086 * that _either_ `src' ends in a backslash followed
1087 * by that string, _or_ `src' is precisely that
1090 int len = strlen(src), dlen = strlen(fdat.cFileName);
1091 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1093 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1094 !strcmp(src + len - dlen, fdat.cFileName)) {
1097 continue; /* ignore this one */
1099 if (strlen(src) + strlen(fdat.cFileName) >= sizeof(namebuf)) {
1100 tell_user(stderr, "%s: Name too long", src);
1103 strcpy(namebuf, src);
1104 if ((last = strrchr(namebuf, '/')) == NULL)
1108 if (strrchr(last, '\\') != NULL)
1109 last = strrchr(last, '\\') + 1;
1110 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1111 last = strchr(namebuf, ':') + 1;
1112 strcpy(last, fdat.cFileName);
1114 } while (FindNextFile(dir, &fdat));
1120 * We will copy files from a remote server to the local machine.
1122 static void tolocal(int argc, char *argv[])
1124 char *src, *targ, *host, *user;
1128 bump("More than one remote source not supported");
1133 /* Separate host from filename */
1137 bump("Local to local copy not supported");
1141 /* Substitute "." for empty filename */
1143 /* Separate username and hostname */
1145 host = strrchr(host, '@');
1155 cmd = smalloc(strlen(src) + 100);
1156 sprintf(cmd, "scp%s%s%s%s -f %s",
1157 verbose ? " -v" : "",
1158 recursive ? " -r" : "",
1159 preserve ? " -p" : "",
1160 targetshouldbedirectory ? " -d" : "", src);
1161 do_cmd(host, user, cmd);
1168 * We will issue a list command to get a remote directory.
1170 static void get_dir_list(int argc, char *argv[])
1172 char *src, *host, *user;
1178 /* Separate host from filename */
1182 bump("Local to local copy not supported");
1186 /* Substitute "." for empty filename */
1188 /* Separate username and hostname */
1190 host = strrchr(host, '@');
1200 cmd = smalloc(4 * strlen(src) + 100);
1201 strcpy(cmd, "ls -la '");
1202 p = cmd + strlen(cmd);
1203 for (q = src; *q; q++) {
1216 do_cmd(host, user, cmd);
1219 while (ssh_scp_recv(&c, 1) > 0)
1220 tell_char(stdout, c);
1224 * Initialize the Win$ock driver.
1226 static void init_winsock(void)
1231 winsock_ver = MAKEWORD(1, 1);
1232 if (WSAStartup(winsock_ver, &wsadata))
1233 bump("Unable to initialise WinSock");
1234 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
1235 bump("WinSock version is incompatible with 1.1");
1239 * Short description of parameters.
1241 static void usage(void)
1243 printf("PuTTY Secure Copy client\n");
1244 printf("%s\n", ver);
1245 printf("Usage: pscp [options] [user@]host:source target\n");
1247 (" pscp [options] source [source...] [user@]host:target\n");
1248 printf(" pscp [options] -ls user@host:filespec\n");
1249 printf("Options:\n");
1250 printf(" -p preserve file attributes\n");
1251 printf(" -q quiet, don't show statistics\n");
1252 printf(" -r copy directories recursively\n");
1253 printf(" -v show verbose messages\n");
1254 printf(" -P port connect to specified port\n");
1255 printf(" -pw passw login with specified password\n");
1258 * -gui is an internal option, used by GUI front ends to get
1259 * pscp to pass progress reports back to them. It's not an
1260 * ordinary user-accessible option, so it shouldn't be part of
1261 * the command-line help. The only people who need to know
1262 * about it are programmers, and they can read the source.
1265 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1271 * Main program (no, really?)
1273 int main(int argc, char *argv[])
1277 default_protocol = PROT_TELNET;
1279 flags = FLAG_STDERR;
1280 ssh_get_line = &get_line;
1284 for (i = 1; i < argc; i++) {
1285 if (argv[i][0] != '-')
1287 if (strcmp(argv[i], "-v") == 0)
1288 verbose = 1, flags |= FLAG_VERBOSE;
1289 else if (strcmp(argv[i], "-r") == 0)
1291 else if (strcmp(argv[i], "-p") == 0)
1293 else if (strcmp(argv[i], "-q") == 0)
1295 else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
1297 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
1298 portnumber = atoi(argv[++i]);
1299 else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
1300 password = argv[++i];
1301 else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
1302 gui_hwnd = argv[++i];
1304 } else if (strcmp(argv[i], "-ls") == 0)
1306 else if (strcmp(argv[i], "--") == 0) {
1319 get_dir_list(argc, argv);
1326 targetshouldbedirectory = 1;
1328 if (colon(argv[argc - 1]) != NULL)
1329 toremote(argc, argv);
1331 tolocal(argc, argv);
1334 if (back != NULL && back->socket() != NULL) {
1336 back->special(TS_EOF);
1337 ssh_scp_recv(&ch, 1);
1342 /* GUI Adaptation - August 2000 */
1344 unsigned int msg_id = WM_RET_ERR_CNT;
1346 msg_id = WM_LS_RET_ERR_CNT;
1348 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
1349 0 /*lParam */ ))SleepEx(1000, TRUE);
1351 return (errs == 0 ? 0 : 1);