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 send_char_msg(unsigned int msg_id
, char c
);
75 static void send_str_msg(unsigned int msg_id
, char *str
);
76 static void gui_update_stats(char *name
, unsigned long size
,
77 int percentage
, unsigned long elapsed
);
79 void logevent(char *string
)
83 void ldisc_send(char *buf
, int len
)
86 * This is only here because of the calls to ldisc_send(NULL,
87 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
88 * as an ldisc. So if we get called with any real data, I want
94 void verify_ssh_host_key(char *host
, int port
, char *keytype
,
95 char *keystr
, char *fingerprint
)
99 static const char absentmsg
[] =
100 "The server's host key is not cached in the registry. You\n"
101 "have no guarantee that the server is the computer you\n"
103 "The server's key fingerprint is:\n"
105 "If you trust this host, enter \"y\" to add the key to\n"
106 "PuTTY's cache and carry on connecting.\n"
107 "If you do not trust this host, enter \"n\" to abandon the\n"
108 "connection.\n" "Continue connecting? (y/n) ";
110 static const char wrongmsg
[] =
111 "WARNING - POTENTIAL SECURITY BREACH!\n"
112 "The server's host key does not match the one PuTTY has\n"
113 "cached in the registry. This means that either the\n"
114 "server administrator has changed the host key, or you\n"
115 "have actually connected to another computer pretending\n"
116 "to be the server.\n"
117 "The new key fingerprint is:\n"
119 "If you were expecting this change and trust the new key,\n"
120 "enter Yes to update PuTTY's cache and continue connecting.\n"
121 "If you want to carry on connecting but without updating\n"
122 "the cache, enter No.\n"
123 "If you want to abandon the connection completely, press\n"
124 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
126 "Update cached key? (y/n, Return cancels connection) ";
128 static const char abandoned
[] = "Connection abandoned.\n";
133 * Verify the key against the registry.
135 ret
= verify_host_key(host
, port
, keytype
, keystr
);
137 if (ret
== 0) /* success - key matched OK */
139 if (ret
== 2) { /* key was different */
140 fprintf(stderr
, wrongmsg
, fingerprint
);
142 if (fgets(line
, sizeof(line
), stdin
) &&
143 line
[0] != '\0' && line
[0] != '\n') {
144 if (line
[0] == 'y' || line
[0] == 'Y')
145 store_host_key(host
, port
, keytype
, keystr
);
147 fprintf(stderr
, abandoned
);
152 if (ret
== 1) { /* key was absent */
153 fprintf(stderr
, absentmsg
, fingerprint
);
154 if (fgets(line
, sizeof(line
), stdin
) &&
155 (line
[0] == 'y' || line
[0] == 'Y'))
156 store_host_key(host
, port
, keytype
, keystr
);
158 fprintf(stderr
, abandoned
);
164 /* GUI Adaptation - Sept 2000 */
165 static void send_msg(HWND h
, UINT message
, WPARAM wParam
)
167 while (!PostMessage(h
, message
, wParam
, 0))
171 static void tell_char(FILE * stream
, char c
)
176 unsigned int msg_id
= WM_STD_OUT_CHAR
;
177 if (stream
== stderr
)
178 msg_id
= WM_STD_ERR_CHAR
;
179 send_msg((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) c
);
183 static void tell_str(FILE * stream
, char *str
)
187 for (i
= 0; i
< strlen(str
); ++i
)
188 tell_char(stream
, str
[i
]);
191 static void tell_user(FILE * stream
, char *fmt
, ...)
193 char str
[0x100]; /* Make the size big enough */
196 vsprintf(str
, fmt
, ap
);
199 tell_str(stream
, str
);
202 static void gui_update_stats(char *name
, unsigned long size
,
203 int percentage
, unsigned long elapsed
)
207 if (strcmp(name
, statname
) != 0) {
208 for (i
= 0; i
< strlen(name
); ++i
)
209 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_CHAR
,
211 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_CHAR
, (WPARAM
) '\n');
212 strcpy(statname
, name
);
214 if (statsize
!= size
) {
215 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_SIZE
, (WPARAM
) size
);
218 if (statelapsed
!= elapsed
) {
219 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_ELAPSED
,
221 statelapsed
= elapsed
;
223 if (statperct
!= percentage
) {
224 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_PERCENT
,
225 (WPARAM
) percentage
);
226 statperct
= percentage
;
231 * Print an error message and perform a fatal exit.
233 void fatalbox(char *fmt
, ...)
235 char str
[0x100]; /* Make the size big enough */
238 strcpy(str
, "Fatal:");
239 vsprintf(str
+ strlen(str
), fmt
, ap
);
242 tell_str(stderr
, str
);
246 unsigned int msg_id
= WM_RET_ERR_CNT
;
248 msg_id
= WM_LS_RET_ERR_CNT
;
250 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
251 0 /*lParam */ ))SleepEx(1000, TRUE
);
256 void connection_fatal(char *fmt
, ...)
258 char str
[0x100]; /* Make the size big enough */
261 strcpy(str
, "Fatal:");
262 vsprintf(str
+ strlen(str
), fmt
, ap
);
265 tell_str(stderr
, str
);
269 unsigned int msg_id
= WM_RET_ERR_CNT
;
271 msg_id
= WM_LS_RET_ERR_CNT
;
273 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
274 0 /*lParam */ ))SleepEx(1000, TRUE
);
281 * Be told what socket we're supposed to be using.
283 static SOCKET scp_ssh_socket
;
284 char *do_select(SOCKET skt
, int startup
)
287 scp_ssh_socket
= skt
;
289 scp_ssh_socket
= INVALID_SOCKET
;
292 extern int select_result(WPARAM
, LPARAM
);
295 * Receive a block of data from the SSH link. Block until all data
298 * To do this, we repeatedly call the SSH protocol module, with our
299 * own trap in from_backend() to catch the data that comes back. We
300 * do this until we have enough data.
303 static unsigned char *outptr
; /* where to put the data */
304 static unsigned outlen
; /* how much data required */
305 static unsigned char *pending
= NULL
; /* any spare data */
306 static unsigned pendlen
= 0, pendsize
= 0; /* length and phys. size of buffer */
307 void from_backend(int is_stderr
, char *data
, int datalen
)
309 unsigned char *p
= (unsigned char *) data
;
310 unsigned len
= (unsigned) datalen
;
313 * stderr data is just spouted to local stderr and otherwise
317 fwrite(data
, 1, len
, stderr
);
324 * If this is before the real session begins, just return.
330 unsigned used
= outlen
;
333 memcpy(outptr
, p
, used
);
341 if (pendsize
< pendlen
+ len
) {
342 pendsize
= pendlen
+ len
+ 4096;
343 pending
= (pending ?
srealloc(pending
, pendsize
) :
346 fatalbox("Out of memory");
348 memcpy(pending
+ pendlen
, p
, len
);
352 static int ssh_scp_recv(unsigned char *buf
, int len
)
358 * See if the pending-input block contains some of what we
362 unsigned pendused
= pendlen
;
363 if (pendused
> outlen
)
365 memcpy(outptr
, pending
, pendused
);
366 memmove(pending
, pending
+ pendused
, pendlen
- pendused
);
383 FD_SET(scp_ssh_socket
, &readfds
);
384 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
386 select_result((WPARAM
) scp_ssh_socket
, (LPARAM
) FD_READ
);
393 * Loop through the ssh connection and authentication process.
395 static void ssh_scp_init(void)
397 if (scp_ssh_socket
== INVALID_SOCKET
)
399 while (!back
->sendok()) {
402 FD_SET(scp_ssh_socket
, &readfds
);
403 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
405 select_result((WPARAM
) scp_ssh_socket
, (LPARAM
) FD_READ
);
410 * Print an error message and exit after closing the SSH link.
412 static void bump(char *fmt
, ...)
414 char str
[0x100]; /* Make the size big enough */
417 strcpy(str
, "Fatal:");
418 vsprintf(str
+ strlen(str
), fmt
, ap
);
421 tell_str(stderr
, str
);
424 if (back
!= NULL
&& back
->socket() != NULL
) {
426 back
->special(TS_EOF
);
427 ssh_scp_recv(&ch
, 1);
431 unsigned int msg_id
= WM_RET_ERR_CNT
;
433 msg_id
= WM_LS_RET_ERR_CNT
;
435 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
436 0 /*lParam */ ))SleepEx(1000, TRUE
);
442 static int get_line(const char *prompt
, char *str
, int maxlen
, int is_pw
)
445 DWORD savemode
, newmode
, i
;
447 if (is_pw
&& password
) {
448 static int tried_once
= 0;
453 strncpy(str
, password
, maxlen
);
454 str
[maxlen
- 1] = '\0';
460 /* GUI Adaptation - Sept 2000 */
465 hin
= GetStdHandle(STD_INPUT_HANDLE
);
466 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
467 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
)
468 bump("Cannot get standard input/output handles");
470 GetConsoleMode(hin
, &savemode
);
471 newmode
= savemode
| ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
;
473 newmode
&= ~ENABLE_ECHO_INPUT
;
475 newmode
|= ENABLE_ECHO_INPUT
;
476 SetConsoleMode(hin
, newmode
);
478 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
479 ReadFile(hin
, str
, maxlen
- 1, &i
, NULL
);
481 SetConsoleMode(hin
, savemode
);
483 if ((int) i
> maxlen
)
490 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
497 * Open an SSH connection to user@host and execute cmd.
499 static void do_cmd(char *host
, char *user
, char *cmd
)
501 char *err
, *realhost
;
504 if (host
== NULL
|| host
[0] == '\0')
505 bump("Empty host name");
507 /* Try to load settings for this host */
508 do_defaults(host
, &cfg
);
509 if (cfg
.host
[0] == '\0') {
510 /* No settings for this host; use defaults */
511 do_defaults(NULL
, &cfg
);
512 strncpy(cfg
.host
, host
, sizeof(cfg
.host
) - 1);
513 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
518 if (user
!= NULL
&& user
[0] != '\0') {
519 strncpy(cfg
.username
, user
, sizeof(cfg
.username
) - 1);
520 cfg
.username
[sizeof(cfg
.username
) - 1] = '\0';
521 } else if (cfg
.username
[0] == '\0') {
523 if (GetUserName(user
, &namelen
) == FALSE
)
524 bump("Empty user name");
525 user
= smalloc(namelen
* sizeof(char));
526 GetUserName(user
, &namelen
);
528 tell_user(stderr
, "Guessing user name: %s", user
);
529 strncpy(cfg
.username
, user
, sizeof(cfg
.username
) - 1);
530 cfg
.username
[sizeof(cfg
.username
) - 1] = '\0';
534 if (cfg
.protocol
!= PROT_SSH
)
538 cfg
.port
= portnumber
;
540 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
541 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
) - 1] = '\0';
546 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
548 bump("ssh_init: %s", err
);
550 if (verbose
&& realhost
!= NULL
)
551 tell_user(stderr
, "Connected to %s\n", realhost
);
556 * Update statistic information about current file.
558 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
559 time_t start
, time_t now
)
566 /* GUI Adaptation - Sept 2000 */
568 gui_update_stats(name
, size
, (int) (100 * (done
* 1.0 / size
)),
569 (unsigned long) difftime(now
, start
));
572 ratebs
= (float) done
/ (now
- start
);
574 ratebs
= (float) done
;
579 eta
= (unsigned long) ((size
- done
) / ratebs
);
580 sprintf(etastr
, "%02ld:%02ld:%02ld",
581 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
583 pct
= (int) (100.0 * (float) done
/ size
);
585 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
586 name
, done
/ 1024, ratebs
/ 1024.0, etastr
, pct
);
594 * Find a colon in str and return a pointer to the colon.
595 * This is used to separate hostname from filename.
597 static char *colon(char *str
)
599 /* We ignore a leading colon, since the hostname cannot be
600 empty. We also ignore a colon as second character because
601 of filenames like f:myfile.txt. */
602 if (str
[0] == '\0' || str
[0] == ':' || str
[1] == ':')
604 while (*str
!= '\0' && *str
!= ':' && *str
!= '/' && *str
!= '\\')
613 * Wait for a response from the other side.
614 * Return 0 if ok, -1 if error.
616 static int response(void)
618 char ch
, resp
, rbuf
[2048];
621 if (ssh_scp_recv(&resp
, 1) <= 0)
622 bump("Lost connection");
632 case 2: /* fatal error */
634 if (ssh_scp_recv(&ch
, 1) <= 0)
635 bump("Protocol error: Lost connection");
637 } while (p
< sizeof(rbuf
) && ch
!= '\n');
640 tell_user(stderr
, "%s\n", rbuf
);
649 * Send an error message to the other side and to the screen.
650 * Increment error counter.
652 static void run_err(const char *fmt
, ...)
658 strcpy(str
, "scp: ");
659 vsprintf(str
+ strlen(str
), fmt
, ap
);
661 back
->send("\001", 1); /* scp protocol error prefix */
662 back
->send(str
, strlen(str
));
663 tell_user(stderr
, "%s", str
);
668 * Execute the source part of the SCP protocol.
670 static void source(char *src
)
678 unsigned long stat_bytes
;
679 time_t stat_starttime
, stat_lasttime
;
681 attr
= GetFileAttributes(src
);
682 if (attr
== (DWORD
) - 1) {
683 run_err("%s: No such file or directory", src
);
687 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
690 * Avoid . and .. directories.
693 p
= strrchr(src
, '/');
695 p
= strrchr(src
, '\\');
700 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
701 /* skip . and .. */ ;
705 run_err("%s: not a regular file", src
);
710 if ((last
= strrchr(src
, '/')) == NULL
)
714 if (strrchr(last
, '\\') != NULL
)
715 last
= strrchr(last
, '\\') + 1;
716 if (last
== src
&& strchr(src
, ':') != NULL
)
717 last
= strchr(src
, ':') + 1;
719 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
720 OPEN_EXISTING
, 0, 0);
721 if (f
== INVALID_HANDLE_VALUE
) {
722 run_err("%s: Cannot open file", src
);
727 FILETIME actime
, wrtime
;
728 unsigned long mtime
, atime
;
729 GetFileTime(f
, NULL
, &actime
, &wrtime
);
730 TIME_WIN_TO_POSIX(actime
, atime
);
731 TIME_WIN_TO_POSIX(wrtime
, mtime
);
732 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
733 back
->send(buf
, strlen(buf
));
738 size
= GetFileSize(f
, NULL
);
739 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
741 tell_user(stderr
, "Sending file modes: %s", buf
);
742 back
->send(buf
, strlen(buf
));
748 stat_starttime
= time(NULL
);
752 for (i
= 0; i
< size
; i
+= 4096) {
757 if (!ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
760 bump("%s: Read error", src
);
762 back
->send(transbuf
, k
);
765 if (time(NULL
) != stat_lasttime
|| i
+ k
== size
) {
766 stat_lasttime
= time(NULL
);
767 print_stats(last
, size
, stat_bytes
,
768 stat_starttime
, stat_lasttime
);
779 * Recursively send the contents of a directory.
781 static void rsource(char *src
)
786 WIN32_FIND_DATA fdat
;
789 if ((last
= strrchr(src
, '/')) == NULL
)
793 if (strrchr(last
, '\\') != NULL
)
794 last
= strrchr(last
, '\\') + 1;
795 if (last
== src
&& strchr(src
, ':') != NULL
)
796 last
= strchr(src
, ':') + 1;
798 /* maybe send filetime */
800 sprintf(buf
, "D0755 0 %s\n", last
);
802 tell_user(stderr
, "Entering directory: %s", buf
);
803 back
->send(buf
, strlen(buf
));
807 sprintf(buf
, "%s/*", src
);
808 dir
= FindFirstFile(buf
, &fdat
);
809 ok
= (dir
!= INVALID_HANDLE_VALUE
);
811 if (strcmp(fdat
.cFileName
, ".") == 0 ||
812 strcmp(fdat
.cFileName
, "..") == 0) {
813 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >= sizeof(buf
)) {
814 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
816 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
819 ok
= FindNextFile(dir
, &fdat
);
824 back
->send(buf
, strlen(buf
));
829 * Execute the sink part of the SCP protocol.
831 static void sink(char *targ
, char *src
)
841 unsigned long mtime
, atime
;
843 unsigned long size
, i
;
845 unsigned long stat_bytes
;
846 time_t stat_starttime
, stat_lasttime
;
849 attr
= GetFileAttributes(targ
);
850 if (attr
!= (DWORD
) - 1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
853 if (targetshouldbedirectory
&& !targisdir
)
854 bump("%s: Not a directory", targ
);
860 if (ssh_scp_recv(&ch
, 1) <= 0)
863 bump("Protocol error: Unexpected newline");
867 if (ssh_scp_recv(&ch
, 1) <= 0)
868 bump("Lost connection");
870 } while (i
< sizeof(buf
) && ch
!= '\n');
873 case '\01': /* error */
874 tell_user(stderr
, "%s\n", buf
+ 1);
877 case '\02': /* fatal error */
883 if (sscanf(buf
, "T%ld %*d %ld %*d", &mtime
, &atime
) == 2) {
888 bump("Protocol error: Illegal time format");
893 bump("Protocol error: Expected control record");
896 if (sscanf(buf
+ 1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
897 bump("Protocol error: Illegal file descriptor format");
898 /* Security fix: ensure the file ends up where we asked for it. */
905 p
= namebuf
+ strlen(namebuf
);
906 while (p
> namebuf
&& p
[-1] != '/' && p
[-1] != '\\')
911 strcpy(namebuf
, targ
);
913 attr
= GetFileAttributes(namebuf
);
914 exists
= (attr
!= (DWORD
) - 1);
917 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
918 run_err("%s: Not a directory", namebuf
);
922 if (!CreateDirectory(namebuf
, NULL
)) {
923 run_err("%s: Cannot create directory", namebuf
);
928 /* can we set the timestamp for directories ? */
932 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
933 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
934 if (f
== INVALID_HANDLE_VALUE
) {
935 run_err("%s: Cannot create file", namebuf
);
943 stat_starttime
= time(NULL
);
945 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
949 if (strrchr(stat_name
, '\\') != NULL
)
950 stat_name
= strrchr(stat_name
, '\\') + 1;
953 for (i
= 0; i
< size
; i
+= 4096) {
958 if (ssh_scp_recv(transbuf
, k
) == 0)
959 bump("Lost connection");
962 if (!WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
965 printf("\r%-25.25s | %50s\n",
967 "Write error.. waiting for end of file");
972 if (time(NULL
) > stat_lasttime
|| i
+ k
== size
) {
973 stat_lasttime
= time(NULL
);
974 print_stats(stat_name
, size
, stat_bytes
,
975 stat_starttime
, stat_lasttime
);
982 FILETIME actime
, wrtime
;
983 TIME_POSIX_TO_WIN(atime
, actime
);
984 TIME_POSIX_TO_WIN(mtime
, wrtime
);
985 SetFileTime(f
, NULL
, &actime
, &wrtime
);
990 run_err("%s: Write error", namebuf
);
998 * We will copy local files to a remote server.
1000 static void toremote(int argc
, char *argv
[])
1002 char *src
, *targ
, *host
, *user
;
1006 targ
= argv
[argc
- 1];
1008 /* Separate host from filename */
1012 bump("targ == NULL in toremote()");
1016 /* Substitute "." for emtpy target */
1018 /* Separate host and username */
1020 host
= strrchr(host
, '@');
1031 /* Find out if the source filespec covers multiple files
1032 if so, we should set the targetshouldbedirectory flag */
1034 WIN32_FIND_DATA fdat
;
1035 if (colon(argv
[0]) != NULL
)
1036 bump("%s: Remote to remote not supported", argv
[0]);
1037 fh
= FindFirstFile(argv
[0], &fdat
);
1038 if (fh
== INVALID_HANDLE_VALUE
)
1039 bump("%s: No such file or directory\n", argv
[0]);
1040 if (FindNextFile(fh
, &fdat
))
1041 targetshouldbedirectory
= 1;
1045 cmd
= smalloc(strlen(targ
) + 100);
1046 sprintf(cmd
, "scp%s%s%s%s -t %s",
1047 verbose ?
" -v" : "",
1048 recursive ?
" -r" : "",
1049 preserve ?
" -p" : "",
1050 targetshouldbedirectory ?
" -d" : "", targ
);
1051 do_cmd(host
, user
, cmd
);
1056 for (i
= 0; i
< argc
- 1; i
++) {
1058 WIN32_FIND_DATA fdat
;
1060 if (colon(src
) != NULL
) {
1061 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
1065 dir
= FindFirstFile(src
, &fdat
);
1066 if (dir
== INVALID_HANDLE_VALUE
) {
1067 run_err("%s: No such file or directory", src
);
1074 * Ensure that . and .. are never matched by wildcards,
1075 * but only by deliberate action.
1077 if (!strcmp(fdat
.cFileName
, ".") ||
1078 !strcmp(fdat
.cFileName
, "..")) {
1080 * Find*File has returned a special dir. We require
1081 * that _either_ `src' ends in a backslash followed
1082 * by that string, _or_ `src' is precisely that
1085 int len
= strlen(src
), dlen
= strlen(fdat
.cFileName
);
1086 if (len
== dlen
&& !strcmp(src
, fdat
.cFileName
)) {
1088 } else if (len
> dlen
+ 1 && src
[len
- dlen
- 1] == '\\' &&
1089 !strcmp(src
+ len
- dlen
, fdat
.cFileName
)) {
1092 continue; /* ignore this one */
1094 if (strlen(src
) + strlen(fdat
.cFileName
) >= sizeof(namebuf
)) {
1095 tell_user(stderr
, "%s: Name too long", src
);
1098 strcpy(namebuf
, src
);
1099 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1103 if (strrchr(last
, '\\') != NULL
)
1104 last
= strrchr(last
, '\\') + 1;
1105 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1106 last
= strchr(namebuf
, ':') + 1;
1107 strcpy(last
, fdat
.cFileName
);
1109 } while (FindNextFile(dir
, &fdat
));
1115 * We will copy files from a remote server to the local machine.
1117 static void tolocal(int argc
, char *argv
[])
1119 char *src
, *targ
, *host
, *user
;
1123 bump("More than one remote source not supported");
1128 /* Separate host from filename */
1132 bump("Local to local copy not supported");
1136 /* Substitute "." for empty filename */
1138 /* Separate username and hostname */
1140 host
= strrchr(host
, '@');
1150 cmd
= smalloc(strlen(src
) + 100);
1151 sprintf(cmd
, "scp%s%s%s%s -f %s",
1152 verbose ?
" -v" : "",
1153 recursive ?
" -r" : "",
1154 preserve ?
" -p" : "",
1155 targetshouldbedirectory ?
" -d" : "", src
);
1156 do_cmd(host
, user
, cmd
);
1163 * We will issue a list command to get a remote directory.
1165 static void get_dir_list(int argc
, char *argv
[])
1167 char *src
, *host
, *user
;
1173 /* Separate host from filename */
1177 bump("Local to local copy not supported");
1181 /* Substitute "." for empty filename */
1183 /* Separate username and hostname */
1185 host
= strrchr(host
, '@');
1195 cmd
= smalloc(4 * strlen(src
) + 100);
1196 strcpy(cmd
, "ls -la '");
1197 p
= cmd
+ strlen(cmd
);
1198 for (q
= src
; *q
; q
++) {
1211 do_cmd(host
, user
, cmd
);
1214 while (ssh_scp_recv(&c
, 1) > 0)
1215 tell_char(stdout
, c
);
1219 * Initialize the Win$ock driver.
1221 static void init_winsock(void)
1226 winsock_ver
= MAKEWORD(1, 1);
1227 if (WSAStartup(winsock_ver
, &wsadata
))
1228 bump("Unable to initialise WinSock");
1229 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1)
1230 bump("WinSock version is incompatible with 1.1");
1234 * Short description of parameters.
1236 static void usage(void)
1238 printf("PuTTY Secure Copy client\n");
1239 printf("%s\n", ver
);
1240 printf("Usage: pscp [options] [user@]host:source target\n");
1242 (" pscp [options] source [source...] [user@]host:target\n");
1243 printf(" pscp [options] -ls user@host:filespec\n");
1244 printf("Options:\n");
1245 printf(" -p preserve file attributes\n");
1246 printf(" -q quiet, don't show statistics\n");
1247 printf(" -r copy directories recursively\n");
1248 printf(" -v show verbose messages\n");
1249 printf(" -P port connect to specified port\n");
1250 printf(" -pw passw login with specified password\n");
1253 * -gui is an internal option, used by GUI front ends to get
1254 * pscp to pass progress reports back to them. It's not an
1255 * ordinary user-accessible option, so it shouldn't be part of
1256 * the command-line help. The only people who need to know
1257 * about it are programmers, and they can read the source.
1260 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1266 * Main program (no, really?)
1268 int main(int argc
, char *argv
[])
1272 default_protocol
= PROT_TELNET
;
1274 flags
= FLAG_STDERR
;
1275 ssh_get_line
= &get_line
;
1279 for (i
= 1; i
< argc
; i
++) {
1280 if (argv
[i
][0] != '-')
1282 if (strcmp(argv
[i
], "-v") == 0)
1283 verbose
= 1, flags
|= FLAG_VERBOSE
;
1284 else if (strcmp(argv
[i
], "-r") == 0)
1286 else if (strcmp(argv
[i
], "-p") == 0)
1288 else if (strcmp(argv
[i
], "-q") == 0)
1290 else if (strcmp(argv
[i
], "-h") == 0 || strcmp(argv
[i
], "-?") == 0)
1292 else if (strcmp(argv
[i
], "-P") == 0 && i
+ 1 < argc
)
1293 portnumber
= atoi(argv
[++i
]);
1294 else if (strcmp(argv
[i
], "-pw") == 0 && i
+ 1 < argc
)
1295 password
= argv
[++i
];
1296 else if (strcmp(argv
[i
], "-gui") == 0 && i
+ 1 < argc
) {
1297 gui_hwnd
= argv
[++i
];
1299 } else if (strcmp(argv
[i
], "-ls") == 0)
1301 else if (strcmp(argv
[i
], "--") == 0) {
1314 get_dir_list(argc
, argv
);
1321 targetshouldbedirectory
= 1;
1323 if (colon(argv
[argc
- 1]) != NULL
)
1324 toremote(argc
, argv
);
1326 tolocal(argc
, argv
);
1329 if (back
!= NULL
&& back
->socket() != NULL
) {
1331 back
->special(TS_EOF
);
1332 ssh_scp_recv(&ch
, 1);
1337 /* GUI Adaptation - August 2000 */
1339 unsigned int msg_id
= WM_RET_ERR_CNT
;
1341 msg_id
= WM_LS_RET_ERR_CNT
;
1343 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
1344 0 /*lParam */ ))SleepEx(1000, TRUE
);
1346 return (errs
== 0 ?
0 : 1);