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
);
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 * Receive a block of data from the SSH link. Block until all data
248 * To do this, we repeatedly call the SSH protocol module, with our
249 * own trap in from_backend() to catch the data that comes back. We
250 * do this until we have enough data.
252 static unsigned char *outptr
; /* where to put the data */
253 static unsigned outlen
; /* how much data required */
254 static unsigned char *pending
= NULL
; /* any spare data */
255 static unsigned pendlen
=0, pendsize
=0; /* length and phys. size of buffer */
256 void from_backend(int is_stderr
, char *data
, int datalen
) {
257 unsigned char *p
= (unsigned char *)data
;
258 unsigned len
= (unsigned)datalen
;
261 * stderr data is just spouted to local stderr and otherwise
265 fwrite(data
, 1, len
, stderr
);
272 * If this is before the real session begins, just return.
278 unsigned used
= outlen
;
279 if (used
> len
) used
= len
;
280 memcpy(outptr
, p
, used
);
281 outptr
+= used
; outlen
-= used
;
282 p
+= used
; len
-= used
;
286 if (pendsize
< pendlen
+ len
) {
287 pendsize
= pendlen
+ len
+ 4096;
288 pending
= (pending ?
realloc(pending
, pendsize
) :
291 fatalbox("Out of memory");
293 memcpy(pending
+pendlen
, p
, len
);
297 static int ssh_scp_recv(unsigned char *buf
, int len
) {
304 * See if the pending-input block contains some of what we
308 unsigned pendused
= pendlen
;
309 if (pendused
> outlen
)
311 memcpy(outptr
, pending
, pendused
);
312 memmove(pending
, pending
+pendused
, pendlen
-pendused
);
328 if (s
== INVALID_SOCKET
) {
329 connection_open
= FALSE
;
334 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
336 back
->msg(0, FD_READ
);
343 * Loop through the ssh connection and authentication process.
345 static void ssh_scp_init(void) {
349 if (s
== INVALID_SOCKET
)
351 while (!back
->sendok()) {
355 if (select(1, &readfds
, NULL
, NULL
, NULL
) < 0)
357 back
->msg(0, FD_READ
);
362 * Print an error message and exit after closing the SSH link.
364 static void bump(char *fmt
, ...)
366 char str
[0x100]; /* Make the size big enough */
369 strcpy(str
, "Fatal:");
370 vsprintf(str
+strlen(str
), fmt
, ap
);
373 tell_str(stderr
, str
);
375 if (connection_open
) {
377 back
->special(TS_EOF
);
378 ssh_scp_recv(&ch
, 1);
383 static int get_password(const char *prompt
, char *str
, int maxlen
)
389 static int tried_once
= 0;
394 strncpy(str
, password
, maxlen
);
395 str
[maxlen
-1] = '\0';
401 /* GUI Adaptation - Sept 2000 */
403 if (maxlen
>0) str
[0] = '\0';
405 hin
= GetStdHandle(STD_INPUT_HANDLE
);
406 hout
= GetStdHandle(STD_OUTPUT_HANDLE
);
407 if (hin
== INVALID_HANDLE_VALUE
|| hout
== INVALID_HANDLE_VALUE
)
408 bump("Cannot get standard input/output handles");
410 GetConsoleMode(hin
, &savemode
);
411 SetConsoleMode(hin
, (savemode
& (~ENABLE_ECHO_INPUT
)) |
412 ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
);
414 WriteFile(hout
, prompt
, strlen(prompt
), &i
, NULL
);
415 ReadFile(hin
, str
, maxlen
-1, &i
, NULL
);
417 SetConsoleMode(hin
, savemode
);
419 if ((int)i
> maxlen
) i
= maxlen
-1; else i
= i
- 2;
422 WriteFile(hout
, "\r\n", 2, &i
, NULL
);
429 * Open an SSH connection to user@host and execute cmd.
431 static void do_cmd(char *host
, char *user
, char *cmd
)
433 char *err
, *realhost
;
435 if (host
== NULL
|| host
[0] == '\0')
436 bump("Empty host name");
438 /* Try to load settings for this host */
439 do_defaults(host
, &cfg
);
440 if (cfg
.host
[0] == '\0') {
441 /* No settings for this host; use defaults */
442 strncpy(cfg
.host
, host
, sizeof(cfg
.host
)-1);
443 cfg
.host
[sizeof(cfg
.host
)-1] = '\0';
448 if (user
!= NULL
&& user
[0] != '\0') {
449 strncpy(cfg
.username
, user
, sizeof(cfg
.username
)-1);
450 cfg
.username
[sizeof(cfg
.username
)-1] = '\0';
451 } else if (cfg
.username
[0] == '\0') {
452 bump("Empty user name");
455 if (cfg
.protocol
!= PROT_SSH
)
459 cfg
.port
= portnumber
;
461 strncpy(cfg
.remote_cmd
, cmd
, sizeof(cfg
.remote_cmd
));
462 cfg
.remote_cmd
[sizeof(cfg
.remote_cmd
)-1] = '\0';
467 err
= back
->init(NULL
, cfg
.host
, cfg
.port
, &realhost
);
469 bump("ssh_init: %s", err
);
471 if (verbose
&& realhost
!= NULL
)
472 tell_user(stderr
, "Connected to %s\n", realhost
);
478 * Update statistic information about current file.
480 static void print_stats(char *name
, unsigned long size
, unsigned long done
,
481 time_t start
, time_t now
)
488 /* GUI Adaptation - Sept 2000 */
490 gui_update_stats(name
, size
, ((done
*100) / size
), now
-start
);
493 ratebs
= (float) done
/ (now
- start
);
495 ratebs
= (float) done
;
500 eta
= (unsigned long) ((size
- done
) / ratebs
);
501 sprintf(etastr
, "%02ld:%02ld:%02ld",
502 eta
/ 3600, (eta
% 3600) / 60, eta
% 60);
504 pct
= (int) (100.0 * (float) done
/ size
);
506 printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
507 name
, done
/ 1024, ratebs
/ 1024.0,
516 * Find a colon in str and return a pointer to the colon.
517 * This is used to separate hostname from filename.
519 static char * colon(char *str
)
521 /* We ignore a leading colon, since the hostname cannot be
522 empty. We also ignore a colon as second character because
523 of filenames like f:myfile.txt. */
524 if (str
[0] == '\0' ||
528 while (*str
!= '\0' &&
540 * Wait for a response from the other side.
541 * Return 0 if ok, -1 if error.
543 static int response(void)
545 char ch
, resp
, rbuf
[2048];
548 if (ssh_scp_recv(&resp
, 1) <= 0)
549 bump("Lost connection");
559 case 2: /* fatal error */
561 if (ssh_scp_recv(&ch
, 1) <= 0)
562 bump("Protocol error: Lost connection");
564 } while (p
< sizeof(rbuf
) && ch
!= '\n');
567 tell_user(stderr
, "%s\n", rbuf
);
576 * Send an error message to the other side and to the screen.
577 * Increment error counter.
579 static void run_err(const char *fmt
, ...)
585 strcpy(str
, "\01scp: ");
586 vsprintf(str
+strlen(str
), fmt
, ap
);
588 back
->send(str
, strlen(str
));
589 tell_user(stderr
, "%s",str
);
594 * Execute the source part of the SCP protocol.
596 static void source(char *src
)
604 unsigned long stat_bytes
;
605 time_t stat_starttime
, stat_lasttime
;
607 attr
= GetFileAttributes(src
);
608 if (attr
== (DWORD
)-1) {
609 run_err("%s: No such file or directory", src
);
613 if ((attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0) {
616 * Avoid . and .. directories.
619 p
= strrchr(src
, '/');
621 p
= strrchr(src
, '\\');
626 if (!strcmp(p
, ".") || !strcmp(p
, ".."))
631 run_err("%s: not a regular file", src
);
636 if ((last
= strrchr(src
, '/')) == NULL
)
640 if (strrchr(last
, '\\') != NULL
)
641 last
= strrchr(last
, '\\') + 1;
642 if (last
== src
&& strchr(src
, ':') != NULL
)
643 last
= strchr(src
, ':') + 1;
645 f
= CreateFile(src
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
646 OPEN_EXISTING
, 0, 0);
647 if (f
== INVALID_HANDLE_VALUE
) {
648 run_err("%s: Cannot open file", src
);
653 FILETIME actime
, wrtime
;
654 unsigned long mtime
, atime
;
655 GetFileTime(f
, NULL
, &actime
, &wrtime
);
656 TIME_WIN_TO_POSIX(actime
, atime
);
657 TIME_WIN_TO_POSIX(wrtime
, mtime
);
658 sprintf(buf
, "T%lu 0 %lu 0\n", mtime
, atime
);
659 back
->send(buf
, strlen(buf
));
664 size
= GetFileSize(f
, NULL
);
665 sprintf(buf
, "C0644 %lu %s\n", size
, last
);
667 tell_user(stderr
, "Sending file modes: %s", buf
);
668 back
->send(buf
, strlen(buf
));
674 stat_starttime
= time(NULL
);
678 for (i
= 0; i
< size
; i
+= 4096) {
681 if (i
+ k
> size
) k
= size
- i
;
682 if (! ReadFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
683 if (statistics
) printf("\n");
684 bump("%s: Read error", src
);
686 back
->send(transbuf
, k
);
689 if (time(NULL
) != stat_lasttime
||
691 stat_lasttime
= time(NULL
);
692 print_stats(last
, size
, stat_bytes
,
693 stat_starttime
, stat_lasttime
);
704 * Recursively send the contents of a directory.
706 static void rsource(char *src
)
711 WIN32_FIND_DATA fdat
;
714 if ((last
= strrchr(src
, '/')) == NULL
)
718 if (strrchr(last
, '\\') != NULL
)
719 last
= strrchr(last
, '\\') + 1;
720 if (last
== src
&& strchr(src
, ':') != NULL
)
721 last
= strchr(src
, ':') + 1;
723 /* maybe send filetime */
725 sprintf(buf
, "D0755 0 %s\n", last
);
727 tell_user(stderr
, "Entering directory: %s", buf
);
728 back
->send(buf
, strlen(buf
));
732 sprintf(buf
, "%s/*", src
);
733 dir
= FindFirstFile(buf
, &fdat
);
734 ok
= (dir
!= INVALID_HANDLE_VALUE
);
736 if (strcmp(fdat
.cFileName
, ".") == 0 ||
737 strcmp(fdat
.cFileName
, "..") == 0) {
738 } else if (strlen(src
) + 1 + strlen(fdat
.cFileName
) >=
740 run_err("%s/%s: Name too long", src
, fdat
.cFileName
);
742 sprintf(buf
, "%s/%s", src
, fdat
.cFileName
);
745 ok
= FindNextFile(dir
, &fdat
);
750 back
->send(buf
, strlen(buf
));
755 * Execute the sink part of the SCP protocol.
757 static void sink(char *targ
)
767 unsigned long mtime
, atime
;
769 unsigned long size
, i
;
771 unsigned long stat_bytes
;
772 time_t stat_starttime
, stat_lasttime
;
775 attr
= GetFileAttributes(targ
);
776 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0)
779 if (targetshouldbedirectory
&& !targisdir
)
780 bump("%s: Not a directory", targ
);
786 if (ssh_scp_recv(&ch
, 1) <= 0)
789 bump("Protocol error: Unexpected newline");
793 if (ssh_scp_recv(&ch
, 1) <= 0)
794 bump("Lost connection");
796 } while (i
< sizeof(buf
) && ch
!= '\n');
799 case '\01': /* error */
800 tell_user(stderr
, "%s\n", buf
+1);
803 case '\02': /* fatal error */
809 if (sscanf(buf
, "T%ld %*d %ld %*d",
810 &mtime
, &atime
) == 2) {
815 bump("Protocol error: Illegal time format");
820 bump("Protocol error: Expected control record");
823 if (sscanf(buf
+1, "%u %lu %[^\n]", &mode
, &size
, namebuf
) != 3)
824 bump("Protocol error: Illegal file descriptor format");
833 strcpy(namebuf
, targ
);
835 attr
= GetFileAttributes(namebuf
);
836 exists
= (attr
!= (DWORD
)-1);
839 if (exists
&& (attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0) {
840 run_err("%s: Not a directory", namebuf
);
844 if (! CreateDirectory(namebuf
, NULL
)) {
845 run_err("%s: Cannot create directory",
851 /* can we set the timestamp for directories ? */
855 f
= CreateFile(namebuf
, GENERIC_WRITE
, 0, NULL
,
856 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
857 if (f
== INVALID_HANDLE_VALUE
) {
858 run_err("%s: Cannot create file", namebuf
);
866 stat_starttime
= time(NULL
);
868 if ((stat_name
= strrchr(namebuf
, '/')) == NULL
)
872 if (strrchr(stat_name
, '\\') != NULL
)
873 stat_name
= strrchr(stat_name
, '\\') + 1;
876 for (i
= 0; i
< size
; i
+= 4096) {
879 if (i
+ k
> size
) k
= size
- i
;
880 if (ssh_scp_recv(transbuf
, k
) == 0)
881 bump("Lost connection");
882 if (wrerror
) continue;
883 if (! WriteFile(f
, transbuf
, k
, &j
, NULL
) || j
!= k
) {
886 printf("\r%-25.25s | %50s\n",
888 "Write error.. waiting for end of file");
893 if (time(NULL
) > stat_lasttime
||
895 stat_lasttime
= time(NULL
);
896 print_stats(stat_name
, size
, stat_bytes
,
897 stat_starttime
, stat_lasttime
);
904 FILETIME actime
, wrtime
;
905 TIME_POSIX_TO_WIN(atime
, actime
);
906 TIME_POSIX_TO_WIN(mtime
, wrtime
);
907 SetFileTime(f
, NULL
, &actime
, &wrtime
);
912 run_err("%s: Write error", namebuf
);
920 * We will copy local files to a remote server.
922 static void toremote(int argc
, char *argv
[])
924 char *src
, *targ
, *host
, *user
;
930 /* Separate host from filename */
934 bump("targ == NULL in toremote()");
938 /* Substitute "." for emtpy target */
940 /* Separate host and username */
942 host
= strrchr(host
, '@');
953 /* Find out if the source filespec covers multiple files
954 if so, we should set the targetshouldbedirectory flag */
956 WIN32_FIND_DATA fdat
;
957 if (colon(argv
[0]) != NULL
)
958 bump("%s: Remote to remote not supported", argv
[0]);
959 fh
= FindFirstFile(argv
[0], &fdat
);
960 if (fh
== INVALID_HANDLE_VALUE
)
961 bump("%s: No such file or directory\n", argv
[0]);
962 if (FindNextFile(fh
, &fdat
))
963 targetshouldbedirectory
= 1;
967 cmd
= smalloc(strlen(targ
) + 100);
968 sprintf(cmd
, "scp%s%s%s%s -t %s",
969 verbose ?
" -v" : "",
970 recursive ?
" -r" : "",
971 preserve ?
" -p" : "",
972 targetshouldbedirectory ?
" -d" : "",
974 do_cmd(host
, user
, cmd
);
979 for (i
= 0; i
< argc
- 1; i
++) {
981 WIN32_FIND_DATA fdat
;
983 if (colon(src
) != NULL
) {
984 tell_user(stderr
, "%s: Remote to remote not supported\n", src
);
988 dir
= FindFirstFile(src
, &fdat
);
989 if (dir
== INVALID_HANDLE_VALUE
) {
990 run_err("%s: No such file or directory", src
);
996 if (strlen(src
) + strlen(fdat
.cFileName
) >=
998 tell_user(stderr
, "%s: Name too long", src
);
1001 strcpy(namebuf
, src
);
1002 if ((last
= strrchr(namebuf
, '/')) == NULL
)
1006 if (strrchr(last
, '\\') != NULL
)
1007 last
= strrchr(last
, '\\') + 1;
1008 if (last
== namebuf
&& strrchr(namebuf
, ':') != NULL
)
1009 last
= strchr(namebuf
, ':') + 1;
1010 strcpy(last
, fdat
.cFileName
);
1012 } while (FindNextFile(dir
, &fdat
));
1018 * We will copy files from a remote server to the local machine.
1020 static void tolocal(int argc
, char *argv
[])
1022 char *src
, *targ
, *host
, *user
;
1026 bump("More than one remote source not supported");
1031 /* Separate host from filename */
1035 bump("Local to local copy not supported");
1039 /* Substitute "." for empty filename */
1041 /* Separate username and hostname */
1043 host
= strrchr(host
, '@');
1053 cmd
= smalloc(strlen(src
) + 100);
1054 sprintf(cmd
, "scp%s%s%s%s -f %s",
1055 verbose ?
" -v" : "",
1056 recursive ?
" -r" : "",
1057 preserve ?
" -p" : "",
1058 targetshouldbedirectory ?
" -d" : "",
1060 do_cmd(host
, user
, cmd
);
1067 * We will issue a list command to get a remote directory.
1069 static void get_dir_list(int argc
, char *argv
[])
1071 char *src
, *host
, *user
;
1077 /* Separate host from filename */
1081 bump("Local to local copy not supported");
1085 /* Substitute "." for empty filename */
1087 /* Separate username and hostname */
1089 host
= strrchr(host
, '@');
1099 cmd
= smalloc(4*strlen(src
) + 100);
1100 strcpy(cmd
, "ls -la '");
1101 p
= cmd
+ strlen(cmd
);
1102 for (q
= src
; *q
; q
++) {
1104 *p
++ = '\''; *p
++ = '\\'; *p
++ = '\''; *p
++ = '\'';
1112 do_cmd(host
, user
, cmd
);
1115 while (ssh_scp_recv(&c
, 1) > 0)
1116 tell_char(stdout
, c
);
1120 * Initialize the Win$ock driver.
1122 static void init_winsock(void)
1127 winsock_ver
= MAKEWORD(1, 1);
1128 if (WSAStartup(winsock_ver
, &wsadata
))
1129 bump("Unable to initialise WinSock");
1130 if (LOBYTE(wsadata
.wVersion
) != 1 ||
1131 HIBYTE(wsadata
.wVersion
) != 1)
1132 bump("WinSock version is incompatible with 1.1");
1136 * Short description of parameters.
1138 static void usage(void)
1140 printf("PuTTY Secure Copy client\n");
1141 printf("%s\n", ver
);
1142 printf("Usage: pscp [options] [user@]host:source target\n");
1143 printf(" pscp [options] source [source...] [user@]host:target\n");
1144 printf(" pscp [options] -ls user@host:filespec\n");
1145 printf("Options:\n");
1146 printf(" -p preserve file attributes\n");
1147 printf(" -q quiet, don't show statistics\n");
1148 printf(" -r copy directories recursively\n");
1149 printf(" -v show verbose messages\n");
1150 printf(" -P port connect to specified port\n");
1151 printf(" -pw passw login with specified password\n");
1152 /* GUI Adaptation - Sept 2000 */
1153 printf(" -gui hWnd GUI mode with the windows handle for receiving messages\n");
1158 * Main program (no, really?)
1160 int main(int argc
, char *argv
[])
1165 default_protocol
= PROT_TELNET
;
1167 flags
= FLAG_STDERR
;
1168 ssh_get_password
= &get_password
;
1171 for (i
= 1; i
< argc
; i
++) {
1172 if (argv
[i
][0] != '-')
1174 if (strcmp(argv
[i
], "-v") == 0)
1175 verbose
= 1, flags
|= FLAG_VERBOSE
;
1176 else if (strcmp(argv
[i
], "-r") == 0)
1178 else if (strcmp(argv
[i
], "-p") == 0)
1180 else if (strcmp(argv
[i
], "-q") == 0)
1182 else if (strcmp(argv
[i
], "-h") == 0 ||
1183 strcmp(argv
[i
], "-?") == 0)
1185 else if (strcmp(argv
[i
], "-P") == 0 && i
+1 < argc
)
1186 portnumber
= atoi(argv
[++i
]);
1187 else if (strcmp(argv
[i
], "-pw") == 0 && i
+1 < argc
)
1188 password
= argv
[++i
];
1189 else if (strcmp(argv
[i
], "-gui") == 0 && i
+1 < argc
) {
1190 gui_hwnd
= argv
[++i
];
1192 } else if (strcmp(argv
[i
], "-ls") == 0)
1194 else if (strcmp(argv
[i
], "--") == 0)
1205 get_dir_list(argc
, argv
);
1212 targetshouldbedirectory
= 1;
1214 if (colon(argv
[argc
-1]) != NULL
)
1215 toremote(argc
, argv
);
1217 tolocal(argc
, argv
);
1220 if (connection_open
) {
1222 back
->special(TS_EOF
);
1223 ssh_scp_recv(&ch
, 1);
1228 /* GUI Adaptation - August 2000 */
1230 unsigned int msg_id
= WM_RET_ERR_CNT
;
1231 if (list
) msg_id
= WM_LS_RET_ERR_CNT
;
1232 while (!PostMessage( (HWND
)atoi(gui_hwnd
), msg_id
, (WPARAM
)errs
, 0/*lParam*/ ) )
1235 return (errs
== 0 ?
0 : 1);