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 char *password = NULL;
58 /* GUI Adaptation - Sept 2000 */
59 #define NAME_STR_MAX 2048
60 static char statname[NAME_STR_MAX + 1];
61 static unsigned long statsize = 0;
62 static int statperct = 0;
63 static unsigned long statelapsed = 0;
64 static int gui_mode = 0;
65 static char *gui_hwnd = NULL;
67 static void source(char *src);
68 static void rsource(char *src);
69 static void sink(char *targ, char *src);
70 /* GUI Adaptation - Sept 2000 */
71 static void tell_char(FILE * stream, char c);
72 static void tell_str(FILE * stream, char *str);
73 static void tell_user(FILE * stream, char *fmt, ...);
74 static void gui_update_stats(char *name, unsigned long size,
75 int percentage, unsigned long elapsed);
77 void logevent(char *string)
81 void ldisc_send(char *buf, int len)
84 * This is only here because of the calls to ldisc_send(NULL,
85 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
86 * as an ldisc. So if we get called with any real data, I want
92 void verify_ssh_host_key(char *host, int port, char *keytype,
93 char *keystr, char *fingerprint)
97 static const char absentmsg[] =
98 "The server's host key is not cached in the registry. You\n"
99 "have no guarantee that the server is the computer you\n"
101 "The server's key fingerprint is:\n"
103 "If you trust this host, enter \"y\" to add the key to\n"
104 "PuTTY's cache and carry on connecting.\n"
105 "If you do not trust this host, enter \"n\" to abandon the\n"
106 "connection.\n" "Continue connecting? (y/n) ";
108 static const char wrongmsg[] =
109 "WARNING - POTENTIAL SECURITY BREACH!\n"
110 "The server's host key does not match the one PuTTY has\n"
111 "cached in the registry. This means that either the\n"
112 "server administrator has changed the host key, or you\n"
113 "have actually connected to another computer pretending\n"
114 "to be the server.\n"
115 "The new key fingerprint is:\n"
117 "If you were expecting this change and trust the new key,\n"
118 "enter Yes to update PuTTY's cache and continue connecting.\n"
119 "If you want to carry on connecting but without updating\n"
120 "the cache, enter No.\n"
121 "If you want to abandon the connection completely, press\n"
122 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
124 "Update cached key? (y/n, Return cancels connection) ";
126 static const char abandoned[] = "Connection abandoned.\n";
131 * Verify the key against the registry.
133 ret = verify_host_key(host, port, keytype, keystr);
135 if (ret == 0) /* success - key matched OK */
137 if (ret == 2) { /* key was different */
138 fprintf(stderr, wrongmsg, fingerprint);
140 if (fgets(line, sizeof(line), stdin) &&
141 line[0] != '\0' && line[0] != '\n') {
142 if (line[0] == 'y' || line[0] == 'Y')
143 store_host_key(host, port, keytype, keystr);
145 fprintf(stderr, abandoned);
150 if (ret == 1) { /* key was absent */
151 fprintf(stderr, absentmsg, fingerprint);
152 if (fgets(line, sizeof(line), stdin) &&
153 (line[0] == 'y' || line[0] == 'Y'))
154 store_host_key(host, port, keytype, keystr);
156 fprintf(stderr, abandoned);
162 /* GUI Adaptation - Sept 2000 */
163 static void send_msg(HWND h, UINT message, WPARAM wParam)
165 while (!PostMessage(h, message, wParam, 0))
169 static void tell_char(FILE * stream, char c)
174 unsigned int msg_id = WM_STD_OUT_CHAR;
175 if (stream == stderr)
176 msg_id = WM_STD_ERR_CHAR;
177 send_msg((HWND) atoi(gui_hwnd), msg_id, (WPARAM) c);
181 static void tell_str(FILE * stream, char *str)
185 for (i = 0; i < strlen(str); ++i)
186 tell_char(stream, str[i]);
189 static void tell_user(FILE * stream, char *fmt, ...)
191 char str[0x100]; /* Make the size big enough */
194 vsprintf(str, fmt, ap);
197 tell_str(stream, str);
200 static void gui_update_stats(char *name, unsigned long size,
201 int percentage, unsigned long elapsed)
205 if (strcmp(name, statname) != 0) {
206 for (i = 0; i < strlen(name); ++i)
207 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR,
209 send_msg((HWND) atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM) '\n');
210 strcpy(statname, name);
212 if (statsize != size) {
213 send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size);
216 if (statelapsed != elapsed) {
217 send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED,
219 statelapsed = elapsed;
221 if (statperct != percentage) {
222 send_msg((HWND) atoi(gui_hwnd), WM_STATS_PERCENT,
223 (WPARAM) percentage);
224 statperct = percentage;
229 * Print an error message and perform a fatal exit.
231 void fatalbox(char *fmt, ...)
233 char str[0x100]; /* Make the size big enough */
236 strcpy(str, "Fatal:");
237 vsprintf(str + strlen(str), fmt, ap);
240 tell_str(stderr, str);
244 unsigned int msg_id = WM_RET_ERR_CNT;
246 msg_id = WM_LS_RET_ERR_CNT;
248 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
249 0 /*lParam */ ))SleepEx(1000, TRUE);
254 void connection_fatal(char *fmt, ...)
256 char str[0x100]; /* Make the size big enough */
259 strcpy(str, "Fatal:");
260 vsprintf(str + strlen(str), fmt, ap);
263 tell_str(stderr, str);
267 unsigned int msg_id = WM_RET_ERR_CNT;
269 msg_id = WM_LS_RET_ERR_CNT;
271 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
272 0 /*lParam */ ))SleepEx(1000, TRUE);
279 * Be told what socket we're supposed to be using.
281 static SOCKET scp_ssh_socket;
282 char *do_select(SOCKET skt, int startup)
285 scp_ssh_socket = skt;
287 scp_ssh_socket = INVALID_SOCKET;
290 extern int select_result(WPARAM, LPARAM);
293 * Receive a block of data from the SSH link. Block until all data
296 * To do this, we repeatedly call the SSH protocol module, with our
297 * own trap in from_backend() to catch the data that comes back. We
298 * do this until we have enough data.
301 static unsigned char *outptr; /* where to put the data */
302 static unsigned outlen; /* how much data required */
303 static unsigned char *pending = NULL; /* any spare data */
304 static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
305 void from_backend(int is_stderr, char *data, int datalen)
307 unsigned char *p = (unsigned char *) data;
308 unsigned len = (unsigned) datalen;
311 * stderr data is just spouted to local stderr and otherwise
315 fwrite(data, 1, len, stderr);
322 * If this is before the real session begins, just return.
328 unsigned used = outlen;
331 memcpy(outptr, p, used);
339 if (pendsize < pendlen + len) {
340 pendsize = pendlen + len + 4096;
341 pending = (pending ? srealloc(pending, pendsize) :
344 fatalbox("Out of memory");
346 memcpy(pending + pendlen, p, len);
350 static int ssh_scp_recv(unsigned char *buf, int len)
356 * See if the pending-input block contains some of what we
360 unsigned pendused = pendlen;
361 if (pendused > outlen)
363 memcpy(outptr, pending, pendused);
364 memmove(pending, pending + pendused, pendlen - pendused);
381 FD_SET(scp_ssh_socket, &readfds);
382 if (select(1, &readfds, NULL, NULL, NULL) < 0)
384 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
391 * Loop through the ssh connection and authentication process.
393 static void ssh_scp_init(void)
395 if (scp_ssh_socket == INVALID_SOCKET)
397 while (!back->sendok()) {
400 FD_SET(scp_ssh_socket, &readfds);
401 if (select(1, &readfds, NULL, NULL, NULL) < 0)
403 select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
408 * Print an error message and exit after closing the SSH link.
410 static void bump(char *fmt, ...)
412 char str[0x100]; /* Make the size big enough */
415 strcpy(str, "Fatal:");
416 vsprintf(str + strlen(str), fmt, ap);
419 tell_str(stderr, str);
422 if (back != NULL && back->socket() != NULL) {
424 back->special(TS_EOF);
425 ssh_scp_recv(&ch, 1);
429 unsigned int msg_id = WM_RET_ERR_CNT;
431 msg_id = WM_LS_RET_ERR_CNT;
433 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
434 0 /*lParam */ ))SleepEx(1000, TRUE);
440 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
443 DWORD savemode, newmode, i;
445 if (is_pw && password) {
446 static int tried_once = 0;
451 strncpy(str, password, maxlen);
452 str[maxlen - 1] = '\0';
458 /* GUI Adaptation - Sept 2000 */
463 hin = GetStdHandle(STD_INPUT_HANDLE);
464 hout = GetStdHandle(STD_OUTPUT_HANDLE);
465 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
466 bump("Cannot get standard input/output handles");
468 GetConsoleMode(hin, &savemode);
469 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
471 newmode &= ~ENABLE_ECHO_INPUT;
473 newmode |= ENABLE_ECHO_INPUT;
474 SetConsoleMode(hin, newmode);
476 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
477 ReadFile(hin, str, maxlen - 1, &i, NULL);
479 SetConsoleMode(hin, savemode);
481 if ((int) i > maxlen)
488 WriteFile(hout, "\r\n", 2, &i, NULL);
495 * Open an SSH connection to user@host and execute cmd.
497 static void do_cmd(char *host, char *user, char *cmd)
499 char *err, *realhost;
502 if (host == NULL || host[0] == '\0')
503 bump("Empty host name");
505 /* Try to load settings for this host */
506 do_defaults(host, &cfg);
507 if (cfg.host[0] == '\0') {
508 /* No settings for this host; use defaults */
509 do_defaults(NULL, &cfg);
510 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
511 cfg.host[sizeof(cfg.host) - 1] = '\0';
516 if (user != NULL && user[0] != '\0') {
517 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
518 cfg.username[sizeof(cfg.username) - 1] = '\0';
519 } else if (cfg.username[0] == '\0') {
521 if (GetUserName(user, &namelen) == FALSE)
522 bump("Empty user name");
523 user = smalloc(namelen * sizeof(char));
524 GetUserName(user, &namelen);
526 tell_user(stderr, "Guessing user name: %s", user);
527 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
528 cfg.username[sizeof(cfg.username) - 1] = '\0';
532 if (cfg.protocol != PROT_SSH)
536 cfg.port = portnumber;
538 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
539 cfg.remote_cmd[sizeof(cfg.remote_cmd) - 1] = '\0';
544 err = back->init(cfg.host, cfg.port, &realhost);
546 bump("ssh_init: %s", err);
548 if (verbose && realhost != NULL)
549 tell_user(stderr, "Connected to %s\n", realhost);
554 * Update statistic information about current file.
556 static void print_stats(char *name, unsigned long size, unsigned long done,
557 time_t start, time_t now)
564 /* GUI Adaptation - Sept 2000 */
566 gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)),
567 (unsigned long) difftime(now, start));
570 ratebs = (float) done / (now - start);
572 ratebs = (float) done;
577 eta = (unsigned long) ((size - done) / ratebs);
578 sprintf(etastr, "%02ld:%02ld:%02ld",
579 eta / 3600, (eta % 3600) / 60, eta % 60);
581 pct = (int) (100.0 * (float) done / size);
583 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
584 name, done / 1024, ratebs / 1024.0, etastr, pct);
592 * Find a colon in str and return a pointer to the colon.
593 * This is used to separate hostname from filename.
595 static char *colon(char *str)
597 /* We ignore a leading colon, since the hostname cannot be
598 empty. We also ignore a colon as second character because
599 of filenames like f:myfile.txt. */
600 if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
602 while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
611 * Wait for a response from the other side.
612 * Return 0 if ok, -1 if error.
614 static int response(void)
616 char ch, resp, rbuf[2048];
619 if (ssh_scp_recv(&resp, 1) <= 0)
620 bump("Lost connection");
630 case 2: /* fatal error */
632 if (ssh_scp_recv(&ch, 1) <= 0)
633 bump("Protocol error: Lost connection");
635 } while (p < sizeof(rbuf) && ch != '\n');
638 tell_user(stderr, "%s\n", rbuf);
647 * Send an error message to the other side and to the screen.
648 * Increment error counter.
650 static void run_err(const char *fmt, ...)
656 strcpy(str, "scp: ");
657 vsprintf(str + strlen(str), fmt, ap);
659 back->send("\001", 1); /* scp protocol error prefix */
660 back->send(str, strlen(str));
661 tell_user(stderr, "%s", str);
666 * Execute the source part of the SCP protocol.
668 static void source(char *src)
676 unsigned long stat_bytes;
677 time_t stat_starttime, stat_lasttime;
679 attr = GetFileAttributes(src);
680 if (attr == (DWORD) - 1) {
681 run_err("%s: No such file or directory", src);
685 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
688 * Avoid . and .. directories.
691 p = strrchr(src, '/');
693 p = strrchr(src, '\\');
698 if (!strcmp(p, ".") || !strcmp(p, ".."))
699 /* skip . and .. */ ;
703 run_err("%s: not a regular file", src);
708 if ((last = strrchr(src, '/')) == NULL)
712 if (strrchr(last, '\\') != NULL)
713 last = strrchr(last, '\\') + 1;
714 if (last == src && strchr(src, ':') != NULL)
715 last = strchr(src, ':') + 1;
717 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
718 OPEN_EXISTING, 0, 0);
719 if (f == INVALID_HANDLE_VALUE) {
720 run_err("%s: Cannot open file", src);
725 FILETIME actime, wrtime;
726 unsigned long mtime, atime;
727 GetFileTime(f, NULL, &actime, &wrtime);
728 TIME_WIN_TO_POSIX(actime, atime);
729 TIME_WIN_TO_POSIX(wrtime, mtime);
730 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
731 back->send(buf, strlen(buf));
736 size = GetFileSize(f, NULL);
737 sprintf(buf, "C0644 %lu %s\n", size, last);
739 tell_user(stderr, "Sending file modes: %s", buf);
740 back->send(buf, strlen(buf));
745 stat_starttime = time(NULL);
748 for (i = 0; i < size; i += 4096) {
753 if (!ReadFile(f, transbuf, k, &j, NULL) || j != k) {
756 bump("%s: Read error", src);
758 back->send(transbuf, k);
761 if (time(NULL) != stat_lasttime || i + k == size) {
762 stat_lasttime = time(NULL);
763 print_stats(last, size, stat_bytes,
764 stat_starttime, stat_lasttime);
775 * Recursively send the contents of a directory.
777 static void rsource(char *src)
782 WIN32_FIND_DATA fdat;
785 if ((last = strrchr(src, '/')) == NULL)
789 if (strrchr(last, '\\') != NULL)
790 last = strrchr(last, '\\') + 1;
791 if (last == src && strchr(src, ':') != NULL)
792 last = strchr(src, ':') + 1;
794 /* maybe send filetime */
796 sprintf(buf, "D0755 0 %s\n", last);
798 tell_user(stderr, "Entering directory: %s", buf);
799 back->send(buf, strlen(buf));
803 sprintf(buf, "%s/*", src);
804 dir = FindFirstFile(buf, &fdat);
805 ok = (dir != INVALID_HANDLE_VALUE);
807 if (strcmp(fdat.cFileName, ".") == 0 ||
808 strcmp(fdat.cFileName, "..") == 0) {
809 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= sizeof(buf)) {
810 run_err("%s/%s: Name too long", src, fdat.cFileName);
812 sprintf(buf, "%s/%s", src, fdat.cFileName);
815 ok = FindNextFile(dir, &fdat);
820 back->send(buf, strlen(buf));
825 * Execute the sink part of the SCP protocol.
827 static void sink(char *targ, char *src)
837 unsigned long mtime, atime;
839 unsigned long size, i;
841 unsigned long stat_bytes;
842 time_t stat_starttime, stat_lasttime;
845 attr = GetFileAttributes(targ);
846 if (attr != (DWORD) - 1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
849 if (targetshouldbedirectory && !targisdir)
850 bump("%s: Not a directory", targ);
856 if (ssh_scp_recv(&ch, 1) <= 0)
859 bump("Protocol error: Unexpected newline");
863 if (ssh_scp_recv(&ch, 1) <= 0)
864 bump("Lost connection");
866 } while (i < sizeof(buf) && ch != '\n');
869 case '\01': /* error */
870 tell_user(stderr, "%s\n", buf + 1);
873 case '\02': /* fatal error */
879 if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
884 bump("Protocol error: Illegal time format");
889 bump("Protocol error: Expected control record");
892 if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
893 bump("Protocol error: Illegal file descriptor format");
894 /* Security fix: ensure the file ends up where we asked for it. */
901 p = namebuf + strlen(namebuf);
902 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
907 strcpy(namebuf, targ);
909 attr = GetFileAttributes(namebuf);
910 exists = (attr != (DWORD) - 1);
913 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
914 run_err("%s: Not a directory", namebuf);
918 if (!CreateDirectory(namebuf, NULL)) {
919 run_err("%s: Cannot create directory", namebuf);
924 /* can we set the timestamp for directories ? */
928 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
929 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
930 if (f == INVALID_HANDLE_VALUE) {
931 run_err("%s: Cannot create file", namebuf);
938 stat_starttime = time(NULL);
940 if ((stat_name = strrchr(namebuf, '/')) == NULL)
944 if (strrchr(stat_name, '\\') != NULL)
945 stat_name = strrchr(stat_name, '\\') + 1;
947 for (i = 0; i < size; i += 4096) {
952 if (ssh_scp_recv(transbuf, k) == 0)
953 bump("Lost connection");
956 if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
959 printf("\r%-25.25s | %50s\n",
961 "Write error.. waiting for end of file");
966 if (time(NULL) > stat_lasttime || i + k == size) {
967 stat_lasttime = time(NULL);
968 print_stats(stat_name, size, stat_bytes,
969 stat_starttime, stat_lasttime);
976 FILETIME actime, wrtime;
977 TIME_POSIX_TO_WIN(atime, actime);
978 TIME_POSIX_TO_WIN(mtime, wrtime);
979 SetFileTime(f, NULL, &actime, &wrtime);
984 run_err("%s: Write error", namebuf);
992 * We will copy local files to a remote server.
994 static void toremote(int argc, char *argv[])
996 char *src, *targ, *host, *user;
1000 targ = argv[argc - 1];
1002 /* Separate host from filename */
1006 bump("targ == NULL in toremote()");
1010 /* Substitute "." for emtpy target */
1012 /* Separate host and username */
1014 host = strrchr(host, '@');
1025 /* Find out if the source filespec covers multiple files
1026 if so, we should set the targetshouldbedirectory flag */
1028 WIN32_FIND_DATA fdat;
1029 if (colon(argv[0]) != NULL)
1030 bump("%s: Remote to remote not supported", argv[0]);
1031 fh = FindFirstFile(argv[0], &fdat);
1032 if (fh == INVALID_HANDLE_VALUE)
1033 bump("%s: No such file or directory\n", argv[0]);
1034 if (FindNextFile(fh, &fdat))
1035 targetshouldbedirectory = 1;
1039 cmd = smalloc(strlen(targ) + 100);
1040 sprintf(cmd, "scp%s%s%s%s -t %s",
1041 verbose ? " -v" : "",
1042 recursive ? " -r" : "",
1043 preserve ? " -p" : "",
1044 targetshouldbedirectory ? " -d" : "", targ);
1045 do_cmd(host, user, cmd);
1050 for (i = 0; i < argc - 1; i++) {
1052 WIN32_FIND_DATA fdat;
1054 if (colon(src) != NULL) {
1055 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1059 dir = FindFirstFile(src, &fdat);
1060 if (dir == INVALID_HANDLE_VALUE) {
1061 run_err("%s: No such file or directory", src);
1068 * Ensure that . and .. are never matched by wildcards,
1069 * but only by deliberate action.
1071 if (!strcmp(fdat.cFileName, ".") ||
1072 !strcmp(fdat.cFileName, "..")) {
1074 * Find*File has returned a special dir. We require
1075 * that _either_ `src' ends in a backslash followed
1076 * by that string, _or_ `src' is precisely that
1079 int len = strlen(src), dlen = strlen(fdat.cFileName);
1080 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1082 } else if (len > dlen + 1 && src[len - dlen - 1] == '\\' &&
1083 !strcmp(src + len - dlen, fdat.cFileName)) {
1086 continue; /* ignore this one */
1088 if (strlen(src) + strlen(fdat.cFileName) >= sizeof(namebuf)) {
1089 tell_user(stderr, "%s: Name too long", src);
1092 strcpy(namebuf, src);
1093 if ((last = strrchr(namebuf, '/')) == NULL)
1097 if (strrchr(last, '\\') != NULL)
1098 last = strrchr(last, '\\') + 1;
1099 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1100 last = strchr(namebuf, ':') + 1;
1101 strcpy(last, fdat.cFileName);
1103 } while (FindNextFile(dir, &fdat));
1109 * We will copy files from a remote server to the local machine.
1111 static void tolocal(int argc, char *argv[])
1113 char *src, *targ, *host, *user;
1117 bump("More than one remote source not supported");
1122 /* Separate host from filename */
1126 bump("Local to local copy not supported");
1130 /* Substitute "." for empty filename */
1132 /* Separate username and hostname */
1134 host = strrchr(host, '@');
1144 cmd = smalloc(strlen(src) + 100);
1145 sprintf(cmd, "scp%s%s%s%s -f %s",
1146 verbose ? " -v" : "",
1147 recursive ? " -r" : "",
1148 preserve ? " -p" : "",
1149 targetshouldbedirectory ? " -d" : "", src);
1150 do_cmd(host, user, cmd);
1157 * We will issue a list command to get a remote directory.
1159 static void get_dir_list(int argc, char *argv[])
1161 char *src, *host, *user;
1167 /* Separate host from filename */
1171 bump("Local to local copy not supported");
1175 /* Substitute "." for empty filename */
1177 /* Separate username and hostname */
1179 host = strrchr(host, '@');
1189 cmd = smalloc(4 * strlen(src) + 100);
1190 strcpy(cmd, "ls -la '");
1191 p = cmd + strlen(cmd);
1192 for (q = src; *q; q++) {
1205 do_cmd(host, user, cmd);
1208 while (ssh_scp_recv(&c, 1) > 0)
1209 tell_char(stdout, c);
1213 * Initialize the Win$ock driver.
1215 static void init_winsock(void)
1220 winsock_ver = MAKEWORD(1, 1);
1221 if (WSAStartup(winsock_ver, &wsadata))
1222 bump("Unable to initialise WinSock");
1223 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1)
1224 bump("WinSock version is incompatible with 1.1");
1228 * Short description of parameters.
1230 static void usage(void)
1232 printf("PuTTY Secure Copy client\n");
1233 printf("%s\n", ver);
1234 printf("Usage: pscp [options] [user@]host:source target\n");
1236 (" pscp [options] source [source...] [user@]host:target\n");
1237 printf(" pscp [options] -ls user@host:filespec\n");
1238 printf("Options:\n");
1239 printf(" -p preserve file attributes\n");
1240 printf(" -q quiet, don't show statistics\n");
1241 printf(" -r copy directories recursively\n");
1242 printf(" -v show verbose messages\n");
1243 printf(" -P port connect to specified port\n");
1244 printf(" -pw passw login with specified password\n");
1247 * -gui is an internal option, used by GUI front ends to get
1248 * pscp to pass progress reports back to them. It's not an
1249 * ordinary user-accessible option, so it shouldn't be part of
1250 * the command-line help. The only people who need to know
1251 * about it are programmers, and they can read the source.
1254 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1260 * Main program (no, really?)
1262 int main(int argc, char *argv[])
1266 default_protocol = PROT_TELNET;
1268 flags = FLAG_STDERR;
1269 ssh_get_line = &get_line;
1273 for (i = 1; i < argc; i++) {
1274 if (argv[i][0] != '-')
1276 if (strcmp(argv[i], "-v") == 0)
1277 verbose = 1, flags |= FLAG_VERBOSE;
1278 else if (strcmp(argv[i], "-r") == 0)
1280 else if (strcmp(argv[i], "-p") == 0)
1282 else if (strcmp(argv[i], "-q") == 0)
1284 else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0)
1286 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc)
1287 portnumber = atoi(argv[++i]);
1288 else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc)
1289 password = argv[++i];
1290 else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
1291 gui_hwnd = argv[++i];
1293 } else if (strcmp(argv[i], "-ls") == 0)
1295 else if (strcmp(argv[i], "--") == 0) {
1308 get_dir_list(argc, argv);
1315 targetshouldbedirectory = 1;
1317 if (colon(argv[argc - 1]) != NULL)
1318 toremote(argc, argv);
1320 tolocal(argc, argv);
1323 if (back != NULL && back->socket() != NULL) {
1325 back->special(TS_EOF);
1326 ssh_scp_recv(&ch, 1);
1331 /* GUI Adaptation - August 2000 */
1333 unsigned int msg_id = WM_RET_ERR_CNT;
1335 msg_id = WM_LS_RET_ERR_CNT;
1337 ((HWND) atoi(gui_hwnd), msg_id, (WPARAM) errs,
1338 0 /*lParam */ ))SleepEx(1000, TRUE);
1340 return (errs == 0 ? 0 : 1);