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 int prev_stats_len
= 0;
57 static char *password
= NULL
;
59 /* GUI Adaptation - Sept 2000 */
60 #define NAME_STR_MAX 2048
61 static char statname
[NAME_STR_MAX
+ 1];
62 static unsigned long statsize
= 0;
63 static int statperct
= 0;
64 static unsigned long statelapsed
= 0;
65 static int gui_mode
= 0;
66 static char *gui_hwnd
= NULL
;
68 static void source(char *src
);
69 static void rsource(char *src
);
70 static void sink(char *targ
, char *src
);
71 /* GUI Adaptation - Sept 2000 */
72 static void tell_char(FILE * stream
, char c
);
73 static void tell_str(FILE * stream
, char *str
);
74 static void tell_user(FILE * stream
, char *fmt
, ...);
75 static void gui_update_stats(char *name
, unsigned long size
,
76 int percentage
, unsigned long elapsed
);
79 * The maximum amount of queued data we accept before we stop and
80 * wait for the server to process some.
82 #define MAX_SCP_BUFSIZE 16384
84 void logevent(char *string
)
88 void ldisc_send(char *buf
, int len
)
91 * This is only here because of the calls to ldisc_send(NULL,
92 * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
93 * as an ldisc. So if we get called with any real data, I want
99 void verify_ssh_host_key(char *host
, int port
, char *keytype
,
100 char *keystr
, char *fingerprint
)
106 static const char absentmsg
[] =
107 "The server's host key is not cached in the registry. You\n"
108 "have no guarantee that the server is the computer you\n"
110 "The server's key fingerprint is:\n"
112 "If you trust this host, enter \"y\" to add the key to\n"
113 "PuTTY's cache and carry on connecting.\n"
114 "If you want to carry on connecting just once, without\n"
115 "adding the key to the cache, enter \"n\".\n"
116 "If you do not trust this host, press Return to abandon the\n"
118 "Store key in cache? (y/n) ";
120 static const char wrongmsg
[] =
121 "WARNING - POTENTIAL SECURITY BREACH!\n"
122 "The server's host key does not match the one PuTTY has\n"
123 "cached in the registry. This means that either the\n"
124 "server administrator has changed the host key, or you\n"
125 "have actually connected to another computer pretending\n"
126 "to be the server.\n"
127 "The new key fingerprint is:\n"
129 "If you were expecting this change and trust the new key,\n"
130 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
131 "If you want to carry on connecting but without updating\n"
132 "the cache, enter \"n\".\n"
133 "If you want to abandon the connection completely, press\n"
134 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
136 "Update cached key? (y/n, Return cancels connection) ";
138 static const char abandoned
[] = "Connection abandoned.\n";
143 * Verify the key against the registry.
145 ret
= verify_host_key(host
, port
, keytype
, keystr
);
147 if (ret
== 0) /* success - key matched OK */
150 if (ret
== 2) { /* key was different */
151 fprintf(stderr
, wrongmsg
, fingerprint
);
154 if (ret
== 1) { /* key was absent */
155 fprintf(stderr
, absentmsg
, fingerprint
);
159 hin
= GetStdHandle(STD_INPUT_HANDLE
);
160 GetConsoleMode(hin
, &savemode
);
161 SetConsoleMode(hin
, (savemode
| ENABLE_ECHO_INPUT
|
162 ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
));
163 ReadFile(hin
, line
, sizeof(line
) - 1, &i
, NULL
);
164 SetConsoleMode(hin
, savemode
);
166 if (line
[0] != '\0' && line
[0] != '\r' && line
[0] != '\n') {
167 if (line
[0] == 'y' || line
[0] == 'Y')
168 store_host_key(host
, port
, keytype
, keystr
);
170 fprintf(stderr
, abandoned
);
175 /* GUI Adaptation - Sept 2000 */
176 static void send_msg(HWND h
, UINT message
, WPARAM wParam
)
178 while (!PostMessage(h
, message
, wParam
, 0))
182 static void tell_char(FILE * stream
, char c
)
187 unsigned int msg_id
= WM_STD_OUT_CHAR
;
188 if (stream
== stderr
)
189 msg_id
= WM_STD_ERR_CHAR
;
190 send_msg((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) c
);
194 static void tell_str(FILE * stream
, char *str
)
198 for (i
= 0; i
< strlen(str
); ++i
)
199 tell_char(stream
, str
[i
]);
202 static void tell_user(FILE * stream
, char *fmt
, ...)
204 char str
[0x100]; /* Make the size big enough */
207 vsprintf(str
, fmt
, ap
);
210 tell_str(stream
, str
);
213 static void gui_update_stats(char *name
, unsigned long size
,
214 int percentage
, unsigned long elapsed
)
218 if (strcmp(name
, statname
) != 0) {
219 for (i
= 0; i
< strlen(name
); ++i
)
220 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_CHAR
,
222 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_CHAR
, (WPARAM
) '\n');
223 strcpy(statname
, name
);
225 if (statsize
!= size
) {
226 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_SIZE
, (WPARAM
) size
);
229 if (statelapsed
!= elapsed
) {
230 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_ELAPSED
,
232 statelapsed
= elapsed
;
234 if (statperct
!= percentage
) {
235 send_msg((HWND
) atoi(gui_hwnd
), WM_STATS_PERCENT
,
236 (WPARAM
) percentage
);
237 statperct
= percentage
;
242 * Print an error message and perform a fatal exit.
244 void fatalbox(char *fmt
, ...)
246 char str
[0x100]; /* Make the size big enough */
249 strcpy(str
, "Fatal:");
250 vsprintf(str
+ strlen(str
), fmt
, ap
);
253 tell_str(stderr
, str
);
257 unsigned int msg_id
= WM_RET_ERR_CNT
;
259 msg_id
= WM_LS_RET_ERR_CNT
;
261 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
262 0 /*lParam */ ))SleepEx(1000, TRUE
);
267 void connection_fatal(char *fmt
, ...)
269 char str
[0x100]; /* Make the size big enough */
272 strcpy(str
, "Fatal:");
273 vsprintf(str
+ strlen(str
), fmt
, ap
);
276 tell_str(stderr
, str
);
280 unsigned int msg_id
= WM_RET_ERR_CNT
;
282 msg_id
= WM_LS_RET_ERR_CNT
;
284 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
285 0 /*lParam */ ))SleepEx(1000, TRUE
);
292 * Be told what socket we're supposed to be using.
294 static SOCKET scp_ssh_socket
;
295 char *do_select(SOCKET skt
, int startup
)
298 scp_ssh_socket
= skt
;
300 scp_ssh_socket
= INVALID_SOCKET
;
303 extern int select_result(WPARAM
, LPARAM
);
306 * Receive a block of data from the SSH link. Block until all data
309 * To do this, we repeatedly call the SSH protocol module, with our
310 * own trap in from_backend() to catch the data that comes back. We
311 * do this until we have enough data.
314 static unsigned char *outptr
; /* where to put the data */
315 static unsigned outlen
; /* how much data required */
316 static unsigned char *pending
= NULL
; /* any spare data */
317 static unsigned pendlen
= 0, pendsize
= 0; /* length and phys. size of buffer */
318 int from_backend(int is_stderr
, char *data
, int datalen
)
320 unsigned char *p
= (unsigned char *) data
;
321 unsigned len
= (unsigned) datalen
;
324 * stderr data is just spouted to local stderr and otherwise
328 fwrite(data
, 1, len
, stderr
);
335 * If this is before the real session begins, just return.
341 unsigned used
= outlen
;
344 memcpy(outptr
, p
, used
);
352 if (pendsize
< pendlen
+ len
) {
353 pendsize
= pendlen
+ len
+ 4096;
354 pending
= (pending ?
srealloc(pending
, pendsize
) :
357 fatalbox("Out of memory");
359 memcpy(pending
+ pendlen
, p
, len
);
365 static int scp_process_network_event(void)
370 FD_SET(scp_ssh_socket
, &readfds
);
371 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
373 select_result((WPARAM
) scp_ssh_socket
, (LPARAM
) FD_READ
);
376 static int ssh_scp_recv(unsigned char *buf
, int len
)
382 * See if the pending-input block contains some of what we
386 unsigned pendused
= pendlen
;
387 if (pendused
> outlen
)
389 memcpy(outptr
, pending
, pendused
);
390 memmove(pending
, pending
+ pendused
, pendlen
- pendused
);
404 if (!scp_process_network_event())
412 * Loop through the ssh connection and authentication process.
414 static void ssh_scp_init(void)
416 if (scp_ssh_socket
== INVALID_SOCKET
)
418 while (!back
->sendok()) {
421 FD_SET(scp_ssh_socket
, &readfds
);
422 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
424 select_result((WPARAM
) scp_ssh_socket
, (LPARAM
) FD_READ
);
429 * Print an error message and exit after closing the SSH link.
431 static void bump(char *fmt
, ...)
433 char str
[0x100]; /* Make the size big enough */
436 strcpy(str
, "Fatal:");
437 vsprintf(str
+ strlen(str
), fmt
, ap
);
440 tell_str(stderr
, str
);
443 if (back
!= NULL
&& back
->socket() != NULL
) {
445 back
->special(TS_EOF
);
446 ssh_scp_recv(&ch
, 1);
450 unsigned int msg_id
= WM_RET_ERR_CNT
;
452 msg_id
= WM_LS_RET_ERR_CNT
;
454 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
455 0 /*lParam */ ))SleepEx(1000, TRUE
);
461 static int get_line(const char *prompt
, char *str
, int maxlen
, int is_pw
)
464 DWORD savemode
, newmode
, i
;
466 if (is_pw
&& password
) {
467 static int tried_once
= 0;
472 strncpy(str
, password
, maxlen
);
473 str
[maxlen
- 1] = '\0';
479 /* GUI Adaptation - Sept 2000 */
484 hin
= GetStdHandle(STD_INPUT_HANDLE
);
485 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
486 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
)
487 bump("Cannot get standard input/output handles");
489 GetConsoleMode(hin
, &savemode
);
490 newmode
= savemode
| ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
;
492 newmode
&= ~ENABLE_ECHO_INPUT
;
494 newmode
|= ENABLE_ECHO_INPUT
;
495 SetConsoleMode(hin
, newmode
);
497 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
498 ReadFile(hin
, str
, maxlen
- 1, &i
, NULL
);
500 SetConsoleMode(hin
, savemode
);
502 if ((int) i
> maxlen
)
509 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
516 * Open an SSH connection to user@host and execute cmd.
518 static void do_cmd(char *host
, char *user
, char *cmd
)
520 char *err
, *realhost
;
523 if (host
== NULL
|| host
[0] == '\0')
524 bump("Empty host name");
526 /* Try to load settings for this host */
527 do_defaults(host
, &cfg
);
528 if (cfg
.host
[0] == '\0') {
529 /* No settings for this host; use defaults */
530 do_defaults(NULL
, &cfg
);
531 strncpy(cfg
.host
, host
, sizeof(cfg
.host
) - 1);
532 cfg
.host
[sizeof(cfg
.host
) - 1] = '\0';
537 if (user
!= NULL
&& user
[0] != '\0') {
538 strncpy(cfg
.username
, user
, sizeof(cfg
.username
) - 1);
539 cfg
.username
[sizeof(cfg
.username
) - 1] = '\0';
540 } else if (cfg
.username
[0] == '\0') {
542 if (GetUserName(user
, &namelen
) == FALSE
)
543 bump("Empty user name");
544 user
= smalloc(namelen
* sizeof(char));
545 GetUserName(user
, &namelen
);
547 tell_user(stderr
, "Guessing user name: %s", user
);
548 strncpy(cfg
.username
, user
, sizeof(cfg
.username
) - 1);
549 cfg
.username
[sizeof(cfg
.username
) - 1] = '\0';
553 if (cfg
.protocol
!= PROT_SSH
)
557 cfg
.port
= portnumber
;
559 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
560 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
) - 1] = '\0';
565 err
= back
->init(cfg
.host
, cfg
.port
, &realhost
);
567 bump("ssh_init: %s", err
);
569 if (verbose
&& realhost
!= NULL
)
570 tell_user(stderr
, "Connected to %s\n", realhost
);
575 * Update statistic information about current file.
577 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
578 time_t start
, time_t now
)
586 /* GUI Adaptation - Sept 2000 */
588 gui_update_stats(name
, size
, (int) (100 * (done
* 1.0 / size
)),
589 (unsigned long) difftime(now
, start
));
592 ratebs
= (float) done
/ (now
- start
);
594 ratebs
= (float) done
;
599 eta
= (unsigned long) ((size
- done
) / ratebs
);
600 sprintf(etastr
, "%02ld:%02ld:%02ld",
601 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
603 pct
= (int) (100.0 * (float) done
/ size
);
605 len
= printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
606 name
, done
/ 1024, ratebs
/ 1024.0, etastr
, pct
);
607 if (len
< prev_stats_len
)
608 printf("%*s", prev_stats_len
- len
, "");
609 prev_stats_len
= len
;
617 * Find a colon in str and return a pointer to the colon.
618 * This is used to separate hostname from filename.
620 static char *colon(char *str
)
622 /* We ignore a leading colon, since the hostname cannot be
623 empty. We also ignore a colon as second character because
624 of filenames like f:myfile.txt. */
625 if (str
[0] == '\0' || str
[0] == ':' || str
[1] == ':')
627 while (*str
!= '\0' && *str
!= ':' && *str
!= '/' && *str
!= '\\')
636 * Wait for a response from the other side.
637 * Return 0 if ok, -1 if error.
639 static int response(void)
641 char ch
, resp
, rbuf
[2048];
644 if (ssh_scp_recv(&resp
, 1) <= 0)
645 bump("Lost connection");
655 case 2: /* fatal error */
657 if (ssh_scp_recv(&ch
, 1) <= 0)
658 bump("Protocol error: Lost connection");
660 } while (p
< sizeof(rbuf
) && ch
!= '\n');
663 tell_user(stderr
, "%s\n", rbuf
);
672 * Send an error message to the other side and to the screen.
673 * Increment error counter.
675 static void run_err(const char *fmt
, ...)
681 strcpy(str
, "scp: ");
682 vsprintf(str
+ strlen(str
), fmt
, ap
);
684 back
->send("\001", 1); /* scp protocol error prefix */
685 back
->send(str
, strlen(str
));
686 tell_user(stderr
, "%s", str
);
691 * Execute the source part of the SCP protocol.
693 static void source(char *src
)
701 unsigned long stat_bytes
;
702 time_t stat_starttime
, stat_lasttime
;
704 attr
= GetFileAttributes(src
);
705 if (attr
== (DWORD
) - 1) {
706 run_err("%s: No such file or directory", src
);
710 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
713 * Avoid . and .. directories.
716 p
= strrchr(src
, '/');
718 p
= strrchr(src
, '\\');
723 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
724 /* skip . and .. */ ;
728 run_err("%s: not a regular file", src
);
733 if ((last
= strrchr(src
, '/')) == NULL
)
737 if (strrchr(last
, '\\') != NULL
)
738 last
= strrchr(last
, '\\') + 1;
739 if (last
== src
&& strchr(src
, ':') != NULL
)
740 last
= strchr(src
, ':') + 1;
742 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
743 OPEN_EXISTING
, 0, 0);
744 if (f
== INVALID_HANDLE_VALUE
) {
745 run_err("%s: Cannot open file", src
);
750 FILETIME actime
, wrtime
;
751 unsigned long mtime
, atime
;
752 GetFileTime(f
, NULL
, &actime
, &wrtime
);
753 TIME_WIN_TO_POSIX(actime
, atime
);
754 TIME_WIN_TO_POSIX(wrtime
, mtime
);
755 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
756 back
->send(buf
, strlen(buf
));
761 size
= GetFileSize(f
, NULL
);
762 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
764 tell_user(stderr
, "Sending file modes: %s", buf
);
765 back
->send(buf
, strlen(buf
));
770 stat_starttime
= time(NULL
);
773 for (i
= 0; i
< size
; i
+= 4096) {
780 if (!ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
783 bump("%s: Read error", src
);
785 bufsize
= back
->send(transbuf
, k
);
788 if (time(NULL
) != stat_lasttime
|| i
+ k
== size
) {
789 stat_lasttime
= time(NULL
);
790 print_stats(last
, size
, stat_bytes
,
791 stat_starttime
, stat_lasttime
);
796 * If the network transfer is backing up - that is, the
797 * remote site is not accepting data as fast as we can
798 * produce it - then we must loop on network events until
799 * we have space in the buffer again.
801 while (bufsize
> MAX_SCP_BUFSIZE
) {
802 if (!scp_process_network_event())
803 bump("%s: Network error occurred", src
);
804 bufsize
= back
->sendbuffer();
814 * Recursively send the contents of a directory.
816 static void rsource(char *src
)
821 WIN32_FIND_DATA fdat
;
824 if ((last
= strrchr(src
, '/')) == NULL
)
828 if (strrchr(last
, '\\') != NULL
)
829 last
= strrchr(last
, '\\') + 1;
830 if (last
== src
&& strchr(src
, ':') != NULL
)
831 last
= strchr(src
, ':') + 1;
833 /* maybe send filetime */
835 sprintf(buf
, "D0755 0 %s\n", last
);
837 tell_user(stderr
, "Entering directory: %s", buf
);
838 back
->send(buf
, strlen(buf
));
842 sprintf(buf
, "%s/*", src
);
843 dir
= FindFirstFile(buf
, &fdat
);
844 ok
= (dir
!= INVALID_HANDLE_VALUE
);
846 if (strcmp(fdat
.cFileName
, ".") == 0 ||
847 strcmp(fdat
.cFileName
, "..") == 0) {
848 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >= sizeof(buf
)) {
849 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
851 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
854 ok
= FindNextFile(dir
, &fdat
);
859 back
->send(buf
, strlen(buf
));
864 * Execute the sink part of the SCP protocol.
866 static void sink(char *targ
, char *src
)
876 unsigned long mtime
, atime
;
878 unsigned long size
, i
;
880 unsigned long stat_bytes
;
881 time_t stat_starttime
, stat_lasttime
;
884 attr
= GetFileAttributes(targ
);
885 if (attr
!= (DWORD
) - 1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
888 if (targetshouldbedirectory
&& !targisdir
)
889 bump("%s: Not a directory", targ
);
895 if (ssh_scp_recv(&ch
, 1) <= 0)
898 bump("Protocol error: Unexpected newline");
902 if (ssh_scp_recv(&ch
, 1) <= 0)
903 bump("Lost connection");
905 } while (i
< sizeof(buf
) && ch
!= '\n');
908 case '\01': /* error */
909 tell_user(stderr
, "%s\n", buf
+ 1);
912 case '\02': /* fatal error */
918 if (sscanf(buf
, "T%ld %*d %ld %*d", &mtime
, &atime
) == 2) {
923 bump("Protocol error: Illegal time format");
928 bump("Protocol error: Expected control record");
931 if (sscanf(buf
+ 1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
932 bump("Protocol error: Illegal file descriptor format");
933 /* Security fix: ensure the file ends up where we asked for it. */
940 p
= namebuf
+ strlen(namebuf
);
941 while (p
> namebuf
&& p
[-1] != '/' && p
[-1] != '\\')
946 strcpy(namebuf
, targ
);
948 attr
= GetFileAttributes(namebuf
);
949 exists
= (attr
!= (DWORD
) - 1);
952 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
953 run_err("%s: Not a directory", namebuf
);
957 if (!CreateDirectory(namebuf
, NULL
)) {
958 run_err("%s: Cannot create directory", namebuf
);
963 /* can we set the timestamp for directories ? */
967 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
968 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
969 if (f
== INVALID_HANDLE_VALUE
) {
970 run_err("%s: Cannot create file", namebuf
);
977 stat_starttime
= time(NULL
);
979 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
983 if (strrchr(stat_name
, '\\') != NULL
)
984 stat_name
= strrchr(stat_name
, '\\') + 1;
986 for (i
= 0; i
< size
; i
+= 4096) {
991 if (ssh_scp_recv(transbuf
, k
) == 0)
992 bump("Lost connection");
995 if (!WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
998 printf("\r%-25.25s | %50s\n",
1000 "Write error.. waiting for end of file");
1005 if (time(NULL
) > stat_lasttime
|| i
+ k
== size
) {
1006 stat_lasttime
= time(NULL
);
1007 print_stats(stat_name
, size
, stat_bytes
,
1008 stat_starttime
, stat_lasttime
);
1015 FILETIME actime
, wrtime
;
1016 TIME_POSIX_TO_WIN(atime
, actime
);
1017 TIME_POSIX_TO_WIN(mtime
, wrtime
);
1018 SetFileTime(f
, NULL
, &actime
, &wrtime
);
1023 run_err("%s: Write error", namebuf
);
1031 * We will copy local files to a remote server.
1033 static void toremote(int argc
, char *argv
[])
1035 char *src
, *targ
, *host
, *user
;
1039 targ
= argv
[argc
- 1];
1041 /* Separate host from filename */
1045 bump("targ == NULL in toremote()");
1049 /* Substitute "." for emtpy target */
1051 /* Separate host and username */
1053 host
= strrchr(host
, '@');
1064 /* Find out if the source filespec covers multiple files
1065 if so, we should set the targetshouldbedirectory flag */
1067 WIN32_FIND_DATA fdat
;
1068 if (colon(argv
[0]) != NULL
)
1069 bump("%s: Remote to remote not supported", argv
[0]);
1070 fh
= FindFirstFile(argv
[0], &fdat
);
1071 if (fh
== INVALID_HANDLE_VALUE
)
1072 bump("%s: No such file or directory\n", argv
[0]);
1073 if (FindNextFile(fh
, &fdat
))
1074 targetshouldbedirectory
= 1;
1078 cmd
= smalloc(strlen(targ
) + 100);
1079 sprintf(cmd
, "scp%s%s%s%s -t %s",
1080 verbose ?
" -v" : "",
1081 recursive ?
" -r" : "",
1082 preserve ?
" -p" : "",
1083 targetshouldbedirectory ?
" -d" : "", targ
);
1084 do_cmd(host
, user
, cmd
);
1089 for (i
= 0; i
< argc
- 1; i
++) {
1091 WIN32_FIND_DATA fdat
;
1093 if (colon(src
) != NULL
) {
1094 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
1098 dir
= FindFirstFile(src
, &fdat
);
1099 if (dir
== INVALID_HANDLE_VALUE
) {
1100 run_err("%s: No such file or directory", src
);
1107 * Ensure that . and .. are never matched by wildcards,
1108 * but only by deliberate action.
1110 if (!strcmp(fdat
.cFileName
, ".") ||
1111 !strcmp(fdat
.cFileName
, "..")) {
1113 * Find*File has returned a special dir. We require
1114 * that _either_ `src' ends in a backslash followed
1115 * by that string, _or_ `src' is precisely that
1118 int len
= strlen(src
), dlen
= strlen(fdat
.cFileName
);
1119 if (len
== dlen
&& !strcmp(src
, fdat
.cFileName
)) {
1121 } else if (len
> dlen
+ 1 && src
[len
- dlen
- 1] == '\\' &&
1122 !strcmp(src
+ len
- dlen
, fdat
.cFileName
)) {
1125 continue; /* ignore this one */
1127 if (strlen(src
) + strlen(fdat
.cFileName
) >= sizeof(namebuf
)) {
1128 tell_user(stderr
, "%s: Name too long", src
);
1131 strcpy(namebuf
, src
);
1132 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1136 if (strrchr(last
, '\\') != NULL
)
1137 last
= strrchr(last
, '\\') + 1;
1138 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1139 last
= strchr(namebuf
, ':') + 1;
1140 strcpy(last
, fdat
.cFileName
);
1142 } while (FindNextFile(dir
, &fdat
));
1148 * We will copy files from a remote server to the local machine.
1150 static void tolocal(int argc
, char *argv
[])
1152 char *src
, *targ
, *host
, *user
;
1156 bump("More than one remote source not supported");
1161 /* Separate host from filename */
1165 bump("Local to local copy not supported");
1169 /* Substitute "." for empty filename */
1171 /* Separate username and hostname */
1173 host
= strrchr(host
, '@');
1183 cmd
= smalloc(strlen(src
) + 100);
1184 sprintf(cmd
, "scp%s%s%s%s -f %s",
1185 verbose ?
" -v" : "",
1186 recursive ?
" -r" : "",
1187 preserve ?
" -p" : "",
1188 targetshouldbedirectory ?
" -d" : "", src
);
1189 do_cmd(host
, user
, cmd
);
1196 * We will issue a list command to get a remote directory.
1198 static void get_dir_list(int argc
, char *argv
[])
1200 char *src
, *host
, *user
;
1206 /* Separate host from filename */
1210 bump("Local to local copy not supported");
1214 /* Substitute "." for empty filename */
1216 /* Separate username and hostname */
1218 host
= strrchr(host
, '@');
1228 cmd
= smalloc(4 * strlen(src
) + 100);
1229 strcpy(cmd
, "ls -la '");
1230 p
= cmd
+ strlen(cmd
);
1231 for (q
= src
; *q
; q
++) {
1244 do_cmd(host
, user
, cmd
);
1247 while (ssh_scp_recv(&c
, 1) > 0)
1248 tell_char(stdout
, c
);
1252 * Initialize the Win$ock driver.
1254 static void init_winsock(void)
1259 winsock_ver
= MAKEWORD(1, 1);
1260 if (WSAStartup(winsock_ver
, &wsadata
))
1261 bump("Unable to initialise WinSock");
1262 if (LOBYTE(wsadata
.wVersion
) != 1 || HIBYTE(wsadata
.wVersion
) != 1)
1263 bump("WinSock version is incompatible with 1.1");
1267 * Short description of parameters.
1269 static void usage(void)
1271 printf("PuTTY Secure Copy client\n");
1272 printf("%s\n", ver
);
1273 printf("Usage: pscp [options] [user@]host:source target\n");
1275 (" pscp [options] source [source...] [user@]host:target\n");
1276 printf(" pscp [options] -ls user@host:filespec\n");
1277 printf("Options:\n");
1278 printf(" -p preserve file attributes\n");
1279 printf(" -q quiet, don't show statistics\n");
1280 printf(" -r copy directories recursively\n");
1281 printf(" -v show verbose messages\n");
1282 printf(" -P port connect to specified port\n");
1283 printf(" -pw passw login with specified password\n");
1286 * -gui is an internal option, used by GUI front ends to get
1287 * pscp to pass progress reports back to them. It's not an
1288 * ordinary user-accessible option, so it shouldn't be part of
1289 * the command-line help. The only people who need to know
1290 * about it are programmers, and they can read the source.
1293 (" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1299 * Main program (no, really?)
1301 int main(int argc
, char *argv
[])
1305 default_protocol
= PROT_TELNET
;
1307 flags
= FLAG_STDERR
;
1308 ssh_get_line
= &get_line
;
1312 for (i
= 1; i
< argc
; i
++) {
1313 if (argv
[i
][0] != '-')
1315 if (strcmp(argv
[i
], "-v") == 0)
1316 verbose
= 1, flags
|= FLAG_VERBOSE
;
1317 else if (strcmp(argv
[i
], "-r") == 0)
1319 else if (strcmp(argv
[i
], "-p") == 0)
1321 else if (strcmp(argv
[i
], "-q") == 0)
1323 else if (strcmp(argv
[i
], "-h") == 0 || strcmp(argv
[i
], "-?") == 0)
1325 else if (strcmp(argv
[i
], "-P") == 0 && i
+ 1 < argc
)
1326 portnumber
= atoi(argv
[++i
]);
1327 else if (strcmp(argv
[i
], "-pw") == 0 && i
+ 1 < argc
)
1328 password
= argv
[++i
];
1329 else if (strcmp(argv
[i
], "-gui") == 0 && i
+ 1 < argc
) {
1330 gui_hwnd
= argv
[++i
];
1332 } else if (strcmp(argv
[i
], "-ls") == 0)
1334 else if (strcmp(argv
[i
], "--") == 0) {
1347 get_dir_list(argc
, argv
);
1354 targetshouldbedirectory
= 1;
1356 if (colon(argv
[argc
- 1]) != NULL
)
1357 toremote(argc
, argv
);
1359 tolocal(argc
, argv
);
1362 if (back
!= NULL
&& back
->socket() != NULL
) {
1364 back
->special(TS_EOF
);
1365 ssh_scp_recv(&ch
, 1);
1370 /* GUI Adaptation - August 2000 */
1372 unsigned int msg_id
= WM_RET_ERR_CNT
;
1374 msg_id
= WM_LS_RET_ERR_CNT
;
1376 ((HWND
) atoi(gui_hwnd
), msg_id
, (WPARAM
) errs
,
1377 0 /*lParam */ ))SleepEx(1000, TRUE
);
1379 return (errs
== 0 ?
0 : 1);