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 )
49 static int verbose = 0;
50 static int recursive = 0;
51 static int preserve = 0;
52 static int targetshouldbedirectory = 0;
53 static int statistics = 1;
54 static int portnumber = 0;
55 static char *password = NULL;
57 /* GUI Adaptation - Sept 2000 */
58 #define NAME_STR_MAX 2048
59 static char statname[NAME_STR_MAX+1];
60 static unsigned long statsize = 0;
61 static int statperct = 0;
62 static unsigned long statelapsed = 0;
63 static int gui_mode = 0;
64 static char *gui_hwnd = NULL;
66 static void source(char *src);
67 static void rsource(char *src);
68 static void sink(char *targ, char *src);
69 /* GUI Adaptation - Sept 2000 */
70 static void tell_char(FILE *stream, char c);
71 static void tell_str(FILE *stream, char *str);
72 static void tell_user(FILE *stream, char *fmt, ...);
73 static void send_char_msg(unsigned int msg_id, char c);
74 static void send_str_msg(unsigned int msg_id, char *str);
75 static void gui_update_stats(char *name, unsigned long size,
76 int percentage, unsigned long elapsed);
78 void logevent(char *string) { }
80 void ldisc_send(char *buf, int len) {
82 * This is only here because of the calls to ldisc_send(NULL,
83 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
84 * as an ldisc. So if we get called with any real data, I want
90 void verify_ssh_host_key(char *host, int port, char *keytype,
91 char *keystr, char *fingerprint) {
94 static const char absentmsg[] =
95 "The server's host key is not cached in the registry. You\n"
96 "have no guarantee that the server is the computer you\n"
98 "The server's key fingerprint is:\n"
100 "If you trust this host, enter \"y\" to add the key to\n"
101 "PuTTY's cache and carry on connecting.\n"
102 "If you do not trust this host, enter \"n\" to abandon the\n"
104 "Continue connecting? (y/n) ";
106 static const char wrongmsg[] =
107 "WARNING - POTENTIAL SECURITY BREACH!\n"
108 "The server's host key does not match the one PuTTY has\n"
109 "cached in the registry. This means that either the\n"
110 "server administrator has changed the host key, or you\n"
111 "have actually connected to another computer pretending\n"
112 "to be the server.\n"
113 "The new key fingerprint is:\n"
115 "If you were expecting this change and trust the new key,\n"
116 "enter Yes to update PuTTY's cache and continue connecting.\n"
117 "If you want to carry on connecting but without updating\n"
118 "the cache, enter No.\n"
119 "If you want to abandon the connection completely, press\n"
120 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
122 "Update cached key? (y/n, Return cancels connection) ";
124 static const char abandoned[] = "Connection abandoned.\n";
129 * Verify the key against the registry.
131 ret = verify_host_key(host, port, keytype, keystr);
133 if (ret == 0) /* success - key matched OK */
135 if (ret == 2) { /* key was different */
136 fprintf(stderr, wrongmsg, fingerprint);
138 if (fgets(line, sizeof(line), stdin) &&
139 line[0] != '\0' && line[0] != '\n') {
140 if (line[0] == 'y' || line[0] == 'Y')
141 store_host_key(host, port, keytype, keystr);
143 fprintf(stderr, abandoned);
148 if (ret == 1) { /* key was absent */
149 fprintf(stderr, absentmsg, fingerprint);
150 if (fgets(line, sizeof(line), stdin) &&
151 (line[0] == 'y' || line[0] == 'Y'))
152 store_host_key(host, port, keytype, keystr);
154 fprintf(stderr, abandoned);
160 /* GUI Adaptation - Sept 2000 */
161 static void send_msg(HWND h, UINT message, WPARAM wParam)
163 while (!PostMessage( h, message, wParam, 0))
167 static void tell_char(FILE *stream, char c)
173 unsigned int msg_id = WM_STD_OUT_CHAR;
174 if (stream == stderr) msg_id = WM_STD_ERR_CHAR;
175 send_msg( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)c );
179 static void tell_str(FILE *stream, char *str)
183 for( i = 0; i < strlen(str); ++i )
184 tell_char(stream, str[i]);
187 static void tell_user(FILE *stream, char *fmt, ...)
189 char str[0x100]; /* Make the size big enough */
192 vsprintf(str, fmt, ap);
195 tell_str(stream, str);
198 static void gui_update_stats(char *name, unsigned long size, int percentage, unsigned long elapsed)
202 if (strcmp(name,statname) != 0)
204 for( i = 0; i < strlen(name); ++i )
205 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)name[i]);
206 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)'\n' );
207 strcpy(statname,name);
209 if (statsize != size)
211 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM)size );
214 if (statelapsed != elapsed)
216 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM)elapsed );
217 statelapsed = elapsed;
219 if (statperct != percentage)
221 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_PERCENT, (WPARAM)percentage );
222 statperct = percentage;
227 * Print an error message and perform a fatal exit.
229 void fatalbox(char *fmt, ...)
231 char str[0x100]; /* Make the size big enough */
234 strcpy(str, "Fatal:");
235 vsprintf(str+strlen(str), fmt, ap);
238 tell_str(stderr, str);
242 void connection_fatal(char *fmt, ...)
244 char str[0x100]; /* Make the size big enough */
247 strcpy(str, "Fatal:");
248 vsprintf(str+strlen(str), fmt, ap);
251 tell_str(stderr, str);
257 * Be told what socket we're supposed to be using.
259 static SOCKET scp_ssh_socket;
260 char *do_select(SOCKET skt, int startup) {
262 scp_ssh_socket = skt;
264 scp_ssh_socket = INVALID_SOCKET;
267 extern int select_result(WPARAM, LPARAM);
270 * Receive a block of data from the SSH link. Block until all data
273 * To do this, we repeatedly call the SSH protocol module, with our
274 * own trap in from_backend() to catch the data that comes back. We
275 * do this until we have enough data.
278 static unsigned char *outptr; /* where to put the data */
279 static unsigned outlen; /* how much data required */
280 static unsigned char *pending = NULL; /* any spare data */
281 static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
282 void from_backend(int is_stderr, char *data, int datalen) {
283 unsigned char *p = (unsigned char *)data;
284 unsigned len = (unsigned)datalen;
287 * stderr data is just spouted to local stderr and otherwise
291 fwrite(data, 1, len, stderr);
298 * If this is before the real session begins, just return.
304 unsigned used = outlen;
305 if (used > len) used = len;
306 memcpy(outptr, p, used);
307 outptr += used; outlen -= used;
308 p += used; len -= used;
312 if (pendsize < pendlen + len) {
313 pendsize = pendlen + len + 4096;
314 pending = (pending ? srealloc(pending, pendsize) :
317 fatalbox("Out of memory");
319 memcpy(pending+pendlen, p, len);
323 static int ssh_scp_recv(unsigned char *buf, int len) {
328 * See if the pending-input block contains some of what we
332 unsigned pendused = pendlen;
333 if (pendused > outlen)
335 memcpy(outptr, pending, pendused);
336 memmove(pending, pending+pendused, pendlen-pendused);
353 FD_SET(scp_ssh_socket, &readfds);
354 if (select(1, &readfds, NULL, NULL, NULL) < 0)
356 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
363 * Loop through the ssh connection and authentication process.
365 static void ssh_scp_init(void) {
366 if (scp_ssh_socket == INVALID_SOCKET)
368 while (!back->sendok()) {
371 FD_SET(scp_ssh_socket, &readfds);
372 if (select(1, &readfds, NULL, NULL, NULL) < 0)
374 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
379 * Print an error message and exit after closing the SSH link.
381 static void bump(char *fmt, ...)
383 char str[0x100]; /* Make the size big enough */
386 strcpy(str, "Fatal:");
387 vsprintf(str+strlen(str), fmt, ap);
390 tell_str(stderr, str);
392 if (back != NULL && back->socket() != NULL) {
394 back->special(TS_EOF);
395 ssh_scp_recv(&ch, 1);
400 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
403 DWORD savemode, newmode, i;
405 if (is_pw && password) {
406 static int tried_once = 0;
411 strncpy(str, password, maxlen);
412 str[maxlen-1] = '\0';
418 /* GUI Adaptation - Sept 2000 */
420 if (maxlen>0) str[0] = '\0';
422 hin = GetStdHandle(STD_INPUT_HANDLE);
423 hout = GetStdHandle(STD_OUTPUT_HANDLE);
424 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
425 bump("Cannot get standard input/output handles");
427 GetConsoleMode(hin, &savemode);
428 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
430 newmode &= ~ENABLE_ECHO_INPUT;
432 newmode |= ENABLE_ECHO_INPUT;
433 SetConsoleMode(hin, newmode);
435 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
436 ReadFile(hin, str, maxlen-1, &i, NULL);
438 SetConsoleMode(hin, savemode);
440 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
444 WriteFile(hout, "\r\n", 2, &i, NULL);
451 * Open an SSH connection to user@host and execute cmd.
453 static void do_cmd(char *host, char *user, char *cmd)
455 char *err, *realhost;
458 if (host == NULL || host[0] == '\0')
459 bump("Empty host name");
461 /* Try to load settings for this host */
462 do_defaults(host, &cfg);
463 if (cfg.host[0] == '\0') {
464 /* No settings for this host; use defaults */
465 do_defaults(NULL, &cfg);
466 strncpy(cfg.host, host, sizeof(cfg.host)-1);
467 cfg.host[sizeof(cfg.host)-1] = '\0';
472 if (user != NULL && user[0] != '\0') {
473 strncpy(cfg.username, user, sizeof(cfg.username)-1);
474 cfg.username[sizeof(cfg.username)-1] = '\0';
475 } else if (cfg.username[0] == '\0') {
477 if (GetUserName(user, &namelen) == FALSE)
478 bump("Empty user name");
479 user = smalloc(namelen * sizeof(char));
480 GetUserName(user, &namelen);
481 if (verbose) tell_user(stderr, "Guessing user name: %s", user);
482 strncpy(cfg.username, user, sizeof(cfg.username)-1);
483 cfg.username[sizeof(cfg.username)-1] = '\0';
487 if (cfg.protocol != PROT_SSH)
491 cfg.port = portnumber;
493 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
494 cfg.remote_cmd[sizeof(cfg.remote_cmd)-1] = '\0';
499 err = back->init(cfg.host, cfg.port, &realhost);
501 bump("ssh_init: %s", err);
503 if (verbose && realhost != NULL)
504 tell_user(stderr, "Connected to %s\n", realhost);
508 * Update statistic information about current file.
510 static void print_stats(char *name, unsigned long size, unsigned long done,
511 time_t start, time_t now)
518 /* GUI Adaptation - Sept 2000 */
520 gui_update_stats(name, size, (int)(100 * (done*1.0/size)),
521 (unsigned long)difftime(now, start));
524 ratebs = (float) done / (now - start);
526 ratebs = (float) done;
531 eta = (unsigned long) ((size - done) / ratebs);
532 sprintf(etastr, "%02ld:%02ld:%02ld",
533 eta / 3600, (eta % 3600) / 60, eta % 60);
535 pct = (int) (100.0 * (float) done / size);
537 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
538 name, done / 1024, ratebs / 1024.0,
547 * Find a colon in str and return a pointer to the colon.
548 * This is used to separate hostname from filename.
550 static char * colon(char *str)
552 /* We ignore a leading colon, since the hostname cannot be
553 empty. We also ignore a colon as second character because
554 of filenames like f:myfile.txt. */
555 if (str[0] == '\0' ||
559 while (*str != '\0' &&
571 * Wait for a response from the other side.
572 * Return 0 if ok, -1 if error.
574 static int response(void)
576 char ch, resp, rbuf[2048];
579 if (ssh_scp_recv(&resp, 1) <= 0)
580 bump("Lost connection");
590 case 2: /* fatal error */
592 if (ssh_scp_recv(&ch, 1) <= 0)
593 bump("Protocol error: Lost connection");
595 } while (p < sizeof(rbuf) && ch != '\n');
598 tell_user(stderr, "%s\n", rbuf);
607 * Send an error message to the other side and to the screen.
608 * Increment error counter.
610 static void run_err(const char *fmt, ...)
616 strcpy(str, "scp: ");
617 vsprintf(str+strlen(str), fmt, ap);
619 back->send("\001", 1); /* scp protocol error prefix */
620 back->send(str, strlen(str));
621 tell_user(stderr, "%s",str);
626 * Execute the source part of the SCP protocol.
628 static void source(char *src)
636 unsigned long stat_bytes;
637 time_t stat_starttime, stat_lasttime;
639 attr = GetFileAttributes(src);
640 if (attr == (DWORD)-1) {
641 run_err("%s: No such file or directory", src);
645 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
648 * Avoid . and .. directories.
651 p = strrchr(src, '/');
653 p = strrchr(src, '\\');
658 if (!strcmp(p, ".") || !strcmp(p, ".."))
663 run_err("%s: not a regular file", src);
668 if ((last = strrchr(src, '/')) == NULL)
672 if (strrchr(last, '\\') != NULL)
673 last = strrchr(last, '\\') + 1;
674 if (last == src && strchr(src, ':') != NULL)
675 last = strchr(src, ':') + 1;
677 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
678 OPEN_EXISTING, 0, 0);
679 if (f == INVALID_HANDLE_VALUE) {
680 run_err("%s: Cannot open file", src);
685 FILETIME actime, wrtime;
686 unsigned long mtime, atime;
687 GetFileTime(f, NULL, &actime, &wrtime);
688 TIME_WIN_TO_POSIX(actime, atime);
689 TIME_WIN_TO_POSIX(wrtime, mtime);
690 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
691 back->send(buf, strlen(buf));
696 size = GetFileSize(f, NULL);
697 sprintf(buf, "C0644 %lu %s\n", size, last);
699 tell_user(stderr, "Sending file modes: %s", buf);
700 back->send(buf, strlen(buf));
706 stat_starttime = time(NULL);
710 for (i = 0; i < size; i += 4096) {
713 if (i + k > size) k = size - i;
714 if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) {
715 if (statistics) printf("\n");
716 bump("%s: Read error", src);
718 back->send(transbuf, k);
721 if (time(NULL) != stat_lasttime ||
723 stat_lasttime = time(NULL);
724 print_stats(last, size, stat_bytes,
725 stat_starttime, stat_lasttime);
736 * Recursively send the contents of a directory.
738 static void rsource(char *src)
743 WIN32_FIND_DATA fdat;
746 if ((last = strrchr(src, '/')) == NULL)
750 if (strrchr(last, '\\') != NULL)
751 last = strrchr(last, '\\') + 1;
752 if (last == src && strchr(src, ':') != NULL)
753 last = strchr(src, ':') + 1;
755 /* maybe send filetime */
757 sprintf(buf, "D0755 0 %s\n", last);
759 tell_user(stderr, "Entering directory: %s", buf);
760 back->send(buf, strlen(buf));
764 sprintf(buf, "%s/*", src);
765 dir = FindFirstFile(buf, &fdat);
766 ok = (dir != INVALID_HANDLE_VALUE);
768 if (strcmp(fdat.cFileName, ".") == 0 ||
769 strcmp(fdat.cFileName, "..") == 0) {
770 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >=
772 run_err("%s/%s: Name too long", src, fdat.cFileName);
774 sprintf(buf, "%s/%s", src, fdat.cFileName);
777 ok = FindNextFile(dir, &fdat);
782 back->send(buf, strlen(buf));
787 * Execute the sink part of the SCP protocol.
789 static void sink(char *targ, char *src)
799 unsigned long mtime, atime;
801 unsigned long size, i;
803 unsigned long stat_bytes;
804 time_t stat_starttime, stat_lasttime;
807 attr = GetFileAttributes(targ);
808 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
811 if (targetshouldbedirectory && !targisdir)
812 bump("%s: Not a directory", targ);
818 if (ssh_scp_recv(&ch, 1) <= 0)
821 bump("Protocol error: Unexpected newline");
825 if (ssh_scp_recv(&ch, 1) <= 0)
826 bump("Lost connection");
828 } while (i < sizeof(buf) && ch != '\n');
831 case '\01': /* error */
832 tell_user(stderr, "%s\n", buf+1);
835 case '\02': /* fatal error */
841 if (sscanf(buf, "T%ld %*d %ld %*d",
842 &mtime, &atime) == 2) {
847 bump("Protocol error: Illegal time format");
852 bump("Protocol error: Expected control record");
855 if (sscanf(buf+1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
856 bump("Protocol error: Illegal file descriptor format");
857 /* Security fix: ensure the file ends up where we asked for it. */
864 p = namebuf + strlen(namebuf);
865 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
870 strcpy(namebuf, targ);
872 attr = GetFileAttributes(namebuf);
873 exists = (attr != (DWORD)-1);
876 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
877 run_err("%s: Not a directory", namebuf);
881 if (! CreateDirectory(namebuf, NULL)) {
882 run_err("%s: Cannot create directory",
888 /* can we set the timestamp for directories ? */
892 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
893 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
894 if (f == INVALID_HANDLE_VALUE) {
895 run_err("%s: Cannot create file", namebuf);
903 stat_starttime = time(NULL);
905 if ((stat_name = strrchr(namebuf, '/')) == NULL)
909 if (strrchr(stat_name, '\\') != NULL)
910 stat_name = strrchr(stat_name, '\\') + 1;
913 for (i = 0; i < size; i += 4096) {
916 if (i + k > size) k = size - i;
917 if (ssh_scp_recv(transbuf, k) == 0)
918 bump("Lost connection");
919 if (wrerror) continue;
920 if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) {
923 printf("\r%-25.25s | %50s\n",
925 "Write error.. waiting for end of file");
930 if (time(NULL) > stat_lasttime ||
932 stat_lasttime = time(NULL);
933 print_stats(stat_name, size, stat_bytes,
934 stat_starttime, stat_lasttime);
941 FILETIME actime, wrtime;
942 TIME_POSIX_TO_WIN(atime, actime);
943 TIME_POSIX_TO_WIN(mtime, wrtime);
944 SetFileTime(f, NULL, &actime, &wrtime);
949 run_err("%s: Write error", namebuf);
957 * We will copy local files to a remote server.
959 static void toremote(int argc, char *argv[])
961 char *src, *targ, *host, *user;
967 /* Separate host from filename */
971 bump("targ == NULL in toremote()");
975 /* Substitute "." for emtpy target */
977 /* Separate host and username */
979 host = strrchr(host, '@');
990 /* Find out if the source filespec covers multiple files
991 if so, we should set the targetshouldbedirectory flag */
993 WIN32_FIND_DATA fdat;
994 if (colon(argv[0]) != NULL)
995 bump("%s: Remote to remote not supported", argv[0]);
996 fh = FindFirstFile(argv[0], &fdat);
997 if (fh == INVALID_HANDLE_VALUE)
998 bump("%s: No such file or directory\n", argv[0]);
999 if (FindNextFile(fh, &fdat))
1000 targetshouldbedirectory = 1;
1004 cmd = smalloc(strlen(targ) + 100);
1005 sprintf(cmd, "scp%s%s%s%s -t %s",
1006 verbose ? " -v" : "",
1007 recursive ? " -r" : "",
1008 preserve ? " -p" : "",
1009 targetshouldbedirectory ? " -d" : "",
1011 do_cmd(host, user, cmd);
1016 for (i = 0; i < argc - 1; i++) {
1018 WIN32_FIND_DATA fdat;
1020 if (colon(src) != NULL) {
1021 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1025 dir = FindFirstFile(src, &fdat);
1026 if (dir == INVALID_HANDLE_VALUE) {
1027 run_err("%s: No such file or directory", src);
1034 * Ensure that . and .. are never matched by wildcards,
1035 * but only by deliberate action.
1037 if (!strcmp(fdat.cFileName, ".") ||
1038 !strcmp(fdat.cFileName, "..")) {
1040 * Find*File has returned a special dir. We require
1041 * that _either_ `src' ends in a backslash followed
1042 * by that string, _or_ `src' is precisely that
1045 int len = strlen(src), dlen = strlen(fdat.cFileName);
1046 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1048 } else if (len > dlen+1 && src[len-dlen-1] == '\\' &&
1049 !strcmp(src+len-dlen, fdat.cFileName)) {
1052 continue; /* ignore this one */
1054 if (strlen(src) + strlen(fdat.cFileName) >=
1056 tell_user(stderr, "%s: Name too long", src);
1059 strcpy(namebuf, src);
1060 if ((last = strrchr(namebuf, '/')) == NULL)
1064 if (strrchr(last, '\\') != NULL)
1065 last = strrchr(last, '\\') + 1;
1066 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1067 last = strchr(namebuf, ':') + 1;
1068 strcpy(last, fdat.cFileName);
1070 } while (FindNextFile(dir, &fdat));
1076 * We will copy files from a remote server to the local machine.
1078 static void tolocal(int argc, char *argv[])
1080 char *src, *targ, *host, *user;
1084 bump("More than one remote source not supported");
1089 /* Separate host from filename */
1093 bump("Local to local copy not supported");
1097 /* Substitute "." for empty filename */
1099 /* Separate username and hostname */
1101 host = strrchr(host, '@');
1111 cmd = smalloc(strlen(src) + 100);
1112 sprintf(cmd, "scp%s%s%s%s -f %s",
1113 verbose ? " -v" : "",
1114 recursive ? " -r" : "",
1115 preserve ? " -p" : "",
1116 targetshouldbedirectory ? " -d" : "",
1118 do_cmd(host, user, cmd);
1125 * We will issue a list command to get a remote directory.
1127 static void get_dir_list(int argc, char *argv[])
1129 char *src, *host, *user;
1135 /* Separate host from filename */
1139 bump("Local to local copy not supported");
1143 /* Substitute "." for empty filename */
1145 /* Separate username and hostname */
1147 host = strrchr(host, '@');
1157 cmd = smalloc(4*strlen(src) + 100);
1158 strcpy(cmd, "ls -la '");
1159 p = cmd + strlen(cmd);
1160 for (q = src; *q; q++) {
1162 *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\'';
1170 do_cmd(host, user, cmd);
1173 while (ssh_scp_recv(&c, 1) > 0)
1174 tell_char(stdout, c);
1178 * Initialize the Win$ock driver.
1180 static void init_winsock(void)
1185 winsock_ver = MAKEWORD(1, 1);
1186 if (WSAStartup(winsock_ver, &wsadata))
1187 bump("Unable to initialise WinSock");
1188 if (LOBYTE(wsadata.wVersion) != 1 ||
1189 HIBYTE(wsadata.wVersion) != 1)
1190 bump("WinSock version is incompatible with 1.1");
1194 * Short description of parameters.
1196 static void usage(void)
1198 printf("PuTTY Secure Copy client\n");
1199 printf("%s\n", ver);
1200 printf("Usage: pscp [options] [user@]host:source target\n");
1201 printf(" pscp [options] source [source...] [user@]host:target\n");
1202 printf(" pscp [options] -ls user@host:filespec\n");
1203 printf("Options:\n");
1204 printf(" -p preserve file attributes\n");
1205 printf(" -q quiet, don't show statistics\n");
1206 printf(" -r copy directories recursively\n");
1207 printf(" -v show verbose messages\n");
1208 printf(" -P port connect to specified port\n");
1209 printf(" -pw passw login with specified password\n");
1212 * -gui is an internal option, used by GUI front ends to get
1213 * pscp to pass progress reports back to them. It's not an
1214 * ordinary user-accessible option, so it shouldn't be part of
1215 * the command-line help. The only people who need to know
1216 * about it are programmers, and they can read the source.
1218 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1224 * Main program (no, really?)
1226 int main(int argc, char *argv[])
1231 default_protocol = PROT_TELNET;
1233 flags = FLAG_STDERR;
1234 ssh_get_line = &get_line;
1238 for (i = 1; i < argc; i++) {
1239 if (argv[i][0] != '-')
1241 if (strcmp(argv[i], "-v") == 0)
1242 verbose = 1, flags |= FLAG_VERBOSE;
1243 else if (strcmp(argv[i], "-r") == 0)
1245 else if (strcmp(argv[i], "-p") == 0)
1247 else if (strcmp(argv[i], "-q") == 0)
1249 else if (strcmp(argv[i], "-h") == 0 ||
1250 strcmp(argv[i], "-?") == 0)
1252 else if (strcmp(argv[i], "-P") == 0 && i+1 < argc)
1253 portnumber = atoi(argv[++i]);
1254 else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc)
1255 password = argv[++i];
1256 else if (strcmp(argv[i], "-gui") == 0 && i+1 < argc) {
1257 gui_hwnd = argv[++i];
1259 } else if (strcmp(argv[i], "-ls") == 0)
1261 else if (strcmp(argv[i], "--") == 0)
1273 get_dir_list(argc, argv);
1280 targetshouldbedirectory = 1;
1282 if (colon(argv[argc-1]) != NULL)
1283 toremote(argc, argv);
1285 tolocal(argc, argv);
1288 if (back != NULL && back->socket() != NULL) {
1290 back->special(TS_EOF);
1291 ssh_scp_recv(&ch, 1);
1296 /* GUI Adaptation - August 2000 */
1298 unsigned int msg_id = WM_RET_ERR_CNT;
1299 if (list) msg_id = WM_LS_RET_ERR_CNT;
1300 while (!PostMessage( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)errs, 0/*lParam*/ ) )
1303 return (errs == 0 ? 0 : 1);