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 static int connection_open
= 0;
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 time_t 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
, int percentage
, time_t 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
, time_t 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 ?
realloc(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 (connection_open
) {
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
;
440 if (host
== NULL
|| host
[0] == '\0')
441 bump("Empty host name");
443 /* Try to load settings for this host */
444 do_defaults(host
, &cfg
);
445 if (cfg
.host
[0] == '\0') {
446 /* No settings for this host; use defaults */
447 strncpy(cfg
.host
, host
, sizeof(cfg
.host
)-1);
448 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
453 if (user
!= NULL
&& user
[0] != '\0') {
454 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
455 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
456 } else if (cfg
.username
[0] == '\0') {
457 bump("Empty user name");
460 if (cfg
.protocol
!= PROT_SSH
)
464 cfg
.port
= portnumber
;
466 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
467 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
)-1] = '\0';
472 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
474 bump("ssh_init: %s", err
);
476 if (verbose
&& realhost
!= NULL
)
477 tell_user(stderr
, "Connected to %s\n", realhost
);
483 * Update statistic information about current file.
485 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
486 time_t start
, time_t now
)
493 /* GUI Adaptation - Sept 2000 */
495 gui_update_stats(name
, size
, ((done
*100) / size
), now
-start
);
498 ratebs
= (float) done
/ (now
- start
);
500 ratebs
= (float) done
;
505 eta
= (unsigned long) ((size
- done
) / ratebs
);
506 sprintf(etastr
, "%02ld:%02ld:%02ld",
507 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
509 pct
= (int) (100.0 * (float) done
/ size
);
511 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
512 name
, done
/ 1024, ratebs
/ 1024.0,
521 * Find a colon in str and return a pointer to the colon.
522 * This is used to separate hostname from filename.
524 static char * colon(char *str
)
526 /* We ignore a leading colon, since the hostname cannot be
527 empty. We also ignore a colon as second character because
528 of filenames like f:myfile.txt. */
529 if (str
[0] == '\0' ||
533 while (*str
!= '\0' &&
545 * Wait for a response from the other side.
546 * Return 0 if ok, -1 if error.
548 static int response(void)
550 char ch
, resp
, rbuf
[2048];
553 if (ssh_scp_recv(&resp
, 1) <= 0)
554 bump("Lost connection");
564 case 2: /* fatal error */
566 if (ssh_scp_recv(&ch
, 1) <= 0)
567 bump("Protocol error: Lost connection");
569 } while (p
< sizeof(rbuf
) && ch
!= '\n');
572 tell_user(stderr
, "%s\n", rbuf
);
581 * Send an error message to the other side and to the screen.
582 * Increment error counter.
584 static void run_err(const char *fmt
, ...)
590 strcpy(str
, "scp: ");
591 vsprintf(str
+strlen(str
), fmt
, ap
);
593 back
->send(str
, strlen(str
));
594 tell_user(stderr
, "%s",str
);
599 * Execute the source part of the SCP protocol.
601 static void source(char *src
)
609 unsigned long stat_bytes
;
610 time_t stat_starttime
, stat_lasttime
;
612 attr
= GetFileAttributes(src
);
613 if (attr
== (DWORD
)-1) {
614 run_err("%s: No such file or directory", src
);
618 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
621 * Avoid . and .. directories.
624 p
= strrchr(src
, '/');
626 p
= strrchr(src
, '\\');
631 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
636 run_err("%s: not a regular file", src
);
641 if ((last
= strrchr(src
, '/')) == NULL
)
645 if (strrchr(last
, '\\') != NULL
)
646 last
= strrchr(last
, '\\') + 1;
647 if (last
== src
&& strchr(src
, ':') != NULL
)
648 last
= strchr(src
, ':') + 1;
650 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
651 OPEN_EXISTING
, 0, 0);
652 if (f
== INVALID_HANDLE_VALUE
) {
653 run_err("%s: Cannot open file", src
);
658 FILETIME actime
, wrtime
;
659 unsigned long mtime
, atime
;
660 GetFileTime(f
, NULL
, &actime
, &wrtime
);
661 TIME_WIN_TO_POSIX(actime
, atime
);
662 TIME_WIN_TO_POSIX(wrtime
, mtime
);
663 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
664 back
->send(buf
, strlen(buf
));
669 size
= GetFileSize(f
, NULL
);
670 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
672 tell_user(stderr
, "Sending file modes: %s", buf
);
673 back
->send(buf
, strlen(buf
));
679 stat_starttime
= time(NULL
);
683 for (i
= 0; i
< size
; i
+= 4096) {
686 if (i
+ k
> size
) k
= size
- i
;
687 if (! ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
688 if (statistics
) printf("\n");
689 bump("%s: Read error", src
);
691 back
->send(transbuf
, k
);
694 if (time(NULL
) != stat_lasttime
||
696 stat_lasttime
= time(NULL
);
697 print_stats(last
, size
, stat_bytes
,
698 stat_starttime
, stat_lasttime
);
709 * Recursively send the contents of a directory.
711 static void rsource(char *src
)
716 WIN32_FIND_DATA fdat
;
719 if ((last
= strrchr(src
, '/')) == NULL
)
723 if (strrchr(last
, '\\') != NULL
)
724 last
= strrchr(last
, '\\') + 1;
725 if (last
== src
&& strchr(src
, ':') != NULL
)
726 last
= strchr(src
, ':') + 1;
728 /* maybe send filetime */
730 sprintf(buf
, "D0755 0 %s\n", last
);
732 tell_user(stderr
, "Entering directory: %s", buf
);
733 back
->send(buf
, strlen(buf
));
737 sprintf(buf
, "%s/*", src
);
738 dir
= FindFirstFile(buf
, &fdat
);
739 ok
= (dir
!= INVALID_HANDLE_VALUE
);
741 if (strcmp(fdat
.cFileName
, ".") == 0 ||
742 strcmp(fdat
.cFileName
, "..") == 0) {
743 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >=
745 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
747 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
750 ok
= FindNextFile(dir
, &fdat
);
755 back
->send(buf
, strlen(buf
));
760 * Execute the sink part of the SCP protocol.
762 static void sink(char *targ
, char *src
)
772 unsigned long mtime
, atime
;
774 unsigned long size
, i
;
776 unsigned long stat_bytes
;
777 time_t stat_starttime
, stat_lasttime
;
780 attr
= GetFileAttributes(targ
);
781 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
784 if (targetshouldbedirectory
&& !targisdir
)
785 bump("%s: Not a directory", targ
);
791 if (ssh_scp_recv(&ch
, 1) <= 0)
794 bump("Protocol error: Unexpected newline");
798 if (ssh_scp_recv(&ch
, 1) <= 0)
799 bump("Lost connection");
801 } while (i
< sizeof(buf
) && ch
!= '\n');
804 case '\01': /* error */
805 tell_user(stderr
, "%s\n", buf
+1);
808 case '\02': /* fatal error */
814 if (sscanf(buf
, "T%ld %*d %ld %*d",
815 &mtime
, &atime
) == 2) {
820 bump("Protocol error: Illegal time format");
825 bump("Protocol error: Expected control record");
828 if (sscanf(buf
+1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
829 bump("Protocol error: Illegal file descriptor format");
830 /* Security fix: ensure the file ends up where we asked for it. */
832 char *p
= src
+ strlen(src
);
833 while (p
> src
&& p
[-1] != '/' && p
[-1] != '\\')
843 p
= namebuf
+ strlen(namebuf
);
844 while (p
> namebuf
&& p
[-1] != '/' && p
[-1] != '\\')
849 strcpy(namebuf
, targ
);
851 attr
= GetFileAttributes(namebuf
);
852 exists
= (attr
!= (DWORD
)-1);
855 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
856 run_err("%s: Not a directory", namebuf
);
860 if (! CreateDirectory(namebuf
, NULL
)) {
861 run_err("%s: Cannot create directory",
867 /* can we set the timestamp for directories ? */
871 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
872 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
873 if (f
== INVALID_HANDLE_VALUE
) {
874 run_err("%s: Cannot create file", namebuf
);
882 stat_starttime
= time(NULL
);
884 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
888 if (strrchr(stat_name
, '\\') != NULL
)
889 stat_name
= strrchr(stat_name
, '\\') + 1;
892 for (i
= 0; i
< size
; i
+= 4096) {
895 if (i
+ k
> size
) k
= size
- i
;
896 if (ssh_scp_recv(transbuf
, k
) == 0)
897 bump("Lost connection");
898 if (wrerror
) continue;
899 if (! WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
902 printf("\r%-25.25s | %50s\n",
904 "Write error.. waiting for end of file");
909 if (time(NULL
) > stat_lasttime
||
911 stat_lasttime
= time(NULL
);
912 print_stats(stat_name
, size
, stat_bytes
,
913 stat_starttime
, stat_lasttime
);
920 FILETIME actime
, wrtime
;
921 TIME_POSIX_TO_WIN(atime
, actime
);
922 TIME_POSIX_TO_WIN(mtime
, wrtime
);
923 SetFileTime(f
, NULL
, &actime
, &wrtime
);
928 run_err("%s: Write error", namebuf
);
936 * We will copy local files to a remote server.
938 static void toremote(int argc
, char *argv
[])
940 char *src
, *targ
, *host
, *user
;
946 /* Separate host from filename */
950 bump("targ == NULL in toremote()");
954 /* Substitute "." for emtpy target */
956 /* Separate host and username */
958 host
= strrchr(host
, '@');
969 /* Find out if the source filespec covers multiple files
970 if so, we should set the targetshouldbedirectory flag */
972 WIN32_FIND_DATA fdat
;
973 if (colon(argv
[0]) != NULL
)
974 bump("%s: Remote to remote not supported", argv
[0]);
975 fh
= FindFirstFile(argv
[0], &fdat
);
976 if (fh
== INVALID_HANDLE_VALUE
)
977 bump("%s: No such file or directory\n", argv
[0]);
978 if (FindNextFile(fh
, &fdat
))
979 targetshouldbedirectory
= 1;
983 cmd
= smalloc(strlen(targ
) + 100);
984 sprintf(cmd
, "scp%s%s%s%s -t %s",
985 verbose ?
" -v" : "",
986 recursive ?
" -r" : "",
987 preserve ?
" -p" : "",
988 targetshouldbedirectory ?
" -d" : "",
990 do_cmd(host
, user
, cmd
);
995 for (i
= 0; i
< argc
- 1; i
++) {
997 WIN32_FIND_DATA fdat
;
999 if (colon(src
) != NULL
) {
1000 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
1004 dir
= FindFirstFile(src
, &fdat
);
1005 if (dir
== INVALID_HANDLE_VALUE
) {
1006 run_err("%s: No such file or directory", src
);
1012 if (strlen(src
) + strlen(fdat
.cFileName
) >=
1014 tell_user(stderr
, "%s: Name too long", src
);
1017 strcpy(namebuf
, src
);
1018 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1022 if (strrchr(last
, '\\') != NULL
)
1023 last
= strrchr(last
, '\\') + 1;
1024 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1025 last
= strchr(namebuf
, ':') + 1;
1026 strcpy(last
, fdat
.cFileName
);
1028 } while (FindNextFile(dir
, &fdat
));
1034 * We will copy files from a remote server to the local machine.
1036 static void tolocal(int argc
, char *argv
[])
1038 char *src
, *targ
, *host
, *user
;
1042 bump("More than one remote source not supported");
1047 /* Separate host from filename */
1051 bump("Local to local copy not supported");
1055 /* Substitute "." for empty filename */
1057 /* Separate username and hostname */
1059 host
= strrchr(host
, '@');
1069 cmd
= smalloc(strlen(src
) + 100);
1070 sprintf(cmd
, "scp%s%s%s%s -f %s",
1071 verbose ?
" -v" : "",
1072 recursive ?
" -r" : "",
1073 preserve ?
" -p" : "",
1074 targetshouldbedirectory ?
" -d" : "",
1076 do_cmd(host
, user
, cmd
);
1083 * We will issue a list command to get a remote directory.
1085 static void get_dir_list(int argc
, char *argv
[])
1087 char *src
, *host
, *user
;
1093 /* Separate host from filename */
1097 bump("Local to local copy not supported");
1101 /* Substitute "." for empty filename */
1103 /* Separate username and hostname */
1105 host
= strrchr(host
, '@');
1115 cmd
= smalloc(4*strlen(src
) + 100);
1116 strcpy(cmd
, "ls -la '");
1117 p
= cmd
+ strlen(cmd
);
1118 for (q
= src
; *q
; q
++) {
1120 *p
++ = '\''; *p
++ = '\\'; *p
++ = '\''; *p
++ = '\'';
1128 do_cmd(host
, user
, cmd
);
1131 while (ssh_scp_recv(&c
, 1) > 0)
1132 tell_char(stdout
, c
);
1136 * Initialize the Win$ock driver.
1138 static void init_winsock(void)
1143 winsock_ver
= MAKEWORD(1, 1);
1144 if (WSAStartup(winsock_ver
, &wsadata
))
1145 bump("Unable to initialise WinSock");
1146 if (LOBYTE(wsadata
.wVersion
) != 1 ||
1147 HIBYTE(wsadata
.wVersion
) != 1)
1148 bump("WinSock version is incompatible with 1.1");
1152 * Short description of parameters.
1154 static void usage(void)
1156 printf("PuTTY Secure Copy client\n");
1157 printf("%s\n", ver
);
1158 printf("Usage: pscp [options] [user@]host:source target\n");
1159 printf(" pscp [options] source [source...] [user@]host:target\n");
1160 printf(" pscp [options] -ls user@host:filespec\n");
1161 printf("Options:\n");
1162 printf(" -p preserve file attributes\n");
1163 printf(" -q quiet, don't show statistics\n");
1164 printf(" -r copy directories recursively\n");
1165 printf(" -v show verbose messages\n");
1166 printf(" -P port connect to specified port\n");
1167 printf(" -pw passw login with specified password\n");
1168 /* GUI Adaptation - Sept 2000 */
1169 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1174 * Main program (no, really?)
1176 int main(int argc
, char *argv
[])
1181 default_protocol
= PROT_TELNET
;
1183 flags
= FLAG_STDERR
;
1184 ssh_get_password
= &get_password
;
1188 for (i
= 1; i
< argc
; i
++) {
1189 if (argv
[i
][0] != '-')
1191 if (strcmp(argv
[i
], "-v") == 0)
1192 verbose
= 1, flags
|= FLAG_VERBOSE
;
1193 else if (strcmp(argv
[i
], "-r") == 0)
1195 else if (strcmp(argv
[i
], "-p") == 0)
1197 else if (strcmp(argv
[i
], "-q") == 0)
1199 else if (strcmp(argv
[i
], "-h") == 0 ||
1200 strcmp(argv
[i
], "-?") == 0)
1202 else if (strcmp(argv
[i
], "-P") == 0 && i
+1 < argc
)
1203 portnumber
= atoi(argv
[++i
]);
1204 else if (strcmp(argv
[i
], "-pw") == 0 && i
+1 < argc
)
1205 password
= argv
[++i
];
1206 else if (strcmp(argv
[i
], "-gui") == 0 && i
+1 < argc
) {
1207 gui_hwnd
= argv
[++i
];
1209 } else if (strcmp(argv
[i
], "-ls") == 0)
1211 else if (strcmp(argv
[i
], "--") == 0)
1222 get_dir_list(argc
, argv
);
1229 targetshouldbedirectory
= 1;
1231 if (colon(argv
[argc
-1]) != NULL
)
1232 toremote(argc
, argv
);
1234 tolocal(argc
, argv
);
1237 if (connection_open
) {
1239 back
->special(TS_EOF
);
1240 ssh_scp_recv(&ch
, 1);
1245 /* GUI Adaptation - August 2000 */
1247 unsigned int msg_id
= WM_RET_ERR_CNT
;
1248 if (list
) msg_id
= WM_LS_RET_ERR_CNT
;
1249 while (!PostMessage( (HWND
)atoi(gui_hwnd
), msg_id
, (WPARAM
)errs
, 0/*lParam*/ ) )
1252 return (errs
== 0 ?
0 : 1);