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);
137 if (fgets(line, sizeof(line), stdin) &&
138 line[0] != '\0' && line[0] != '\n') {
139 if (line[0] == 'y' || line[0] == 'Y')
140 store_host_key(host, port, keytype, keystr);
142 fprintf(stderr, abandoned);
146 if (ret == 1) { /* key was absent */
147 fprintf(stderr, absentmsg, fingerprint);
148 if (fgets(line, sizeof(line), stdin) &&
149 (line[0] == 'y' || line[0] == 'Y'))
150 store_host_key(host, port, keytype, keystr);
152 fprintf(stderr, abandoned);
158 /* GUI Adaptation - Sept 2000 */
159 static void send_msg(HWND h, UINT message, WPARAM wParam)
161 while (!PostMessage( h, message, wParam, 0))
165 static void tell_char(FILE *stream, char c)
171 unsigned int msg_id = WM_STD_OUT_CHAR;
172 if (stream == stderr) msg_id = WM_STD_ERR_CHAR;
173 send_msg( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)c );
177 static void tell_str(FILE *stream, char *str)
181 for( i = 0; i < strlen(str); ++i )
182 tell_char(stream, str[i]);
185 static void tell_user(FILE *stream, char *fmt, ...)
187 char str[0x100]; /* Make the size big enough */
190 vsprintf(str, fmt, ap);
193 tell_str(stream, str);
196 static void gui_update_stats(char *name, unsigned long size, int percentage, unsigned long elapsed)
200 if (strcmp(name,statname) != 0)
202 for( i = 0; i < strlen(name); ++i )
203 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)name[i]);
204 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)'\n' );
205 strcpy(statname,name);
207 if (statsize != size)
209 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM)size );
212 if (statelapsed != elapsed)
214 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM)elapsed );
215 statelapsed = elapsed;
217 if (statperct != percentage)
219 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_PERCENT, (WPARAM)percentage );
220 statperct = percentage;
225 * Print an error message and perform a fatal exit.
227 void fatalbox(char *fmt, ...)
229 char str[0x100]; /* Make the size big enough */
232 strcpy(str, "Fatal:");
233 vsprintf(str+strlen(str), fmt, ap);
236 tell_str(stderr, str);
240 void connection_fatal(char *fmt, ...)
242 char str[0x100]; /* Make the size big enough */
245 strcpy(str, "Fatal:");
246 vsprintf(str+strlen(str), fmt, ap);
249 tell_str(stderr, str);
255 * Be told what socket we're supposed to be using.
257 static SOCKET scp_ssh_socket;
258 char *do_select(SOCKET skt, int startup) {
260 scp_ssh_socket = skt;
262 scp_ssh_socket = INVALID_SOCKET;
265 extern int select_result(WPARAM, LPARAM);
268 * Receive a block of data from the SSH link. Block until all data
271 * To do this, we repeatedly call the SSH protocol module, with our
272 * own trap in from_backend() to catch the data that comes back. We
273 * do this until we have enough data.
276 static unsigned char *outptr; /* where to put the data */
277 static unsigned outlen; /* how much data required */
278 static unsigned char *pending = NULL; /* any spare data */
279 static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
280 void from_backend(int is_stderr, char *data, int datalen) {
281 unsigned char *p = (unsigned char *)data;
282 unsigned len = (unsigned)datalen;
285 * stderr data is just spouted to local stderr and otherwise
289 fwrite(data, 1, len, stderr);
296 * If this is before the real session begins, just return.
302 unsigned used = outlen;
303 if (used > len) used = len;
304 memcpy(outptr, p, used);
305 outptr += used; outlen -= used;
306 p += used; len -= used;
310 if (pendsize < pendlen + len) {
311 pendsize = pendlen + len + 4096;
312 pending = (pending ? srealloc(pending, pendsize) :
315 fatalbox("Out of memory");
317 memcpy(pending+pendlen, p, len);
321 static int ssh_scp_recv(unsigned char *buf, int len) {
326 * See if the pending-input block contains some of what we
330 unsigned pendused = pendlen;
331 if (pendused > outlen)
333 memcpy(outptr, pending, pendused);
334 memmove(pending, pending+pendused, pendlen-pendused);
351 FD_SET(scp_ssh_socket, &readfds);
352 if (select(1, &readfds, NULL, NULL, NULL) < 0)
354 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
361 * Loop through the ssh connection and authentication process.
363 static void ssh_scp_init(void) {
364 if (scp_ssh_socket == INVALID_SOCKET)
366 while (!back->sendok()) {
369 FD_SET(scp_ssh_socket, &readfds);
370 if (select(1, &readfds, NULL, NULL, NULL) < 0)
372 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
377 * Print an error message and exit after closing the SSH link.
379 static void bump(char *fmt, ...)
381 char str[0x100]; /* Make the size big enough */
384 strcpy(str, "Fatal:");
385 vsprintf(str+strlen(str), fmt, ap);
388 tell_str(stderr, str);
390 if (back != NULL && back->socket() != NULL) {
392 back->special(TS_EOF);
393 ssh_scp_recv(&ch, 1);
398 static int get_password(const char *prompt, char *str, int maxlen)
404 static int tried_once = 0;
409 strncpy(str, password, maxlen);
410 str[maxlen-1] = '\0';
416 /* GUI Adaptation - Sept 2000 */
418 if (maxlen>0) str[0] = '\0';
420 hin = GetStdHandle(STD_INPUT_HANDLE);
421 hout = GetStdHandle(STD_OUTPUT_HANDLE);
422 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
423 bump("Cannot get standard input/output handles");
425 GetConsoleMode(hin, &savemode);
426 SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
427 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
429 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
430 ReadFile(hin, str, maxlen-1, &i, NULL);
432 SetConsoleMode(hin, savemode);
434 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
437 WriteFile(hout, "\r\n", 2, &i, NULL);
444 * Open an SSH connection to user@host and execute cmd.
446 static void do_cmd(char *host, char *user, char *cmd)
448 char *err, *realhost;
451 if (host == NULL || host[0] == '\0')
452 bump("Empty host name");
454 /* Try to load settings for this host */
455 do_defaults(host, &cfg);
456 if (cfg.host[0] == '\0') {
457 /* No settings for this host; use defaults */
458 do_defaults(NULL, &cfg);
459 strncpy(cfg.host, host, sizeof(cfg.host)-1);
460 cfg.host[sizeof(cfg.host)-1] = '\0';
465 if (user != NULL && user[0] != '\0') {
466 strncpy(cfg.username, user, sizeof(cfg.username)-1);
467 cfg.username[sizeof(cfg.username)-1] = '\0';
468 } else if (cfg.username[0] == '\0') {
470 if (GetUserName(user, &namelen) == FALSE)
471 bump("Empty user name");
472 user = smalloc(namelen * sizeof(char));
473 GetUserName(user, &namelen);
474 if (verbose) tell_user(stderr, "Guessing user name: %s", user);
475 strncpy(cfg.username, user, sizeof(cfg.username)-1);
476 cfg.username[sizeof(cfg.username)-1] = '\0';
480 if (cfg.protocol != PROT_SSH)
484 cfg.port = portnumber;
486 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
487 cfg.remote_cmd[sizeof(cfg.remote_cmd)-1] = '\0';
492 err = back->init(cfg.host, cfg.port, &realhost);
494 bump("ssh_init: %s", err);
496 if (verbose && realhost != NULL)
497 tell_user(stderr, "Connected to %s\n", realhost);
501 * Update statistic information about current file.
503 static void print_stats(char *name, unsigned long size, unsigned long done,
504 time_t start, time_t now)
511 /* GUI Adaptation - Sept 2000 */
513 gui_update_stats(name, size, (int)(100 * (done*1.0/size)),
514 (unsigned long)difftime(now, start));
517 ratebs = (float) done / (now - start);
519 ratebs = (float) done;
524 eta = (unsigned long) ((size - done) / ratebs);
525 sprintf(etastr, "%02ld:%02ld:%02ld",
526 eta / 3600, (eta % 3600) / 60, eta % 60);
528 pct = (int) (100.0 * (float) done / size);
530 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
531 name, done / 1024, ratebs / 1024.0,
540 * Find a colon in str and return a pointer to the colon.
541 * This is used to separate hostname from filename.
543 static char * colon(char *str)
545 /* We ignore a leading colon, since the hostname cannot be
546 empty. We also ignore a colon as second character because
547 of filenames like f:myfile.txt. */
548 if (str[0] == '\0' ||
552 while (*str != '\0' &&
564 * Wait for a response from the other side.
565 * Return 0 if ok, -1 if error.
567 static int response(void)
569 char ch, resp, rbuf[2048];
572 if (ssh_scp_recv(&resp, 1) <= 0)
573 bump("Lost connection");
583 case 2: /* fatal error */
585 if (ssh_scp_recv(&ch, 1) <= 0)
586 bump("Protocol error: Lost connection");
588 } while (p < sizeof(rbuf) && ch != '\n');
591 tell_user(stderr, "%s\n", rbuf);
600 * Send an error message to the other side and to the screen.
601 * Increment error counter.
603 static void run_err(const char *fmt, ...)
609 strcpy(str, "scp: ");
610 vsprintf(str+strlen(str), fmt, ap);
612 back->send(str, strlen(str));
613 tell_user(stderr, "%s",str);
618 * Execute the source part of the SCP protocol.
620 static void source(char *src)
628 unsigned long stat_bytes;
629 time_t stat_starttime, stat_lasttime;
631 attr = GetFileAttributes(src);
632 if (attr == (DWORD)-1) {
633 run_err("%s: No such file or directory", src);
637 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
640 * Avoid . and .. directories.
643 p = strrchr(src, '/');
645 p = strrchr(src, '\\');
650 if (!strcmp(p, ".") || !strcmp(p, ".."))
655 run_err("%s: not a regular file", src);
660 if ((last = strrchr(src, '/')) == NULL)
664 if (strrchr(last, '\\') != NULL)
665 last = strrchr(last, '\\') + 1;
666 if (last == src && strchr(src, ':') != NULL)
667 last = strchr(src, ':') + 1;
669 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
670 OPEN_EXISTING, 0, 0);
671 if (f == INVALID_HANDLE_VALUE) {
672 run_err("%s: Cannot open file", src);
677 FILETIME actime, wrtime;
678 unsigned long mtime, atime;
679 GetFileTime(f, NULL, &actime, &wrtime);
680 TIME_WIN_TO_POSIX(actime, atime);
681 TIME_WIN_TO_POSIX(wrtime, mtime);
682 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
683 back->send(buf, strlen(buf));
688 size = GetFileSize(f, NULL);
689 sprintf(buf, "C0644 %lu %s\n", size, last);
691 tell_user(stderr, "Sending file modes: %s", buf);
692 back->send(buf, strlen(buf));
698 stat_starttime = time(NULL);
702 for (i = 0; i < size; i += 4096) {
705 if (i + k > size) k = size - i;
706 if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) {
707 if (statistics) printf("\n");
708 bump("%s: Read error", src);
710 back->send(transbuf, k);
713 if (time(NULL) != stat_lasttime ||
715 stat_lasttime = time(NULL);
716 print_stats(last, size, stat_bytes,
717 stat_starttime, stat_lasttime);
728 * Recursively send the contents of a directory.
730 static void rsource(char *src)
735 WIN32_FIND_DATA fdat;
738 if ((last = strrchr(src, '/')) == NULL)
742 if (strrchr(last, '\\') != NULL)
743 last = strrchr(last, '\\') + 1;
744 if (last == src && strchr(src, ':') != NULL)
745 last = strchr(src, ':') + 1;
747 /* maybe send filetime */
749 sprintf(buf, "D0755 0 %s\n", last);
751 tell_user(stderr, "Entering directory: %s", buf);
752 back->send(buf, strlen(buf));
756 sprintf(buf, "%s/*", src);
757 dir = FindFirstFile(buf, &fdat);
758 ok = (dir != INVALID_HANDLE_VALUE);
760 if (strcmp(fdat.cFileName, ".") == 0 ||
761 strcmp(fdat.cFileName, "..") == 0) {
762 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >=
764 run_err("%s/%s: Name too long", src, fdat.cFileName);
766 sprintf(buf, "%s/%s", src, fdat.cFileName);
769 ok = FindNextFile(dir, &fdat);
774 back->send(buf, strlen(buf));
779 * Execute the sink part of the SCP protocol.
781 static void sink(char *targ, char *src)
791 unsigned long mtime, atime;
793 unsigned long size, i;
795 unsigned long stat_bytes;
796 time_t stat_starttime, stat_lasttime;
799 attr = GetFileAttributes(targ);
800 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
803 if (targetshouldbedirectory && !targisdir)
804 bump("%s: Not a directory", targ);
810 if (ssh_scp_recv(&ch, 1) <= 0)
813 bump("Protocol error: Unexpected newline");
817 if (ssh_scp_recv(&ch, 1) <= 0)
818 bump("Lost connection");
820 } while (i < sizeof(buf) && ch != '\n');
823 case '\01': /* error */
824 tell_user(stderr, "%s\n", buf+1);
827 case '\02': /* fatal error */
833 if (sscanf(buf, "T%ld %*d %ld %*d",
834 &mtime, &atime) == 2) {
839 bump("Protocol error: Illegal time format");
844 bump("Protocol error: Expected control record");
847 if (sscanf(buf+1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
848 bump("Protocol error: Illegal file descriptor format");
849 /* Security fix: ensure the file ends up where we asked for it. */
856 p = namebuf + strlen(namebuf);
857 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
862 strcpy(namebuf, targ);
864 attr = GetFileAttributes(namebuf);
865 exists = (attr != (DWORD)-1);
868 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
869 run_err("%s: Not a directory", namebuf);
873 if (! CreateDirectory(namebuf, NULL)) {
874 run_err("%s: Cannot create directory",
880 /* can we set the timestamp for directories ? */
884 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
885 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
886 if (f == INVALID_HANDLE_VALUE) {
887 run_err("%s: Cannot create file", namebuf);
895 stat_starttime = time(NULL);
897 if ((stat_name = strrchr(namebuf, '/')) == NULL)
901 if (strrchr(stat_name, '\\') != NULL)
902 stat_name = strrchr(stat_name, '\\') + 1;
905 for (i = 0; i < size; i += 4096) {
908 if (i + k > size) k = size - i;
909 if (ssh_scp_recv(transbuf, k) == 0)
910 bump("Lost connection");
911 if (wrerror) continue;
912 if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) {
915 printf("\r%-25.25s | %50s\n",
917 "Write error.. waiting for end of file");
922 if (time(NULL) > stat_lasttime ||
924 stat_lasttime = time(NULL);
925 print_stats(stat_name, size, stat_bytes,
926 stat_starttime, stat_lasttime);
933 FILETIME actime, wrtime;
934 TIME_POSIX_TO_WIN(atime, actime);
935 TIME_POSIX_TO_WIN(mtime, wrtime);
936 SetFileTime(f, NULL, &actime, &wrtime);
941 run_err("%s: Write error", namebuf);
949 * We will copy local files to a remote server.
951 static void toremote(int argc, char *argv[])
953 char *src, *targ, *host, *user;
959 /* Separate host from filename */
963 bump("targ == NULL in toremote()");
967 /* Substitute "." for emtpy target */
969 /* Separate host and username */
971 host = strrchr(host, '@');
982 /* Find out if the source filespec covers multiple files
983 if so, we should set the targetshouldbedirectory flag */
985 WIN32_FIND_DATA fdat;
986 if (colon(argv[0]) != NULL)
987 bump("%s: Remote to remote not supported", argv[0]);
988 fh = FindFirstFile(argv[0], &fdat);
989 if (fh == INVALID_HANDLE_VALUE)
990 bump("%s: No such file or directory\n", argv[0]);
991 if (FindNextFile(fh, &fdat))
992 targetshouldbedirectory = 1;
996 cmd = smalloc(strlen(targ) + 100);
997 sprintf(cmd, "scp%s%s%s%s -t %s",
998 verbose ? " -v" : "",
999 recursive ? " -r" : "",
1000 preserve ? " -p" : "",
1001 targetshouldbedirectory ? " -d" : "",
1003 do_cmd(host, user, cmd);
1008 for (i = 0; i < argc - 1; i++) {
1010 WIN32_FIND_DATA fdat;
1012 if (colon(src) != NULL) {
1013 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1017 dir = FindFirstFile(src, &fdat);
1018 if (dir == INVALID_HANDLE_VALUE) {
1019 run_err("%s: No such file or directory", src);
1026 * Ensure that . and .. are never matched by wildcards,
1027 * but only by deliberate action.
1029 if (!strcmp(fdat.cFileName, ".") ||
1030 !strcmp(fdat.cFileName, "..")) {
1032 * Find*File has returned a special dir. We require
1033 * that _either_ `src' ends in a backslash followed
1034 * by that string, _or_ `src' is precisely that
1037 int len = strlen(src), dlen = strlen(fdat.cFileName);
1038 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1040 } else if (len > dlen+1 && src[len-dlen-1] == '\\' &&
1041 !strcmp(src+len-dlen, fdat.cFileName)) {
1044 continue; /* ignore this one */
1046 if (strlen(src) + strlen(fdat.cFileName) >=
1048 tell_user(stderr, "%s: Name too long", src);
1051 strcpy(namebuf, src);
1052 if ((last = strrchr(namebuf, '/')) == NULL)
1056 if (strrchr(last, '\\') != NULL)
1057 last = strrchr(last, '\\') + 1;
1058 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1059 last = strchr(namebuf, ':') + 1;
1060 strcpy(last, fdat.cFileName);
1062 } while (FindNextFile(dir, &fdat));
1068 * We will copy files from a remote server to the local machine.
1070 static void tolocal(int argc, char *argv[])
1072 char *src, *targ, *host, *user;
1076 bump("More than one remote source not supported");
1081 /* Separate host from filename */
1085 bump("Local to local copy not supported");
1089 /* Substitute "." for empty filename */
1091 /* Separate username and hostname */
1093 host = strrchr(host, '@');
1103 cmd = smalloc(strlen(src) + 100);
1104 sprintf(cmd, "scp%s%s%s%s -f %s",
1105 verbose ? " -v" : "",
1106 recursive ? " -r" : "",
1107 preserve ? " -p" : "",
1108 targetshouldbedirectory ? " -d" : "",
1110 do_cmd(host, user, cmd);
1117 * We will issue a list command to get a remote directory.
1119 static void get_dir_list(int argc, char *argv[])
1121 char *src, *host, *user;
1127 /* Separate host from filename */
1131 bump("Local to local copy not supported");
1135 /* Substitute "." for empty filename */
1137 /* Separate username and hostname */
1139 host = strrchr(host, '@');
1149 cmd = smalloc(4*strlen(src) + 100);
1150 strcpy(cmd, "ls -la '");
1151 p = cmd + strlen(cmd);
1152 for (q = src; *q; q++) {
1154 *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\'';
1162 do_cmd(host, user, cmd);
1165 while (ssh_scp_recv(&c, 1) > 0)
1166 tell_char(stdout, c);
1170 * Initialize the Win$ock driver.
1172 static void init_winsock(void)
1177 winsock_ver = MAKEWORD(1, 1);
1178 if (WSAStartup(winsock_ver, &wsadata))
1179 bump("Unable to initialise WinSock");
1180 if (LOBYTE(wsadata.wVersion) != 1 ||
1181 HIBYTE(wsadata.wVersion) != 1)
1182 bump("WinSock version is incompatible with 1.1");
1186 * Short description of parameters.
1188 static void usage(void)
1190 printf("PuTTY Secure Copy client\n");
1191 printf("%s\n", ver);
1192 printf("Usage: pscp [options] [user@]host:source target\n");
1193 printf(" pscp [options] source [source...] [user@]host:target\n");
1194 printf(" pscp [options] -ls user@host:filespec\n");
1195 printf("Options:\n");
1196 printf(" -p preserve file attributes\n");
1197 printf(" -q quiet, don't show statistics\n");
1198 printf(" -r copy directories recursively\n");
1199 printf(" -v show verbose messages\n");
1200 printf(" -P port connect to specified port\n");
1201 printf(" -pw passw login with specified password\n");
1204 * -gui is an internal option, used by GUI front ends to get
1205 * pscp to pass progress reports back to them. It's not an
1206 * ordinary user-accessible option, so it shouldn't be part of
1207 * the command-line help. The only people who need to know
1208 * about it are programmers, and they can read the source.
1210 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1216 * Main program (no, really?)
1218 int main(int argc, char *argv[])
1223 default_protocol = PROT_TELNET;
1225 flags = FLAG_STDERR;
1226 ssh_get_password = &get_password;
1230 for (i = 1; i < argc; i++) {
1231 if (argv[i][0] != '-')
1233 if (strcmp(argv[i], "-v") == 0)
1234 verbose = 1, flags |= FLAG_VERBOSE;
1235 else if (strcmp(argv[i], "-r") == 0)
1237 else if (strcmp(argv[i], "-p") == 0)
1239 else if (strcmp(argv[i], "-q") == 0)
1241 else if (strcmp(argv[i], "-h") == 0 ||
1242 strcmp(argv[i], "-?") == 0)
1244 else if (strcmp(argv[i], "-P") == 0 && i+1 < argc)
1245 portnumber = atoi(argv[++i]);
1246 else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc)
1247 password = argv[++i];
1248 else if (strcmp(argv[i], "-gui") == 0 && i+1 < argc) {
1249 gui_hwnd = argv[++i];
1251 } else if (strcmp(argv[i], "-ls") == 0)
1253 else if (strcmp(argv[i], "--") == 0)
1265 get_dir_list(argc, argv);
1272 targetshouldbedirectory = 1;
1274 if (colon(argv[argc-1]) != NULL)
1275 toremote(argc, argv);
1277 tolocal(argc, argv);
1280 if (back != NULL && back->socket() != NULL) {
1282 back->special(TS_EOF);
1283 ssh_scp_recv(&ch, 1);
1288 /* GUI Adaptation - August 2000 */
1290 unsigned int msg_id = WM_RET_ERR_CNT;
1291 if (list) msg_id = WM_LS_RET_ERR_CNT;
1292 while (!PostMessage( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)errs, 0/*lParam*/ ) )
1295 return (errs == 0 ? 0 : 1);