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
23 /* GUI Adaptation - Sept 2000 */
27 #define PUTTY_DO_GLOBALS
32 #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
33 ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
34 #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
35 ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
37 /* GUI Adaptation - Sept 2000 */
38 #define WM_APP_BASE 0x8000
39 #define WM_STD_OUT_CHAR ( WM_APP_BASE+400 )
40 #define WM_STD_ERR_CHAR ( WM_APP_BASE+401 )
41 #define WM_STATS_CHAR ( WM_APP_BASE+402 )
42 #define WM_STATS_SIZE ( WM_APP_BASE+403 )
43 #define WM_STATS_PERCENT ( WM_APP_BASE+404 )
44 #define WM_STATS_ELAPSED ( WM_APP_BASE+405 )
45 #define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
46 #define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
48 static int verbose = 0;
49 static int recursive = 0;
50 static int preserve = 0;
51 static int targetshouldbedirectory = 0;
52 static int statistics = 1;
53 static int portnumber = 0;
54 static char *password = NULL;
56 /* GUI Adaptation - Sept 2000 */
57 #define NAME_STR_MAX 2048
58 static char statname[NAME_STR_MAX+1];
59 static unsigned long statsize = 0;
60 static int statperct = 0;
61 static unsigned long statelapsed = 0;
62 static int gui_mode = 0;
63 static char *gui_hwnd = NULL;
65 static void source(char *src);
66 static void rsource(char *src);
67 static void sink(char *targ, char *src);
68 /* GUI Adaptation - Sept 2000 */
69 static void tell_char(FILE *stream, char c);
70 static void tell_str(FILE *stream, char *str);
71 static void tell_user(FILE *stream, char *fmt, ...);
72 static void send_char_msg(unsigned int msg_id, char c);
73 static void send_str_msg(unsigned int msg_id, char *str);
74 static void gui_update_stats(char *name, unsigned long size,
75 int percentage, unsigned long elapsed);
77 void begin_session(void) { }
78 void logevent(char *string) { }
80 void verify_ssh_host_key(char *host, int port, char *keytype,
81 char *keystr, char *fingerprint) {
84 static const char absentmsg[] =
85 "The server's host key is not cached in the registry. You\n"
86 "have no guarantee that the server is the computer you\n"
88 "The server's key fingerprint is:\n"
90 "If you trust this host, enter \"y\" to add the key to\n"
91 "PuTTY's cache and carry on connecting.\n"
92 "If you do not trust this host, enter \"n\" to abandon the\n"
94 "Continue connecting? (y/n) ";
96 static const char wrongmsg[] =
97 "WARNING - POTENTIAL SECURITY BREACH!\n"
98 "The server's host key does not match the one PuTTY has\n"
99 "cached in the registry. This means that either the\n"
100 "server administrator has changed the host key, or you\n"
101 "have actually connected to another computer pretending\n"
102 "to be the server.\n"
103 "The new key fingerprint is:\n"
105 "If you were expecting this change and trust the new key,\n"
106 "enter Yes to update PuTTY's cache and continue connecting.\n"
107 "If you want to carry on connecting but without updating\n"
108 "the cache, enter No.\n"
109 "If you want to abandon the connection completely, press\n"
110 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
112 "Update cached key? (y/n, Return cancels connection) ";
114 static const char abandoned[] = "Connection abandoned.\n";
119 * Verify the key against the registry.
121 ret = verify_host_key(host, port, keytype, keystr);
123 if (ret == 0) /* success - key matched OK */
125 if (ret == 2) { /* key was different */
126 fprintf(stderr, wrongmsg, fingerprint);
127 if (fgets(line, sizeof(line), stdin) &&
128 line[0] != '\0' && line[0] != '\n') {
129 if (line[0] == 'y' || line[0] == 'Y')
130 store_host_key(host, port, keytype, keystr);
132 fprintf(stderr, abandoned);
136 if (ret == 1) { /* key was absent */
137 fprintf(stderr, absentmsg, fingerprint);
138 if (fgets(line, sizeof(line), stdin) &&
139 (line[0] == 'y' || line[0] == 'Y'))
140 store_host_key(host, port, keytype, keystr);
142 fprintf(stderr, abandoned);
148 /* GUI Adaptation - Sept 2000 */
149 static void send_msg(HWND h, UINT message, WPARAM wParam)
151 while (!PostMessage( h, message, wParam, 0))
155 static void tell_char(FILE *stream, char c)
161 unsigned int msg_id = WM_STD_OUT_CHAR;
162 if (stream == stderr) msg_id = WM_STD_ERR_CHAR;
163 send_msg( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)c );
167 static void tell_str(FILE *stream, char *str)
171 for( i = 0; i < strlen(str); ++i )
172 tell_char(stream, str[i]);
175 static void tell_user(FILE *stream, char *fmt, ...)
177 char str[0x100]; /* Make the size big enough */
180 vsprintf(str, fmt, ap);
183 tell_str(stream, str);
186 static void gui_update_stats(char *name, unsigned long size, int percentage, unsigned long elapsed)
190 if (strcmp(name,statname) != 0)
192 for( i = 0; i < strlen(name); ++i )
193 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)name[i]);
194 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_CHAR, (WPARAM)'\n' );
195 strcpy(statname,name);
197 if (statsize != size)
199 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM)size );
202 if (statelapsed != elapsed)
204 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM)elapsed );
205 statelapsed = elapsed;
207 if (statperct != percentage)
209 send_msg( (HWND)atoi(gui_hwnd), WM_STATS_PERCENT, (WPARAM)percentage );
210 statperct = percentage;
215 * Print an error message and perform a fatal exit.
217 void fatalbox(char *fmt, ...)
219 char str[0x100]; /* Make the size big enough */
222 strcpy(str, "Fatal:");
223 vsprintf(str+strlen(str), fmt, ap);
226 tell_str(stderr, str);
230 void connection_fatal(char *fmt, ...)
232 char str[0x100]; /* Make the size big enough */
235 strcpy(str, "Fatal:");
236 vsprintf(str+strlen(str), fmt, ap);
239 tell_str(stderr, str);
245 * Be told what socket we're supposed to be using.
247 static SOCKET scp_ssh_socket;
248 char *do_select(SOCKET skt, int startup) {
250 scp_ssh_socket = skt;
252 scp_ssh_socket = INVALID_SOCKET;
255 extern int select_result(WPARAM, LPARAM);
258 * Receive a block of data from the SSH link. Block until all data
261 * To do this, we repeatedly call the SSH protocol module, with our
262 * own trap in from_backend() to catch the data that comes back. We
263 * do this until we have enough data.
266 static unsigned char *outptr; /* where to put the data */
267 static unsigned outlen; /* how much data required */
268 static unsigned char *pending = NULL; /* any spare data */
269 static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
270 void from_backend(int is_stderr, char *data, int datalen) {
271 unsigned char *p = (unsigned char *)data;
272 unsigned len = (unsigned)datalen;
275 * stderr data is just spouted to local stderr and otherwise
279 fwrite(data, 1, len, stderr);
286 * If this is before the real session begins, just return.
292 unsigned used = outlen;
293 if (used > len) used = len;
294 memcpy(outptr, p, used);
295 outptr += used; outlen -= used;
296 p += used; len -= used;
300 if (pendsize < pendlen + len) {
301 pendsize = pendlen + len + 4096;
302 pending = (pending ? srealloc(pending, pendsize) :
305 fatalbox("Out of memory");
307 memcpy(pending+pendlen, p, len);
311 static int ssh_scp_recv(unsigned char *buf, int len) {
316 * See if the pending-input block contains some of what we
320 unsigned pendused = pendlen;
321 if (pendused > outlen)
323 memcpy(outptr, pending, pendused);
324 memmove(pending, pending+pendused, pendlen-pendused);
341 FD_SET(scp_ssh_socket, &readfds);
342 if (select(1, &readfds, NULL, NULL, NULL) < 0)
344 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
351 * Loop through the ssh connection and authentication process.
353 static void ssh_scp_init(void) {
354 if (scp_ssh_socket == INVALID_SOCKET)
356 while (!back->sendok()) {
359 FD_SET(scp_ssh_socket, &readfds);
360 if (select(1, &readfds, NULL, NULL, NULL) < 0)
362 select_result((WPARAM)scp_ssh_socket, (LPARAM)FD_READ);
367 * Print an error message and exit after closing the SSH link.
369 static void bump(char *fmt, ...)
371 char str[0x100]; /* Make the size big enough */
374 strcpy(str, "Fatal:");
375 vsprintf(str+strlen(str), fmt, ap);
378 tell_str(stderr, str);
380 if (back != NULL && back->socket() != NULL) {
382 back->special(TS_EOF);
383 ssh_scp_recv(&ch, 1);
388 static int get_password(const char *prompt, char *str, int maxlen)
394 static int tried_once = 0;
399 strncpy(str, password, maxlen);
400 str[maxlen-1] = '\0';
406 /* GUI Adaptation - Sept 2000 */
408 if (maxlen>0) str[0] = '\0';
410 hin = GetStdHandle(STD_INPUT_HANDLE);
411 hout = GetStdHandle(STD_OUTPUT_HANDLE);
412 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE)
413 bump("Cannot get standard input/output handles");
415 GetConsoleMode(hin, &savemode);
416 SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
417 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
419 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
420 ReadFile(hin, str, maxlen-1, &i, NULL);
422 SetConsoleMode(hin, savemode);
424 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
427 WriteFile(hout, "\r\n", 2, &i, NULL);
434 * Open an SSH connection to user@host and execute cmd.
436 static void do_cmd(char *host, char *user, char *cmd)
438 char *err, *realhost;
441 if (host == NULL || host[0] == '\0')
442 bump("Empty host name");
444 /* Try to load settings for this host */
445 do_defaults(host, &cfg);
446 if (cfg.host[0] == '\0') {
447 /* No settings for this host; use defaults */
448 do_defaults(NULL, &cfg);
449 strncpy(cfg.host, host, sizeof(cfg.host)-1);
450 cfg.host[sizeof(cfg.host)-1] = '\0';
455 if (user != NULL && user[0] != '\0') {
456 strncpy(cfg.username, user, sizeof(cfg.username)-1);
457 cfg.username[sizeof(cfg.username)-1] = '\0';
458 } else if (cfg.username[0] == '\0') {
460 if (GetUserName(user, &namelen) == FALSE)
461 bump("Empty user name");
462 user = smalloc(namelen * sizeof(char));
463 GetUserName(user, &namelen);
464 if (verbose) tell_user(stderr, "Guessing user name: %s", user);
465 strncpy(cfg.username, user, sizeof(cfg.username)-1);
466 cfg.username[sizeof(cfg.username)-1] = '\0';
470 if (cfg.protocol != PROT_SSH)
474 cfg.port = portnumber;
476 strncpy(cfg.remote_cmd, cmd, sizeof(cfg.remote_cmd));
477 cfg.remote_cmd[sizeof(cfg.remote_cmd)-1] = '\0';
482 err = back->init(cfg.host, cfg.port, &realhost);
484 bump("ssh_init: %s", err);
486 if (verbose && realhost != NULL)
487 tell_user(stderr, "Connected to %s\n", realhost);
491 * Update statistic information about current file.
493 static void print_stats(char *name, unsigned long size, unsigned long done,
494 time_t start, time_t now)
501 /* GUI Adaptation - Sept 2000 */
503 gui_update_stats(name, size, (int)(100 * (done*1.0/size)),
504 (unsigned long)difftime(now, start));
507 ratebs = (float) done / (now - start);
509 ratebs = (float) done;
514 eta = (unsigned long) ((size - done) / ratebs);
515 sprintf(etastr, "%02ld:%02ld:%02ld",
516 eta / 3600, (eta % 3600) / 60, eta % 60);
518 pct = (int) (100.0 * (float) done / size);
520 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
521 name, done / 1024, ratebs / 1024.0,
530 * Find a colon in str and return a pointer to the colon.
531 * This is used to separate hostname from filename.
533 static char * colon(char *str)
535 /* We ignore a leading colon, since the hostname cannot be
536 empty. We also ignore a colon as second character because
537 of filenames like f:myfile.txt. */
538 if (str[0] == '\0' ||
542 while (*str != '\0' &&
554 * Wait for a response from the other side.
555 * Return 0 if ok, -1 if error.
557 static int response(void)
559 char ch, resp, rbuf[2048];
562 if (ssh_scp_recv(&resp, 1) <= 0)
563 bump("Lost connection");
573 case 2: /* fatal error */
575 if (ssh_scp_recv(&ch, 1) <= 0)
576 bump("Protocol error: Lost connection");
578 } while (p < sizeof(rbuf) && ch != '\n');
581 tell_user(stderr, "%s\n", rbuf);
590 * Send an error message to the other side and to the screen.
591 * Increment error counter.
593 static void run_err(const char *fmt, ...)
599 strcpy(str, "scp: ");
600 vsprintf(str+strlen(str), fmt, ap);
602 back->send(str, strlen(str));
603 tell_user(stderr, "%s",str);
608 * Execute the source part of the SCP protocol.
610 static void source(char *src)
618 unsigned long stat_bytes;
619 time_t stat_starttime, stat_lasttime;
621 attr = GetFileAttributes(src);
622 if (attr == (DWORD)-1) {
623 run_err("%s: No such file or directory", src);
627 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
630 * Avoid . and .. directories.
633 p = strrchr(src, '/');
635 p = strrchr(src, '\\');
640 if (!strcmp(p, ".") || !strcmp(p, ".."))
645 run_err("%s: not a regular file", src);
650 if ((last = strrchr(src, '/')) == NULL)
654 if (strrchr(last, '\\') != NULL)
655 last = strrchr(last, '\\') + 1;
656 if (last == src && strchr(src, ':') != NULL)
657 last = strchr(src, ':') + 1;
659 f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL,
660 OPEN_EXISTING, 0, 0);
661 if (f == INVALID_HANDLE_VALUE) {
662 run_err("%s: Cannot open file", src);
667 FILETIME actime, wrtime;
668 unsigned long mtime, atime;
669 GetFileTime(f, NULL, &actime, &wrtime);
670 TIME_WIN_TO_POSIX(actime, atime);
671 TIME_WIN_TO_POSIX(wrtime, mtime);
672 sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
673 back->send(buf, strlen(buf));
678 size = GetFileSize(f, NULL);
679 sprintf(buf, "C0644 %lu %s\n", size, last);
681 tell_user(stderr, "Sending file modes: %s", buf);
682 back->send(buf, strlen(buf));
688 stat_starttime = time(NULL);
692 for (i = 0; i < size; i += 4096) {
695 if (i + k > size) k = size - i;
696 if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) {
697 if (statistics) printf("\n");
698 bump("%s: Read error", src);
700 back->send(transbuf, k);
703 if (time(NULL) != stat_lasttime ||
705 stat_lasttime = time(NULL);
706 print_stats(last, size, stat_bytes,
707 stat_starttime, stat_lasttime);
718 * Recursively send the contents of a directory.
720 static void rsource(char *src)
725 WIN32_FIND_DATA fdat;
728 if ((last = strrchr(src, '/')) == NULL)
732 if (strrchr(last, '\\') != NULL)
733 last = strrchr(last, '\\') + 1;
734 if (last == src && strchr(src, ':') != NULL)
735 last = strchr(src, ':') + 1;
737 /* maybe send filetime */
739 sprintf(buf, "D0755 0 %s\n", last);
741 tell_user(stderr, "Entering directory: %s", buf);
742 back->send(buf, strlen(buf));
746 sprintf(buf, "%s/*", src);
747 dir = FindFirstFile(buf, &fdat);
748 ok = (dir != INVALID_HANDLE_VALUE);
750 if (strcmp(fdat.cFileName, ".") == 0 ||
751 strcmp(fdat.cFileName, "..") == 0) {
752 } else if (strlen(src) + 1 + strlen(fdat.cFileName) >=
754 run_err("%s/%s: Name too long", src, fdat.cFileName);
756 sprintf(buf, "%s/%s", src, fdat.cFileName);
759 ok = FindNextFile(dir, &fdat);
764 back->send(buf, strlen(buf));
769 * Execute the sink part of the SCP protocol.
771 static void sink(char *targ, char *src)
781 unsigned long mtime, atime;
783 unsigned long size, i;
785 unsigned long stat_bytes;
786 time_t stat_starttime, stat_lasttime;
789 attr = GetFileAttributes(targ);
790 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
793 if (targetshouldbedirectory && !targisdir)
794 bump("%s: Not a directory", targ);
800 if (ssh_scp_recv(&ch, 1) <= 0)
803 bump("Protocol error: Unexpected newline");
807 if (ssh_scp_recv(&ch, 1) <= 0)
808 bump("Lost connection");
810 } while (i < sizeof(buf) && ch != '\n');
813 case '\01': /* error */
814 tell_user(stderr, "%s\n", buf+1);
817 case '\02': /* fatal error */
823 if (sscanf(buf, "T%ld %*d %ld %*d",
824 &mtime, &atime) == 2) {
829 bump("Protocol error: Illegal time format");
834 bump("Protocol error: Expected control record");
837 if (sscanf(buf+1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
838 bump("Protocol error: Illegal file descriptor format");
839 /* Security fix: ensure the file ends up where we asked for it. */
846 p = namebuf + strlen(namebuf);
847 while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
852 strcpy(namebuf, targ);
854 attr = GetFileAttributes(namebuf);
855 exists = (attr != (DWORD)-1);
858 if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
859 run_err("%s: Not a directory", namebuf);
863 if (! CreateDirectory(namebuf, NULL)) {
864 run_err("%s: Cannot create directory",
870 /* can we set the timestamp for directories ? */
874 f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL,
875 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
876 if (f == INVALID_HANDLE_VALUE) {
877 run_err("%s: Cannot create file", namebuf);
885 stat_starttime = time(NULL);
887 if ((stat_name = strrchr(namebuf, '/')) == NULL)
891 if (strrchr(stat_name, '\\') != NULL)
892 stat_name = strrchr(stat_name, '\\') + 1;
895 for (i = 0; i < size; i += 4096) {
898 if (i + k > size) k = size - i;
899 if (ssh_scp_recv(transbuf, k) == 0)
900 bump("Lost connection");
901 if (wrerror) continue;
902 if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) {
905 printf("\r%-25.25s | %50s\n",
907 "Write error.. waiting for end of file");
912 if (time(NULL) > stat_lasttime ||
914 stat_lasttime = time(NULL);
915 print_stats(stat_name, size, stat_bytes,
916 stat_starttime, stat_lasttime);
923 FILETIME actime, wrtime;
924 TIME_POSIX_TO_WIN(atime, actime);
925 TIME_POSIX_TO_WIN(mtime, wrtime);
926 SetFileTime(f, NULL, &actime, &wrtime);
931 run_err("%s: Write error", namebuf);
939 * We will copy local files to a remote server.
941 static void toremote(int argc, char *argv[])
943 char *src, *targ, *host, *user;
949 /* Separate host from filename */
953 bump("targ == NULL in toremote()");
957 /* Substitute "." for emtpy target */
959 /* Separate host and username */
961 host = strrchr(host, '@');
972 /* Find out if the source filespec covers multiple files
973 if so, we should set the targetshouldbedirectory flag */
975 WIN32_FIND_DATA fdat;
976 if (colon(argv[0]) != NULL)
977 bump("%s: Remote to remote not supported", argv[0]);
978 fh = FindFirstFile(argv[0], &fdat);
979 if (fh == INVALID_HANDLE_VALUE)
980 bump("%s: No such file or directory\n", argv[0]);
981 if (FindNextFile(fh, &fdat))
982 targetshouldbedirectory = 1;
986 cmd = smalloc(strlen(targ) + 100);
987 sprintf(cmd, "scp%s%s%s%s -t %s",
988 verbose ? " -v" : "",
989 recursive ? " -r" : "",
990 preserve ? " -p" : "",
991 targetshouldbedirectory ? " -d" : "",
993 do_cmd(host, user, cmd);
998 for (i = 0; i < argc - 1; i++) {
1000 WIN32_FIND_DATA fdat;
1002 if (colon(src) != NULL) {
1003 tell_user(stderr, "%s: Remote to remote not supported\n", src);
1007 dir = FindFirstFile(src, &fdat);
1008 if (dir == INVALID_HANDLE_VALUE) {
1009 run_err("%s: No such file or directory", src);
1016 * Ensure that . and .. are never matched by wildcards,
1017 * but only by deliberate action.
1019 if (!strcmp(fdat.cFileName, ".") ||
1020 !strcmp(fdat.cFileName, "..")) {
1022 * Find*File has returned a special dir. We require
1023 * that _either_ `src' ends in a backslash followed
1024 * by that string, _or_ `src' is precisely that
1027 int len = strlen(src), dlen = strlen(fdat.cFileName);
1028 if (len == dlen && !strcmp(src, fdat.cFileName)) {
1030 } else if (len > dlen+1 && src[len-dlen-1] == '\\' &&
1031 !strcmp(src+len-dlen, fdat.cFileName)) {
1034 continue; /* ignore this one */
1036 if (strlen(src) + strlen(fdat.cFileName) >=
1038 tell_user(stderr, "%s: Name too long", src);
1041 strcpy(namebuf, src);
1042 if ((last = strrchr(namebuf, '/')) == NULL)
1046 if (strrchr(last, '\\') != NULL)
1047 last = strrchr(last, '\\') + 1;
1048 if (last == namebuf && strrchr(namebuf, ':') != NULL)
1049 last = strchr(namebuf, ':') + 1;
1050 strcpy(last, fdat.cFileName);
1052 } while (FindNextFile(dir, &fdat));
1058 * We will copy files from a remote server to the local machine.
1060 static void tolocal(int argc, char *argv[])
1062 char *src, *targ, *host, *user;
1066 bump("More than one remote source not supported");
1071 /* Separate host from filename */
1075 bump("Local to local copy not supported");
1079 /* Substitute "." for empty filename */
1081 /* Separate username and hostname */
1083 host = strrchr(host, '@');
1093 cmd = smalloc(strlen(src) + 100);
1094 sprintf(cmd, "scp%s%s%s%s -f %s",
1095 verbose ? " -v" : "",
1096 recursive ? " -r" : "",
1097 preserve ? " -p" : "",
1098 targetshouldbedirectory ? " -d" : "",
1100 do_cmd(host, user, cmd);
1107 * We will issue a list command to get a remote directory.
1109 static void get_dir_list(int argc, char *argv[])
1111 char *src, *host, *user;
1117 /* Separate host from filename */
1121 bump("Local to local copy not supported");
1125 /* Substitute "." for empty filename */
1127 /* Separate username and hostname */
1129 host = strrchr(host, '@');
1139 cmd = smalloc(4*strlen(src) + 100);
1140 strcpy(cmd, "ls -la '");
1141 p = cmd + strlen(cmd);
1142 for (q = src; *q; q++) {
1144 *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\'';
1152 do_cmd(host, user, cmd);
1155 while (ssh_scp_recv(&c, 1) > 0)
1156 tell_char(stdout, c);
1160 * Initialize the Win$ock driver.
1162 static void init_winsock(void)
1167 winsock_ver = MAKEWORD(1, 1);
1168 if (WSAStartup(winsock_ver, &wsadata))
1169 bump("Unable to initialise WinSock");
1170 if (LOBYTE(wsadata.wVersion) != 1 ||
1171 HIBYTE(wsadata.wVersion) != 1)
1172 bump("WinSock version is incompatible with 1.1");
1176 * Short description of parameters.
1178 static void usage(void)
1180 printf("PuTTY Secure Copy client\n");
1181 printf("%s\n", ver);
1182 printf("Usage: pscp [options] [user@]host:source target\n");
1183 printf(" pscp [options] source [source...] [user@]host:target\n");
1184 printf(" pscp [options] -ls user@host:filespec\n");
1185 printf("Options:\n");
1186 printf(" -p preserve file attributes\n");
1187 printf(" -q quiet, don't show statistics\n");
1188 printf(" -r copy directories recursively\n");
1189 printf(" -v show verbose messages\n");
1190 printf(" -P port connect to specified port\n");
1191 printf(" -pw passw login with specified password\n");
1192 /* GUI Adaptation - Sept 2000 */
1193 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1198 * Main program (no, really?)
1200 int main(int argc, char *argv[])
1205 default_protocol = PROT_TELNET;
1207 flags = FLAG_STDERR;
1208 ssh_get_password = &get_password;
1212 for (i = 1; i < argc; i++) {
1213 if (argv[i][0] != '-')
1215 if (strcmp(argv[i], "-v") == 0)
1216 verbose = 1, flags |= FLAG_VERBOSE;
1217 else if (strcmp(argv[i], "-r") == 0)
1219 else if (strcmp(argv[i], "-p") == 0)
1221 else if (strcmp(argv[i], "-q") == 0)
1223 else if (strcmp(argv[i], "-h") == 0 ||
1224 strcmp(argv[i], "-?") == 0)
1226 else if (strcmp(argv[i], "-P") == 0 && i+1 < argc)
1227 portnumber = atoi(argv[++i]);
1228 else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc)
1229 password = argv[++i];
1230 else if (strcmp(argv[i], "-gui") == 0 && i+1 < argc) {
1231 gui_hwnd = argv[++i];
1233 } else if (strcmp(argv[i], "-ls") == 0)
1235 else if (strcmp(argv[i], "--") == 0)
1247 get_dir_list(argc, argv);
1254 targetshouldbedirectory = 1;
1256 if (colon(argv[argc-1]) != NULL)
1257 toremote(argc, argv);
1259 tolocal(argc, argv);
1262 if (back != NULL && back->socket() != NULL) {
1264 back->special(TS_EOF);
1265 ssh_scp_recv(&ch, 1);
1270 /* GUI Adaptation - August 2000 */
1272 unsigned int msg_id = WM_RET_ERR_CNT;
1273 if (list) msg_id = WM_LS_RET_ERR_CNT;
1274 while (!PostMessage( (HWND)atoi(gui_hwnd), msg_id, (WPARAM)errs, 0/*lParam*/ ) )
1277 return (errs == 0 ? 0 : 1);