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
);
138 if (fgets(line
, sizeof(line
), stdin
) &&
139 line
[0] != '\0' && line
[0] != '\n') {
140 if (line
[0] == 'y' || line
[0] == 'Y')
141 store_host_key(host
, port
, keytype
, keystr
);
143 fprintf(stderr
, abandoned
);
148 if (ret
== 1) { /* key was absent */
149 fprintf(stderr
, absentmsg
, fingerprint
);
150 if (fgets(line
, sizeof(line
), stdin
) &&
151 (line
[0] == 'y' || line
[0] == 'Y'))
152 store_host_key(host
, port
, keytype
, keystr
);
154 fprintf(stderr
, abandoned
);
160 /* GUI Adaptation - Sept 2000 */
161 static void send_msg(HWND h
, UINT message
, WPARAM wParam
)
163 while (!PostMessage( h
, message
, wParam
, 0))
167 static void tell_char(FILE *stream
, char c
)
173 unsigned int msg_id
= WM_STD_OUT_CHAR
;
174 if (stream
== stderr
) msg_id
= WM_STD_ERR_CHAR
;
175 send_msg( (HWND
)atoi(gui_hwnd
), msg_id
, (WPARAM
)c
);
179 static void tell_str(FILE *stream
, char *str
)
183 for( i
= 0; i
< strlen(str
); ++i
)
184 tell_char(stream
, str
[i
]);
187 static void tell_user(FILE *stream
, char *fmt
, ...)
189 char str
[0x100]; /* Make the size big enough */
192 vsprintf(str
, fmt
, ap
);
195 tell_str(stream
, str
);
198 static void gui_update_stats(char *name
, unsigned long size
, int percentage
, unsigned long elapsed
)
202 if (strcmp(name
,statname
) != 0)
204 for( i
= 0; i
< strlen(name
); ++i
)
205 send_msg( (HWND
)atoi(gui_hwnd
), WM_STATS_CHAR
, (WPARAM
)name
[i
]);
206 send_msg( (HWND
)atoi(gui_hwnd
), WM_STATS_CHAR
, (WPARAM
)'\n' );
207 strcpy(statname
,name
);
209 if (statsize
!= size
)
211 send_msg( (HWND
)atoi(gui_hwnd
), WM_STATS_SIZE
, (WPARAM
)size
);
214 if (statelapsed
!= elapsed
)
216 send_msg( (HWND
)atoi(gui_hwnd
), WM_STATS_ELAPSED
, (WPARAM
)elapsed
);
217 statelapsed
= elapsed
;
219 if (statperct
!= percentage
)
221 send_msg( (HWND
)atoi(gui_hwnd
), WM_STATS_PERCENT
, (WPARAM
)percentage
);
222 statperct
= percentage
;
227 * Print an error message and perform a fatal exit.
229 void fatalbox(char *fmt
, ...)
231 char str
[0x100]; /* Make the size big enough */
234 strcpy(str
, "Fatal:");
235 vsprintf(str
+strlen(str
), fmt
, ap
);
238 tell_str(stderr
, str
);
242 void connection_fatal(char *fmt
, ...)
244 char str
[0x100]; /* Make the size big enough */
247 strcpy(str
, "Fatal:");
248 vsprintf(str
+strlen(str
), fmt
, ap
);
251 tell_str(stderr
, str
);
257 * Be told what socket we're supposed to be using.
259 static SOCKET scp_ssh_socket
;
260 char *do_select(SOCKET skt
, int startup
) {
262 scp_ssh_socket
= skt
;
264 scp_ssh_socket
= INVALID_SOCKET
;
267 extern int select_result(WPARAM
, LPARAM
);
270 * Receive a block of data from the SSH link. Block until all data
273 * To do this, we repeatedly call the SSH protocol module, with our
274 * own trap in from_backend() to catch the data that comes back. We
275 * do this until we have enough data.
278 static unsigned char *outptr
; /* where to put the data */
279 static unsigned outlen
; /* how much data required */
280 static unsigned char *pending
= NULL
; /* any spare data */
281 static unsigned pendlen
=0, pendsize
=0; /* length and phys. size of buffer */
282 void from_backend(int is_stderr
, char *data
, int datalen
) {
283 unsigned char *p
= (unsigned char *)data
;
284 unsigned len
= (unsigned)datalen
;
287 * stderr data is just spouted to local stderr and otherwise
291 fwrite(data
, 1, len
, stderr
);
298 * If this is before the real session begins, just return.
304 unsigned used
= outlen
;
305 if (used
> len
) used
= len
;
306 memcpy(outptr
, p
, used
);
307 outptr
+= used
; outlen
-= used
;
308 p
+= used
; len
-= used
;
312 if (pendsize
< pendlen
+ len
) {
313 pendsize
= pendlen
+ len
+ 4096;
314 pending
= (pending ?
srealloc(pending
, pendsize
) :
317 fatalbox("Out of memory");
319 memcpy(pending
+pendlen
, p
, len
);
323 static int ssh_scp_recv(unsigned char *buf
, int len
) {
328 * See if the pending-input block contains some of what we
332 unsigned pendused
= pendlen
;
333 if (pendused
> outlen
)
335 memcpy(outptr
, pending
, pendused
);
336 memmove(pending
, pending
+pendused
, pendlen
-pendused
);
353 FD_SET(scp_ssh_socket
, &readfds
);
354 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
356 select_result((WPARAM
)scp_ssh_socket
, (LPARAM
)FD_READ
);
363 * Loop through the ssh connection and authentication process.
365 static void ssh_scp_init(void) {
366 if (scp_ssh_socket
== INVALID_SOCKET
)
368 while (!back
->sendok()) {
371 FD_SET(scp_ssh_socket
, &readfds
);
372 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
374 select_result((WPARAM
)scp_ssh_socket
, (LPARAM
)FD_READ
);
379 * Print an error message and exit after closing the SSH link.
381 static void bump(char *fmt
, ...)
383 char str
[0x100]; /* Make the size big enough */
386 strcpy(str
, "Fatal:");
387 vsprintf(str
+strlen(str
), fmt
, ap
);
390 tell_str(stderr
, str
);
392 if (back
!= NULL
&& back
->socket() != NULL
) {
394 back
->special(TS_EOF
);
395 ssh_scp_recv(&ch
, 1);
400 static int get_line(const char *prompt
, char *str
, int maxlen
, int is_pw
)
403 DWORD savemode
, newmode
, i
;
405 if (is_pw
&& password
) {
406 static int tried_once
= 0;
411 strncpy(str
, password
, maxlen
);
412 str
[maxlen
-1] = '\0';
418 /* GUI Adaptation - Sept 2000 */
420 if (maxlen
>0) str
[0] = '\0';
422 hin
= GetStdHandle(STD_INPUT_HANDLE
);
423 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
424 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
)
425 bump("Cannot get standard input/output handles");
427 GetConsoleMode(hin
, &savemode
);
428 newmode
= savemode
| ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
;
430 newmode
&= ~ENABLE_ECHO_INPUT
;
432 newmode
|= ENABLE_ECHO_INPUT
;
433 SetConsoleMode(hin
, newmode
);
435 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
436 ReadFile(hin
, str
, maxlen
-1, &i
, NULL
);
438 SetConsoleMode(hin
, savemode
);
440 if ((int)i
> maxlen
) i
= maxlen
-1; else i
= i
- 2;
444 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
451 * Open an SSH connection to user@host and execute cmd.
453 static void do_cmd(char *host
, char *user
, char *cmd
)
455 char *err
, *realhost
;
458 if (host
== NULL
|| host
[0] == '\0')
459 bump("Empty host name");
461 /* Try to load settings for this host */
462 do_defaults(host
, &cfg
);
463 if (cfg
.host
[0] == '\0') {
464 /* No settings for this host; use defaults */
465 do_defaults(NULL
, &cfg
);
466 strncpy(cfg
.host
, host
, sizeof(cfg
.host
)-1);
467 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
472 if (user
!= NULL
&& user
[0] != '\0') {
473 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
474 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
475 } else if (cfg
.username
[0] == '\0') {
477 if (GetUserName(user
, &namelen
) == FALSE
)
478 bump("Empty user name");
479 user
= smalloc(namelen
* sizeof(char));
480 GetUserName(user
, &namelen
);
481 if (verbose
) tell_user(stderr
, "Guessing user name: %s", user
);
482 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
483 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
487 if (cfg
.protocol
!= PROT_SSH
)
491 cfg
.port
= portnumber
;
493 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
494 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
)-1] = '\0';
499 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
501 bump("ssh_init: %s", err
);
503 if (verbose
&& realhost
!= NULL
)
504 tell_user(stderr
, "Connected to %s\n", realhost
);
508 * Update statistic information about current file.
510 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
511 time_t start
, time_t now
)
518 /* GUI Adaptation - Sept 2000 */
520 gui_update_stats(name
, size
, (int)(100 * (done
*1.0/size
)),
521 (unsigned long)difftime(now
, start
));
524 ratebs
= (float) done
/ (now
- start
);
526 ratebs
= (float) done
;
531 eta
= (unsigned long) ((size
- done
) / ratebs
);
532 sprintf(etastr
, "%02ld:%02ld:%02ld",
533 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
535 pct
= (int) (100.0 * (float) done
/ size
);
537 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
538 name
, done
/ 1024, ratebs
/ 1024.0,
547 * Find a colon in str and return a pointer to the colon.
548 * This is used to separate hostname from filename.
550 static char * colon(char *str
)
552 /* We ignore a leading colon, since the hostname cannot be
553 empty. We also ignore a colon as second character because
554 of filenames like f:myfile.txt. */
555 if (str
[0] == '\0' ||
559 while (*str
!= '\0' &&
571 * Wait for a response from the other side.
572 * Return 0 if ok, -1 if error.
574 static int response(void)
576 char ch
, resp
, rbuf
[2048];
579 if (ssh_scp_recv(&resp
, 1) <= 0)
580 bump("Lost connection");
590 case 2: /* fatal error */
592 if (ssh_scp_recv(&ch
, 1) <= 0)
593 bump("Protocol error: Lost connection");
595 } while (p
< sizeof(rbuf
) && ch
!= '\n');
598 tell_user(stderr
, "%s\n", rbuf
);
607 * Send an error message to the other side and to the screen.
608 * Increment error counter.
610 static void run_err(const char *fmt
, ...)
616 strcpy(str
, "scp: ");
617 vsprintf(str
+strlen(str
), fmt
, ap
);
619 back
->send(str
, strlen(str
));
620 tell_user(stderr
, "%s",str
);
625 * Execute the source part of the SCP protocol.
627 static void source(char *src
)
635 unsigned long stat_bytes
;
636 time_t stat_starttime
, stat_lasttime
;
638 attr
= GetFileAttributes(src
);
639 if (attr
== (DWORD
)-1) {
640 run_err("%s: No such file or directory", src
);
644 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
647 * Avoid . and .. directories.
650 p
= strrchr(src
, '/');
652 p
= strrchr(src
, '\\');
657 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
662 run_err("%s: not a regular file", src
);
667 if ((last
= strrchr(src
, '/')) == NULL
)
671 if (strrchr(last
, '\\') != NULL
)
672 last
= strrchr(last
, '\\') + 1;
673 if (last
== src
&& strchr(src
, ':') != NULL
)
674 last
= strchr(src
, ':') + 1;
676 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
677 OPEN_EXISTING
, 0, 0);
678 if (f
== INVALID_HANDLE_VALUE
) {
679 run_err("%s: Cannot open file", src
);
684 FILETIME actime
, wrtime
;
685 unsigned long mtime
, atime
;
686 GetFileTime(f
, NULL
, &actime
, &wrtime
);
687 TIME_WIN_TO_POSIX(actime
, atime
);
688 TIME_WIN_TO_POSIX(wrtime
, mtime
);
689 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
690 back
->send(buf
, strlen(buf
));
695 size
= GetFileSize(f
, NULL
);
696 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
698 tell_user(stderr
, "Sending file modes: %s", buf
);
699 back
->send(buf
, strlen(buf
));
705 stat_starttime
= time(NULL
);
709 for (i
= 0; i
< size
; i
+= 4096) {
712 if (i
+ k
> size
) k
= size
- i
;
713 if (! ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
714 if (statistics
) printf("\n");
715 bump("%s: Read error", src
);
717 back
->send(transbuf
, k
);
720 if (time(NULL
) != stat_lasttime
||
722 stat_lasttime
= time(NULL
);
723 print_stats(last
, size
, stat_bytes
,
724 stat_starttime
, stat_lasttime
);
735 * Recursively send the contents of a directory.
737 static void rsource(char *src
)
742 WIN32_FIND_DATA fdat
;
745 if ((last
= strrchr(src
, '/')) == NULL
)
749 if (strrchr(last
, '\\') != NULL
)
750 last
= strrchr(last
, '\\') + 1;
751 if (last
== src
&& strchr(src
, ':') != NULL
)
752 last
= strchr(src
, ':') + 1;
754 /* maybe send filetime */
756 sprintf(buf
, "D0755 0 %s\n", last
);
758 tell_user(stderr
, "Entering directory: %s", buf
);
759 back
->send(buf
, strlen(buf
));
763 sprintf(buf
, "%s/*", src
);
764 dir
= FindFirstFile(buf
, &fdat
);
765 ok
= (dir
!= INVALID_HANDLE_VALUE
);
767 if (strcmp(fdat
.cFileName
, ".") == 0 ||
768 strcmp(fdat
.cFileName
, "..") == 0) {
769 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >=
771 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
773 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
776 ok
= FindNextFile(dir
, &fdat
);
781 back
->send(buf
, strlen(buf
));
786 * Execute the sink part of the SCP protocol.
788 static void sink(char *targ
, char *src
)
798 unsigned long mtime
, atime
;
800 unsigned long size
, i
;
802 unsigned long stat_bytes
;
803 time_t stat_starttime
, stat_lasttime
;
806 attr
= GetFileAttributes(targ
);
807 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
810 if (targetshouldbedirectory
&& !targisdir
)
811 bump("%s: Not a directory", targ
);
817 if (ssh_scp_recv(&ch
, 1) <= 0)
820 bump("Protocol error: Unexpected newline");
824 if (ssh_scp_recv(&ch
, 1) <= 0)
825 bump("Lost connection");
827 } while (i
< sizeof(buf
) && ch
!= '\n');
830 case '\01': /* error */
831 tell_user(stderr
, "%s\n", buf
+1);
834 case '\02': /* fatal error */
840 if (sscanf(buf
, "T%ld %*d %ld %*d",
841 &mtime
, &atime
) == 2) {
846 bump("Protocol error: Illegal time format");
851 bump("Protocol error: Expected control record");
854 if (sscanf(buf
+1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
855 bump("Protocol error: Illegal file descriptor format");
856 /* Security fix: ensure the file ends up where we asked for it. */
863 p
= namebuf
+ strlen(namebuf
);
864 while (p
> namebuf
&& p
[-1] != '/' && p
[-1] != '\\')
869 strcpy(namebuf
, targ
);
871 attr
= GetFileAttributes(namebuf
);
872 exists
= (attr
!= (DWORD
)-1);
875 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
876 run_err("%s: Not a directory", namebuf
);
880 if (! CreateDirectory(namebuf
, NULL
)) {
881 run_err("%s: Cannot create directory",
887 /* can we set the timestamp for directories ? */
891 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
892 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
893 if (f
== INVALID_HANDLE_VALUE
) {
894 run_err("%s: Cannot create file", namebuf
);
902 stat_starttime
= time(NULL
);
904 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
908 if (strrchr(stat_name
, '\\') != NULL
)
909 stat_name
= strrchr(stat_name
, '\\') + 1;
912 for (i
= 0; i
< size
; i
+= 4096) {
915 if (i
+ k
> size
) k
= size
- i
;
916 if (ssh_scp_recv(transbuf
, k
) == 0)
917 bump("Lost connection");
918 if (wrerror
) continue;
919 if (! WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
922 printf("\r%-25.25s | %50s\n",
924 "Write error.. waiting for end of file");
929 if (time(NULL
) > stat_lasttime
||
931 stat_lasttime
= time(NULL
);
932 print_stats(stat_name
, size
, stat_bytes
,
933 stat_starttime
, stat_lasttime
);
940 FILETIME actime
, wrtime
;
941 TIME_POSIX_TO_WIN(atime
, actime
);
942 TIME_POSIX_TO_WIN(mtime
, wrtime
);
943 SetFileTime(f
, NULL
, &actime
, &wrtime
);
948 run_err("%s: Write error", namebuf
);
956 * We will copy local files to a remote server.
958 static void toremote(int argc
, char *argv
[])
960 char *src
, *targ
, *host
, *user
;
966 /* Separate host from filename */
970 bump("targ == NULL in toremote()");
974 /* Substitute "." for emtpy target */
976 /* Separate host and username */
978 host
= strrchr(host
, '@');
989 /* Find out if the source filespec covers multiple files
990 if so, we should set the targetshouldbedirectory flag */
992 WIN32_FIND_DATA fdat
;
993 if (colon(argv
[0]) != NULL
)
994 bump("%s: Remote to remote not supported", argv
[0]);
995 fh
= FindFirstFile(argv
[0], &fdat
);
996 if (fh
== INVALID_HANDLE_VALUE
)
997 bump("%s: No such file or directory\n", argv
[0]);
998 if (FindNextFile(fh
, &fdat
))
999 targetshouldbedirectory
= 1;
1003 cmd
= smalloc(strlen(targ
) + 100);
1004 sprintf(cmd
, "scp%s%s%s%s -t %s",
1005 verbose ?
" -v" : "",
1006 recursive ?
" -r" : "",
1007 preserve ?
" -p" : "",
1008 targetshouldbedirectory ?
" -d" : "",
1010 do_cmd(host
, user
, cmd
);
1015 for (i
= 0; i
< argc
- 1; i
++) {
1017 WIN32_FIND_DATA fdat
;
1019 if (colon(src
) != NULL
) {
1020 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
1024 dir
= FindFirstFile(src
, &fdat
);
1025 if (dir
== INVALID_HANDLE_VALUE
) {
1026 run_err("%s: No such file or directory", src
);
1033 * Ensure that . and .. are never matched by wildcards,
1034 * but only by deliberate action.
1036 if (!strcmp(fdat
.cFileName
, ".") ||
1037 !strcmp(fdat
.cFileName
, "..")) {
1039 * Find*File has returned a special dir. We require
1040 * that _either_ `src' ends in a backslash followed
1041 * by that string, _or_ `src' is precisely that
1044 int len
= strlen(src
), dlen
= strlen(fdat
.cFileName
);
1045 if (len
== dlen
&& !strcmp(src
, fdat
.cFileName
)) {
1047 } else if (len
> dlen
+1 && src
[len
-dlen
-1] == '\\' &&
1048 !strcmp(src
+len
-dlen
, fdat
.cFileName
)) {
1051 continue; /* ignore this one */
1053 if (strlen(src
) + strlen(fdat
.cFileName
) >=
1055 tell_user(stderr
, "%s: Name too long", src
);
1058 strcpy(namebuf
, src
);
1059 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1063 if (strrchr(last
, '\\') != NULL
)
1064 last
= strrchr(last
, '\\') + 1;
1065 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1066 last
= strchr(namebuf
, ':') + 1;
1067 strcpy(last
, fdat
.cFileName
);
1069 } while (FindNextFile(dir
, &fdat
));
1075 * We will copy files from a remote server to the local machine.
1077 static void tolocal(int argc
, char *argv
[])
1079 char *src
, *targ
, *host
, *user
;
1083 bump("More than one remote source not supported");
1088 /* Separate host from filename */
1092 bump("Local to local copy not supported");
1096 /* Substitute "." for empty filename */
1098 /* Separate username and hostname */
1100 host
= strrchr(host
, '@');
1110 cmd
= smalloc(strlen(src
) + 100);
1111 sprintf(cmd
, "scp%s%s%s%s -f %s",
1112 verbose ?
" -v" : "",
1113 recursive ?
" -r" : "",
1114 preserve ?
" -p" : "",
1115 targetshouldbedirectory ?
" -d" : "",
1117 do_cmd(host
, user
, cmd
);
1124 * We will issue a list command to get a remote directory.
1126 static void get_dir_list(int argc
, char *argv
[])
1128 char *src
, *host
, *user
;
1134 /* Separate host from filename */
1138 bump("Local to local copy not supported");
1142 /* Substitute "." for empty filename */
1144 /* Separate username and hostname */
1146 host
= strrchr(host
, '@');
1156 cmd
= smalloc(4*strlen(src
) + 100);
1157 strcpy(cmd
, "ls -la '");
1158 p
= cmd
+ strlen(cmd
);
1159 for (q
= src
; *q
; q
++) {
1161 *p
++ = '\''; *p
++ = '\\'; *p
++ = '\''; *p
++ = '\'';
1169 do_cmd(host
, user
, cmd
);
1172 while (ssh_scp_recv(&c
, 1) > 0)
1173 tell_char(stdout
, c
);
1177 * Initialize the Win$ock driver.
1179 static void init_winsock(void)
1184 winsock_ver
= MAKEWORD(1, 1);
1185 if (WSAStartup(winsock_ver
, &wsadata
))
1186 bump("Unable to initialise WinSock");
1187 if (LOBYTE(wsadata
.wVersion
) != 1 ||
1188 HIBYTE(wsadata
.wVersion
) != 1)
1189 bump("WinSock version is incompatible with 1.1");
1193 * Short description of parameters.
1195 static void usage(void)
1197 printf("PuTTY Secure Copy client\n");
1198 printf("%s\n", ver
);
1199 printf("Usage: pscp [options] [user@]host:source target\n");
1200 printf(" pscp [options] source [source...] [user@]host:target\n");
1201 printf(" pscp [options] -ls user@host:filespec\n");
1202 printf("Options:\n");
1203 printf(" -p preserve file attributes\n");
1204 printf(" -q quiet, don't show statistics\n");
1205 printf(" -r copy directories recursively\n");
1206 printf(" -v show verbose messages\n");
1207 printf(" -P port connect to specified port\n");
1208 printf(" -pw passw login with specified password\n");
1211 * -gui is an internal option, used by GUI front ends to get
1212 * pscp to pass progress reports back to them. It's not an
1213 * ordinary user-accessible option, so it shouldn't be part of
1214 * the command-line help. The only people who need to know
1215 * about it are programmers, and they can read the source.
1217 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1223 * Main program (no, really?)
1225 int main(int argc
, char *argv
[])
1230 default_protocol
= PROT_TELNET
;
1232 flags
= FLAG_STDERR
;
1233 ssh_get_line
= &get_line
;
1237 for (i
= 1; i
< argc
; i
++) {
1238 if (argv
[i
][0] != '-')
1240 if (strcmp(argv
[i
], "-v") == 0)
1241 verbose
= 1, flags
|= FLAG_VERBOSE
;
1242 else if (strcmp(argv
[i
], "-r") == 0)
1244 else if (strcmp(argv
[i
], "-p") == 0)
1246 else if (strcmp(argv
[i
], "-q") == 0)
1248 else if (strcmp(argv
[i
], "-h") == 0 ||
1249 strcmp(argv
[i
], "-?") == 0)
1251 else if (strcmp(argv
[i
], "-P") == 0 && i
+1 < argc
)
1252 portnumber
= atoi(argv
[++i
]);
1253 else if (strcmp(argv
[i
], "-pw") == 0 && i
+1 < argc
)
1254 password
= argv
[++i
];
1255 else if (strcmp(argv
[i
], "-gui") == 0 && i
+1 < argc
) {
1256 gui_hwnd
= argv
[++i
];
1258 } else if (strcmp(argv
[i
], "-ls") == 0)
1260 else if (strcmp(argv
[i
], "--") == 0)
1272 get_dir_list(argc
, argv
);
1279 targetshouldbedirectory
= 1;
1281 if (colon(argv
[argc
-1]) != NULL
)
1282 toremote(argc
, argv
);
1284 tolocal(argc
, argv
);
1287 if (back
!= NULL
&& back
->socket() != NULL
) {
1289 back
->special(TS_EOF
);
1290 ssh_scp_recv(&ch
, 1);
1295 /* GUI Adaptation - August 2000 */
1297 unsigned int msg_id
= WM_RET_ERR_CNT
;
1298 if (list
) msg_id
= WM_LS_RET_ERR_CNT
;
1299 while (!PostMessage( (HWND
)atoi(gui_hwnd
), msg_id
, (WPARAM
)errs
, 0/*lParam*/ ) )
1302 return (errs
== 0 ?
0 : 1);