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_line(const char *prompt
, char *str
, int maxlen
, int is_pw
)
401 DWORD savemode
, newmode
, i
;
403 if (is_pw
&& password
) {
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 newmode
= savemode
| ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
;
428 newmode
&= ~ENABLE_ECHO_INPUT
;
430 newmode
|= ENABLE_ECHO_INPUT
;
431 SetConsoleMode(hin
, newmode
);
433 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
434 ReadFile(hin
, str
, maxlen
-1, &i
, NULL
);
436 SetConsoleMode(hin
, savemode
);
438 if ((int)i
> maxlen
) i
= maxlen
-1; else i
= i
- 2;
442 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
449 * Open an SSH connection to user@host and execute cmd.
451 static void do_cmd(char *host
, char *user
, char *cmd
)
453 char *err
, *realhost
;
456 if (host
== NULL
|| host
[0] == '\0')
457 bump("Empty host name");
459 /* Try to load settings for this host */
460 do_defaults(host
, &cfg
);
461 if (cfg
.host
[0] == '\0') {
462 /* No settings for this host; use defaults */
463 do_defaults(NULL
, &cfg
);
464 strncpy(cfg
.host
, host
, sizeof(cfg
.host
)-1);
465 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
470 if (user
!= NULL
&& user
[0] != '\0') {
471 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
472 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
473 } else if (cfg
.username
[0] == '\0') {
475 if (GetUserName(user
, &namelen
) == FALSE
)
476 bump("Empty user name");
477 user
= smalloc(namelen
* sizeof(char));
478 GetUserName(user
, &namelen
);
479 if (verbose
) tell_user(stderr
, "Guessing user name: %s", user
);
480 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
481 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
485 if (cfg
.protocol
!= PROT_SSH
)
489 cfg
.port
= portnumber
;
491 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
492 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
)-1] = '\0';
497 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
499 bump("ssh_init: %s", err
);
501 if (verbose
&& realhost
!= NULL
)
502 tell_user(stderr
, "Connected to %s\n", realhost
);
506 * Update statistic information about current file.
508 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
509 time_t start
, time_t now
)
516 /* GUI Adaptation - Sept 2000 */
518 gui_update_stats(name
, size
, (int)(100 * (done
*1.0/size
)),
519 (unsigned long)difftime(now
, start
));
522 ratebs
= (float) done
/ (now
- start
);
524 ratebs
= (float) done
;
529 eta
= (unsigned long) ((size
- done
) / ratebs
);
530 sprintf(etastr
, "%02ld:%02ld:%02ld",
531 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
533 pct
= (int) (100.0 * (float) done
/ size
);
535 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
536 name
, done
/ 1024, ratebs
/ 1024.0,
545 * Find a colon in str and return a pointer to the colon.
546 * This is used to separate hostname from filename.
548 static char * colon(char *str
)
550 /* We ignore a leading colon, since the hostname cannot be
551 empty. We also ignore a colon as second character because
552 of filenames like f:myfile.txt. */
553 if (str
[0] == '\0' ||
557 while (*str
!= '\0' &&
569 * Wait for a response from the other side.
570 * Return 0 if ok, -1 if error.
572 static int response(void)
574 char ch
, resp
, rbuf
[2048];
577 if (ssh_scp_recv(&resp
, 1) <= 0)
578 bump("Lost connection");
588 case 2: /* fatal error */
590 if (ssh_scp_recv(&ch
, 1) <= 0)
591 bump("Protocol error: Lost connection");
593 } while (p
< sizeof(rbuf
) && ch
!= '\n');
596 tell_user(stderr
, "%s\n", rbuf
);
605 * Send an error message to the other side and to the screen.
606 * Increment error counter.
608 static void run_err(const char *fmt
, ...)
614 strcpy(str
, "scp: ");
615 vsprintf(str
+strlen(str
), fmt
, ap
);
617 back
->send(str
, strlen(str
));
618 tell_user(stderr
, "%s",str
);
623 * Execute the source part of the SCP protocol.
625 static void source(char *src
)
633 unsigned long stat_bytes
;
634 time_t stat_starttime
, stat_lasttime
;
636 attr
= GetFileAttributes(src
);
637 if (attr
== (DWORD
)-1) {
638 run_err("%s: No such file or directory", src
);
642 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
645 * Avoid . and .. directories.
648 p
= strrchr(src
, '/');
650 p
= strrchr(src
, '\\');
655 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
660 run_err("%s: not a regular file", src
);
665 if ((last
= strrchr(src
, '/')) == NULL
)
669 if (strrchr(last
, '\\') != NULL
)
670 last
= strrchr(last
, '\\') + 1;
671 if (last
== src
&& strchr(src
, ':') != NULL
)
672 last
= strchr(src
, ':') + 1;
674 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
675 OPEN_EXISTING
, 0, 0);
676 if (f
== INVALID_HANDLE_VALUE
) {
677 run_err("%s: Cannot open file", src
);
682 FILETIME actime
, wrtime
;
683 unsigned long mtime
, atime
;
684 GetFileTime(f
, NULL
, &actime
, &wrtime
);
685 TIME_WIN_TO_POSIX(actime
, atime
);
686 TIME_WIN_TO_POSIX(wrtime
, mtime
);
687 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
688 back
->send(buf
, strlen(buf
));
693 size
= GetFileSize(f
, NULL
);
694 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
696 tell_user(stderr
, "Sending file modes: %s", buf
);
697 back
->send(buf
, strlen(buf
));
703 stat_starttime
= time(NULL
);
707 for (i
= 0; i
< size
; i
+= 4096) {
710 if (i
+ k
> size
) k
= size
- i
;
711 if (! ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
712 if (statistics
) printf("\n");
713 bump("%s: Read error", src
);
715 back
->send(transbuf
, k
);
718 if (time(NULL
) != stat_lasttime
||
720 stat_lasttime
= time(NULL
);
721 print_stats(last
, size
, stat_bytes
,
722 stat_starttime
, stat_lasttime
);
733 * Recursively send the contents of a directory.
735 static void rsource(char *src
)
740 WIN32_FIND_DATA fdat
;
743 if ((last
= strrchr(src
, '/')) == NULL
)
747 if (strrchr(last
, '\\') != NULL
)
748 last
= strrchr(last
, '\\') + 1;
749 if (last
== src
&& strchr(src
, ':') != NULL
)
750 last
= strchr(src
, ':') + 1;
752 /* maybe send filetime */
754 sprintf(buf
, "D0755 0 %s\n", last
);
756 tell_user(stderr
, "Entering directory: %s", buf
);
757 back
->send(buf
, strlen(buf
));
761 sprintf(buf
, "%s/*", src
);
762 dir
= FindFirstFile(buf
, &fdat
);
763 ok
= (dir
!= INVALID_HANDLE_VALUE
);
765 if (strcmp(fdat
.cFileName
, ".") == 0 ||
766 strcmp(fdat
.cFileName
, "..") == 0) {
767 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >=
769 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
771 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
774 ok
= FindNextFile(dir
, &fdat
);
779 back
->send(buf
, strlen(buf
));
784 * Execute the sink part of the SCP protocol.
786 static void sink(char *targ
, char *src
)
796 unsigned long mtime
, atime
;
798 unsigned long size
, i
;
800 unsigned long stat_bytes
;
801 time_t stat_starttime
, stat_lasttime
;
804 attr
= GetFileAttributes(targ
);
805 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
808 if (targetshouldbedirectory
&& !targisdir
)
809 bump("%s: Not a directory", targ
);
815 if (ssh_scp_recv(&ch
, 1) <= 0)
818 bump("Protocol error: Unexpected newline");
822 if (ssh_scp_recv(&ch
, 1) <= 0)
823 bump("Lost connection");
825 } while (i
< sizeof(buf
) && ch
!= '\n');
828 case '\01': /* error */
829 tell_user(stderr
, "%s\n", buf
+1);
832 case '\02': /* fatal error */
838 if (sscanf(buf
, "T%ld %*d %ld %*d",
839 &mtime
, &atime
) == 2) {
844 bump("Protocol error: Illegal time format");
849 bump("Protocol error: Expected control record");
852 if (sscanf(buf
+1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
853 bump("Protocol error: Illegal file descriptor format");
854 /* Security fix: ensure the file ends up where we asked for it. */
861 p
= namebuf
+ strlen(namebuf
);
862 while (p
> namebuf
&& p
[-1] != '/' && p
[-1] != '\\')
867 strcpy(namebuf
, targ
);
869 attr
= GetFileAttributes(namebuf
);
870 exists
= (attr
!= (DWORD
)-1);
873 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
874 run_err("%s: Not a directory", namebuf
);
878 if (! CreateDirectory(namebuf
, NULL
)) {
879 run_err("%s: Cannot create directory",
885 /* can we set the timestamp for directories ? */
889 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
890 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
891 if (f
== INVALID_HANDLE_VALUE
) {
892 run_err("%s: Cannot create file", namebuf
);
900 stat_starttime
= time(NULL
);
902 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
906 if (strrchr(stat_name
, '\\') != NULL
)
907 stat_name
= strrchr(stat_name
, '\\') + 1;
910 for (i
= 0; i
< size
; i
+= 4096) {
913 if (i
+ k
> size
) k
= size
- i
;
914 if (ssh_scp_recv(transbuf
, k
) == 0)
915 bump("Lost connection");
916 if (wrerror
) continue;
917 if (! WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
920 printf("\r%-25.25s | %50s\n",
922 "Write error.. waiting for end of file");
927 if (time(NULL
) > stat_lasttime
||
929 stat_lasttime
= time(NULL
);
930 print_stats(stat_name
, size
, stat_bytes
,
931 stat_starttime
, stat_lasttime
);
938 FILETIME actime
, wrtime
;
939 TIME_POSIX_TO_WIN(atime
, actime
);
940 TIME_POSIX_TO_WIN(mtime
, wrtime
);
941 SetFileTime(f
, NULL
, &actime
, &wrtime
);
946 run_err("%s: Write error", namebuf
);
954 * We will copy local files to a remote server.
956 static void toremote(int argc
, char *argv
[])
958 char *src
, *targ
, *host
, *user
;
964 /* Separate host from filename */
968 bump("targ == NULL in toremote()");
972 /* Substitute "." for emtpy target */
974 /* Separate host and username */
976 host
= strrchr(host
, '@');
987 /* Find out if the source filespec covers multiple files
988 if so, we should set the targetshouldbedirectory flag */
990 WIN32_FIND_DATA fdat
;
991 if (colon(argv
[0]) != NULL
)
992 bump("%s: Remote to remote not supported", argv
[0]);
993 fh
= FindFirstFile(argv
[0], &fdat
);
994 if (fh
== INVALID_HANDLE_VALUE
)
995 bump("%s: No such file or directory\n", argv
[0]);
996 if (FindNextFile(fh
, &fdat
))
997 targetshouldbedirectory
= 1;
1001 cmd
= smalloc(strlen(targ
) + 100);
1002 sprintf(cmd
, "scp%s%s%s%s -t %s",
1003 verbose ?
" -v" : "",
1004 recursive ?
" -r" : "",
1005 preserve ?
" -p" : "",
1006 targetshouldbedirectory ?
" -d" : "",
1008 do_cmd(host
, user
, cmd
);
1013 for (i
= 0; i
< argc
- 1; i
++) {
1015 WIN32_FIND_DATA fdat
;
1017 if (colon(src
) != NULL
) {
1018 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
1022 dir
= FindFirstFile(src
, &fdat
);
1023 if (dir
== INVALID_HANDLE_VALUE
) {
1024 run_err("%s: No such file or directory", src
);
1031 * Ensure that . and .. are never matched by wildcards,
1032 * but only by deliberate action.
1034 if (!strcmp(fdat
.cFileName
, ".") ||
1035 !strcmp(fdat
.cFileName
, "..")) {
1037 * Find*File has returned a special dir. We require
1038 * that _either_ `src' ends in a backslash followed
1039 * by that string, _or_ `src' is precisely that
1042 int len
= strlen(src
), dlen
= strlen(fdat
.cFileName
);
1043 if (len
== dlen
&& !strcmp(src
, fdat
.cFileName
)) {
1045 } else if (len
> dlen
+1 && src
[len
-dlen
-1] == '\\' &&
1046 !strcmp(src
+len
-dlen
, fdat
.cFileName
)) {
1049 continue; /* ignore this one */
1051 if (strlen(src
) + strlen(fdat
.cFileName
) >=
1053 tell_user(stderr
, "%s: Name too long", src
);
1056 strcpy(namebuf
, src
);
1057 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1061 if (strrchr(last
, '\\') != NULL
)
1062 last
= strrchr(last
, '\\') + 1;
1063 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1064 last
= strchr(namebuf
, ':') + 1;
1065 strcpy(last
, fdat
.cFileName
);
1067 } while (FindNextFile(dir
, &fdat
));
1073 * We will copy files from a remote server to the local machine.
1075 static void tolocal(int argc
, char *argv
[])
1077 char *src
, *targ
, *host
, *user
;
1081 bump("More than one remote source not supported");
1086 /* Separate host from filename */
1090 bump("Local to local copy not supported");
1094 /* Substitute "." for empty filename */
1096 /* Separate username and hostname */
1098 host
= strrchr(host
, '@');
1108 cmd
= smalloc(strlen(src
) + 100);
1109 sprintf(cmd
, "scp%s%s%s%s -f %s",
1110 verbose ?
" -v" : "",
1111 recursive ?
" -r" : "",
1112 preserve ?
" -p" : "",
1113 targetshouldbedirectory ?
" -d" : "",
1115 do_cmd(host
, user
, cmd
);
1122 * We will issue a list command to get a remote directory.
1124 static void get_dir_list(int argc
, char *argv
[])
1126 char *src
, *host
, *user
;
1132 /* Separate host from filename */
1136 bump("Local to local copy not supported");
1140 /* Substitute "." for empty filename */
1142 /* Separate username and hostname */
1144 host
= strrchr(host
, '@');
1154 cmd
= smalloc(4*strlen(src
) + 100);
1155 strcpy(cmd
, "ls -la '");
1156 p
= cmd
+ strlen(cmd
);
1157 for (q
= src
; *q
; q
++) {
1159 *p
++ = '\''; *p
++ = '\\'; *p
++ = '\''; *p
++ = '\'';
1167 do_cmd(host
, user
, cmd
);
1170 while (ssh_scp_recv(&c
, 1) > 0)
1171 tell_char(stdout
, c
);
1175 * Initialize the Win$ock driver.
1177 static void init_winsock(void)
1182 winsock_ver
= MAKEWORD(1, 1);
1183 if (WSAStartup(winsock_ver
, &wsadata
))
1184 bump("Unable to initialise WinSock");
1185 if (LOBYTE(wsadata
.wVersion
) != 1 ||
1186 HIBYTE(wsadata
.wVersion
) != 1)
1187 bump("WinSock version is incompatible with 1.1");
1191 * Short description of parameters.
1193 static void usage(void)
1195 printf("PuTTY Secure Copy client\n");
1196 printf("%s\n", ver
);
1197 printf("Usage: pscp [options] [user@]host:source target\n");
1198 printf(" pscp [options] source [source...] [user@]host:target\n");
1199 printf(" pscp [options] -ls user@host:filespec\n");
1200 printf("Options:\n");
1201 printf(" -p preserve file attributes\n");
1202 printf(" -q quiet, don't show statistics\n");
1203 printf(" -r copy directories recursively\n");
1204 printf(" -v show verbose messages\n");
1205 printf(" -P port connect to specified port\n");
1206 printf(" -pw passw login with specified password\n");
1209 * -gui is an internal option, used by GUI front ends to get
1210 * pscp to pass progress reports back to them. It's not an
1211 * ordinary user-accessible option, so it shouldn't be part of
1212 * the command-line help. The only people who need to know
1213 * about it are programmers, and they can read the source.
1215 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1221 * Main program (no, really?)
1223 int main(int argc
, char *argv
[])
1228 default_protocol
= PROT_TELNET
;
1230 flags
= FLAG_STDERR
;
1231 ssh_get_line
= &get_line
;
1235 for (i
= 1; i
< argc
; i
++) {
1236 if (argv
[i
][0] != '-')
1238 if (strcmp(argv
[i
], "-v") == 0)
1239 verbose
= 1, flags
|= FLAG_VERBOSE
;
1240 else if (strcmp(argv
[i
], "-r") == 0)
1242 else if (strcmp(argv
[i
], "-p") == 0)
1244 else if (strcmp(argv
[i
], "-q") == 0)
1246 else if (strcmp(argv
[i
], "-h") == 0 ||
1247 strcmp(argv
[i
], "-?") == 0)
1249 else if (strcmp(argv
[i
], "-P") == 0 && i
+1 < argc
)
1250 portnumber
= atoi(argv
[++i
]);
1251 else if (strcmp(argv
[i
], "-pw") == 0 && i
+1 < argc
)
1252 password
= argv
[++i
];
1253 else if (strcmp(argv
[i
], "-gui") == 0 && i
+1 < argc
) {
1254 gui_hwnd
= argv
[++i
];
1256 } else if (strcmp(argv
[i
], "-ls") == 0)
1258 else if (strcmp(argv
[i
], "--") == 0)
1270 get_dir_list(argc
, argv
);
1277 targetshouldbedirectory
= 1;
1279 if (colon(argv
[argc
-1]) != NULL
)
1280 toremote(argc
, argv
);
1282 tolocal(argc
, argv
);
1285 if (back
!= NULL
&& back
->socket() != NULL
) {
1287 back
->special(TS_EOF
);
1288 ssh_scp_recv(&ch
, 1);
1293 /* GUI Adaptation - August 2000 */
1295 unsigned int msg_id
= WM_RET_ERR_CNT
;
1296 if (list
) msg_id
= WM_LS_RET_ERR_CNT
;
1297 while (!PostMessage( (HWND
)atoi(gui_hwnd
), msg_id
, (WPARAM
)errs
, 0/*lParam*/ ) )
1300 return (errs
== 0 ?
0 : 1);