07d9aa13 |
1 | /* |
2 | * scp.c - Scp (Secure Copy) client for PuTTY. |
3 | * Joris van Rantwijk, Aug 1999. |
4 | * |
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. |
7 | */ |
8 | |
9 | |
10 | #include <windows.h> |
11 | #include <winsock.h> |
12 | #include <stdlib.h> |
13 | #include <stdio.h> |
14 | #include <string.h> |
15 | #include <time.h> |
16 | |
17 | #define PUTTY_DO_GLOBALS |
18 | #include "putty.h" |
19 | #include "scp.h" |
20 | |
21 | |
22 | #define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \ |
23 | ((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000) |
24 | #define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \ |
25 | ((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600)) |
26 | |
27 | |
28 | int verbose = 0; |
29 | static int recursive = 0; |
30 | static int preserve = 0; |
31 | static int targetshouldbedirectory = 0; |
32 | static int statistics = 1; |
33 | static int errs = 0; |
34 | static int connection_open = 0; |
35 | |
36 | static void source(char *src); |
37 | static void rsource(char *src); |
38 | static void sink(char *targ); |
39 | |
40 | |
41 | /* |
42 | * Print an error message and perform a fatal exit. |
43 | */ |
44 | void fatalbox(char *fmt, ...) |
45 | { |
46 | va_list ap; |
47 | va_start(ap, fmt); |
48 | fprintf(stderr, "Fatal: "); |
49 | vfprintf(stderr, fmt, ap); |
50 | fprintf(stderr, "\n"); |
51 | va_end(ap); |
52 | exit(1); |
53 | } |
54 | |
55 | |
56 | /* |
57 | * Print an error message and exit after closing the SSH link. |
58 | */ |
59 | static void bump(char *fmt, ...) |
60 | { |
61 | va_list ap; |
62 | va_start(ap, fmt); |
63 | fprintf(stderr, "Fatal: "); |
64 | vfprintf(stderr, fmt, ap); |
65 | fprintf(stderr, "\n"); |
66 | va_end(ap); |
67 | if (connection_open) { |
68 | char ch; |
69 | ssh_send_eof(); |
70 | ssh_recv(&ch, 1); |
71 | } |
72 | exit(1); |
73 | } |
74 | |
75 | |
76 | void ssh_get_password(char *prompt, char *str, int maxlen) |
77 | { |
78 | HANDLE hin, hout; |
79 | DWORD savemode, i; |
80 | |
81 | hin = GetStdHandle(STD_INPUT_HANDLE); |
82 | hout = GetStdHandle(STD_OUTPUT_HANDLE); |
83 | if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) |
84 | bump("Cannot get standard input/output handles"); |
85 | |
86 | GetConsoleMode(hin, &savemode); |
87 | SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) | |
88 | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT); |
89 | |
90 | WriteFile(hout, prompt, strlen(prompt), &i, NULL); |
91 | ReadFile(hin, str, maxlen-1, &i, NULL); |
92 | |
93 | SetConsoleMode(hin, savemode); |
94 | |
95 | if (i > maxlen) i = maxlen-1; else i = i - 2; |
96 | str[i] = '\0'; |
97 | |
98 | WriteFile(hout, "\r\n", 2, &i, NULL); |
99 | } |
100 | |
101 | |
102 | /* |
103 | * Open an SSH connection to user@host and execute cmd. |
104 | */ |
105 | static void do_cmd(char *host, char *user, char *cmd) |
106 | { |
107 | char *err, *realhost; |
108 | |
109 | if (host == NULL || host[0] == '\0') |
110 | bump("Empty host name"); |
111 | |
112 | /* Try to load settings for this host */ |
113 | do_defaults(host); |
114 | if (cfg.host[0] == '\0') { |
115 | /* No settings for this host; use defaults */ |
116 | strncpy(cfg.host, host, sizeof(cfg.host)-1); |
117 | cfg.host[sizeof(cfg.host)-1] = '\0'; |
118 | cfg.port = 22; |
119 | } |
120 | |
121 | /* Set username */ |
122 | if (user != NULL && user[0] != '\0') { |
123 | strncpy(cfg.username, user, sizeof(cfg.username)-1); |
124 | cfg.username[sizeof(cfg.username)-1] = '\0'; |
125 | cfg.port = 22; |
126 | } else if (cfg.username[0] == '\0') { |
127 | bump("Empty user name"); |
128 | } |
129 | |
130 | if (cfg.protocol != PROT_SSH) |
131 | cfg.port = 22; |
132 | |
133 | err = ssh_init(cfg.host, cfg.port, cmd, &realhost); |
134 | if (err != NULL) |
135 | bump("ssh_init: %s", err); |
136 | if (verbose && realhost != NULL) |
137 | fprintf(stderr, "Connected to %s\n", realhost); |
138 | |
139 | connection_open = 1; |
140 | } |
141 | |
142 | |
143 | /* |
144 | * Update statistic information about current file. |
145 | */ |
146 | static void print_stats(char *name, unsigned long size, unsigned long done, |
147 | unsigned long start, unsigned long now) |
148 | { |
149 | float ratebs; |
150 | unsigned long eta; |
151 | char etastr[10]; |
152 | int pct; |
153 | |
154 | if (now > start) |
155 | ratebs = (float) done / (now - start); |
156 | else |
157 | ratebs = (float) done; |
158 | |
159 | if (ratebs < 1.0) |
160 | eta = size - done; |
161 | else |
162 | eta = (size - done) / ratebs; |
163 | sprintf(etastr, "%02d:%02d:%02d", |
164 | eta / 3600, (eta % 3600) / 60, eta % 60); |
165 | |
166 | pct = 100.0 * (float) done / size; |
167 | |
168 | printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%", |
169 | name, done / 1024, ratebs / 1024.0, |
170 | etastr, pct); |
171 | |
172 | if (done == size) |
173 | printf("\n"); |
174 | } |
175 | |
176 | |
177 | /* |
178 | * Find a colon in str and return a pointer to the colon. |
179 | * This is used to seperate hostname from filename. |
180 | */ |
181 | static char * colon(char *str) |
182 | { |
183 | /* We ignore a leading colon, since the hostname cannot be |
184 | empty. We also ignore a colon as second character because |
185 | of filenames like f:myfile.txt. */ |
186 | if (str[0] == '\0' || |
187 | str[0] == ':' || |
188 | str[1] == ':') |
189 | return (NULL); |
190 | while (*str != '\0' && |
191 | *str != ':' && |
192 | *str != '/' && |
193 | *str != '\\') |
194 | str++; |
195 | if (*str == ':') |
196 | return (str); |
197 | else |
198 | return (NULL); |
199 | } |
200 | |
201 | |
202 | /* |
203 | * Wait for a response from the other side. |
204 | * Return 0 if ok, -1 if error. |
205 | */ |
206 | static int response(void) |
207 | { |
208 | char ch, resp, rbuf[2048]; |
209 | int p; |
210 | |
211 | if (ssh_recv(&resp, 1) <= 0) |
212 | bump("Lost connection"); |
213 | |
214 | p = 0; |
215 | switch (resp) { |
216 | case 0: /* ok */ |
217 | return (0); |
218 | default: |
219 | rbuf[p++] = resp; |
220 | /* fallthrough */ |
221 | case 1: /* error */ |
222 | case 2: /* fatal error */ |
223 | do { |
224 | if (ssh_recv(&ch, 1) <= 0) |
225 | bump("Protocol error: Lost connection"); |
226 | rbuf[p++] = ch; |
227 | } while (p < sizeof(rbuf) && ch != '\n'); |
228 | rbuf[p-1] = '\0'; |
229 | if (resp == 1) |
230 | fprintf(stderr, "%s\n", rbuf); |
231 | else |
232 | bump("%s", rbuf); |
233 | errs++; |
234 | return (-1); |
235 | } |
236 | } |
237 | |
238 | |
239 | /* |
240 | * Send an error message to the other side and to the screen. |
241 | * Increment error counter. |
242 | */ |
243 | static void run_err(const char *fmt, ...) |
244 | { |
245 | char str[2048]; |
246 | va_list ap; |
247 | va_start(ap, fmt); |
248 | errs++; |
249 | strcpy(str, "\01scp: "); |
250 | vsprintf(str+strlen(str), fmt, ap); |
251 | strcat(str, "\n"); |
252 | ssh_send(str, strlen(str)); |
253 | vfprintf(stderr, fmt, ap); |
254 | fprintf(stderr, "\n"); |
255 | va_end(ap); |
256 | } |
257 | |
258 | |
259 | /* |
260 | * Execute the source part of the SCP protocol. |
261 | */ |
262 | static void source(char *src) |
263 | { |
264 | char buf[2048]; |
265 | unsigned long size; |
266 | char *last; |
267 | HANDLE f; |
268 | DWORD attr; |
269 | unsigned long i; |
270 | unsigned long stat_bytes; |
271 | unsigned long stat_starttime, stat_lasttime; |
272 | |
273 | attr = GetFileAttributes(src); |
274 | if (attr == -1) { |
275 | run_err("%s: No such file or directory", src); |
276 | return; |
277 | } |
278 | |
279 | if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
280 | if (recursive) |
281 | rsource(src); |
282 | else |
283 | run_err("%s: not a regular file", src); |
284 | return; |
285 | } |
286 | |
287 | if ((last = strrchr(src, '/')) == NULL) |
288 | last = src; |
289 | else |
290 | last++; |
291 | if (strrchr(last, '\\') != NULL) |
292 | last = strrchr(last, '\\') + 1; |
293 | if (last == src && strchr(src, ':') != NULL) |
294 | last = strchr(src, ':') + 1; |
295 | |
296 | f = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, NULL, |
297 | OPEN_EXISTING, 0, 0); |
298 | if (f == INVALID_HANDLE_VALUE) { |
299 | run_err("%s: Cannot open file"); |
300 | return; |
301 | } |
302 | |
303 | if (preserve) { |
304 | FILETIME actime, wrtime; |
305 | unsigned long mtime, atime; |
306 | GetFileTime(f, NULL, &actime, &wrtime); |
307 | TIME_WIN_TO_POSIX(actime, atime); |
308 | TIME_WIN_TO_POSIX(wrtime, mtime); |
309 | sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime); |
310 | ssh_send(buf, strlen(buf)); |
311 | if (response()) |
312 | return; |
313 | } |
314 | |
315 | size = GetFileSize(f, NULL); |
316 | sprintf(buf, "C0644 %lu %s\n", size, last); |
317 | if (verbose) |
318 | fprintf(stderr, "Sending file modes: %s", buf); |
319 | ssh_send(buf, strlen(buf)); |
320 | if (response()) |
321 | return; |
322 | |
323 | if (statistics) { |
324 | stat_bytes = 0; |
325 | stat_starttime = time(NULL); |
326 | stat_lasttime = 0; |
327 | } |
328 | |
329 | for (i = 0; i < size; i += 4096) { |
330 | char transbuf[4096]; |
331 | DWORD j, k = 4096; |
332 | if (i + k > size) k = size - i; |
333 | if (! ReadFile(f, transbuf, k, &j, NULL) || j != k) { |
334 | if (statistics) printf("\n"); |
335 | bump("%s: Read error", src); |
336 | } |
337 | ssh_send(transbuf, k); |
338 | if (statistics) { |
339 | stat_bytes += k; |
340 | if (time(NULL) != stat_lasttime || |
341 | i + k == size) { |
342 | stat_lasttime = time(NULL); |
343 | print_stats(last, size, stat_bytes, |
344 | stat_starttime, stat_lasttime); |
345 | } |
346 | } |
347 | } |
348 | CloseHandle(f); |
349 | |
350 | ssh_send("", 1); |
351 | (void) response(); |
352 | } |
353 | |
354 | |
355 | /* |
356 | * Recursively send the contents of a directory. |
357 | */ |
358 | static void rsource(char *src) |
359 | { |
360 | char buf[2048]; |
361 | char *last; |
362 | HANDLE dir; |
363 | WIN32_FIND_DATA fdat; |
364 | int ok; |
365 | |
366 | if ((last = strrchr(src, '/')) == NULL) |
367 | last = src; |
368 | else |
369 | last++; |
370 | if (strrchr(last, '\\') != NULL) |
371 | last = strrchr(last, '\\') + 1; |
372 | if (last == src && strchr(src, ':') != NULL) |
373 | last = strchr(src, ':') + 1; |
374 | |
375 | /* maybe send filetime */ |
376 | |
377 | sprintf(buf, "D0755 0 %s\n", last); |
378 | if (verbose) |
379 | fprintf(stderr, "Entering directory: %s", buf); |
380 | ssh_send(buf, strlen(buf)); |
381 | if (response()) |
382 | return; |
383 | |
384 | sprintf(buf, "%s/*", src); |
385 | dir = FindFirstFile(buf, &fdat); |
386 | ok = (dir != INVALID_HANDLE_VALUE); |
387 | while (ok) { |
388 | if (strcmp(fdat.cFileName, ".") == 0 || |
389 | strcmp(fdat.cFileName, "..") == 0) { |
390 | } else if (strlen(src) + 1 + strlen(fdat.cFileName) >= |
391 | sizeof(buf)) { |
392 | run_err("%s/%s: Name too long", src, fdat.cFileName); |
393 | } else { |
394 | sprintf(buf, "%s/%s", src, fdat.cFileName); |
395 | source(buf); |
396 | } |
397 | ok = FindNextFile(dir, &fdat); |
398 | } |
399 | FindClose(dir); |
400 | |
401 | sprintf(buf, "E\n"); |
402 | ssh_send(buf, strlen(buf)); |
403 | (void) response(); |
404 | } |
405 | |
406 | |
407 | /* |
408 | * Execute the sink part of the SCP protocol. |
409 | */ |
410 | static void sink(char *targ) |
411 | { |
412 | char buf[2048]; |
413 | char namebuf[2048]; |
414 | char ch; |
415 | int targisdir = 0; |
416 | int settime = 0; |
417 | int exists; |
418 | DWORD attr; |
419 | HANDLE f; |
420 | unsigned long mtime, atime; |
421 | unsigned int mode; |
422 | unsigned long size, i; |
423 | int wrerror = 0; |
424 | unsigned long stat_bytes; |
425 | unsigned long stat_starttime, stat_lasttime; |
426 | char *stat_name; |
427 | |
428 | attr = GetFileAttributes(targ); |
429 | if (attr != -1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) |
430 | targisdir = 1; |
431 | |
432 | if (targetshouldbedirectory && !targisdir) |
433 | bump("%s: Not a directory", targ); |
434 | |
435 | ssh_send("", 1); |
436 | while (1) { |
437 | settime = 0; |
438 | gottime: |
439 | if (ssh_recv(&ch, 1) <= 0) |
440 | return; |
441 | if (ch == '\n') |
442 | bump("Protocol error: Unexpected newline"); |
443 | i = 0; |
444 | buf[i++] = ch; |
445 | do { |
446 | if (ssh_recv(&ch, 1) <= 0) |
447 | bump("Lost connection"); |
448 | buf[i++] = ch; |
449 | } while (i < sizeof(buf) && ch != '\n'); |
450 | buf[i-1] = '\0'; |
451 | switch (buf[0]) { |
452 | case '\01': /* error */ |
453 | fprintf(stderr, "%s\n", buf+1); |
454 | errs++; |
455 | continue; |
456 | case '\02': /* fatal error */ |
457 | bump("%s", buf+1); |
458 | case 'E': |
459 | ssh_send("", 1); |
460 | return; |
461 | case 'T': |
462 | if (sscanf(buf, "T%d %*d %d %*d", |
463 | &mtime, &atime) == 2) { |
464 | settime = 1; |
465 | ssh_send("", 1); |
466 | goto gottime; |
467 | } |
468 | bump("Protocol error: Illegal time format"); |
469 | case 'C': |
470 | case 'D': |
471 | break; |
472 | default: |
473 | bump("Protocol error: Expected control record"); |
474 | } |
475 | |
476 | if (sscanf(buf+1, "%u %u %[^\n]", &mode, &size, namebuf) != 3) |
477 | bump("Protocol error: Illegal file descriptor format"); |
478 | if (targisdir) { |
479 | char t[2048]; |
480 | strcpy(t, targ); |
481 | if (targ[0] != '\0') |
482 | strcat(t, "/"); |
483 | strcat(t, namebuf); |
484 | strcpy(namebuf, t); |
485 | } else { |
486 | strcpy(namebuf, targ); |
487 | } |
488 | attr = GetFileAttributes(namebuf); |
489 | exists = (attr != -1); |
490 | |
491 | if (buf[0] == 'D') { |
492 | if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { |
493 | run_err("%s: Not a directory", namebuf); |
494 | continue; |
495 | } |
496 | if (!exists) { |
497 | if (! CreateDirectory(namebuf, NULL)) { |
498 | run_err("%s: Cannot create directory", |
499 | namebuf); |
500 | continue; |
501 | } |
502 | } |
503 | sink(namebuf); |
504 | /* can we set the timestamp for directories ? */ |
505 | continue; |
506 | } |
507 | |
508 | f = CreateFile(namebuf, GENERIC_WRITE, 0, NULL, |
509 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); |
510 | if (f == INVALID_HANDLE_VALUE) { |
511 | run_err("%s: Cannot create file", namebuf); |
512 | continue; |
513 | } |
514 | |
515 | ssh_send("", 1); |
516 | |
517 | if (statistics) { |
518 | stat_bytes = 0; |
519 | stat_starttime = time(NULL); |
520 | stat_lasttime = 0; |
521 | if ((stat_name = strrchr(namebuf, '/')) == NULL) |
522 | stat_name = namebuf; |
523 | else |
524 | stat_name++; |
525 | if (strrchr(stat_name, '\\') != NULL) |
526 | stat_name = strrchr(stat_name, '\\') + 1; |
527 | } |
528 | |
529 | for (i = 0; i < size; i += 4096) { |
530 | char transbuf[4096]; |
531 | int j, k = 4096; |
532 | if (i + k > size) k = size - i; |
533 | if (ssh_recv(transbuf, k) == 0) |
534 | bump("Lost connection"); |
535 | if (wrerror) continue; |
536 | if (! WriteFile(f, transbuf, k, &j, NULL) || j != k) { |
537 | wrerror = 1; |
538 | if (statistics) |
539 | printf("\r%-25.25s | %50s\n", |
540 | stat_name, |
541 | "Write error.. waiting for end of file"); |
542 | continue; |
543 | } |
544 | if (statistics) { |
545 | stat_bytes += k; |
546 | if (time(NULL) > stat_lasttime || |
547 | i + k == size) { |
548 | stat_lasttime = time(NULL); |
549 | print_stats(stat_name, size, stat_bytes, |
550 | stat_starttime, stat_lasttime); |
551 | } |
552 | } |
553 | } |
554 | (void) response(); |
555 | |
556 | if (settime) { |
557 | FILETIME actime, wrtime; |
558 | TIME_POSIX_TO_WIN(atime, actime); |
559 | TIME_POSIX_TO_WIN(mtime, wrtime); |
560 | SetFileTime(f, NULL, &actime, &wrtime); |
561 | } |
562 | |
563 | CloseHandle(f); |
564 | if (wrerror) { |
565 | run_err("%s: Write error", namebuf); |
566 | continue; |
567 | } |
568 | ssh_send("", 1); |
569 | } |
570 | } |
571 | |
572 | |
573 | /* |
574 | * We will copy local files to a remote server. |
575 | */ |
576 | static void toremote(int argc, char *argv[]) |
577 | { |
578 | char *src, *targ, *host, *user; |
579 | char *cmd; |
580 | int i; |
581 | |
582 | targ = argv[argc-1]; |
583 | |
584 | /* Seperate host from filename */ |
585 | host = targ; |
586 | targ = colon(targ); |
587 | if (targ == NULL) |
588 | bump("targ == NULL in toremote()"); |
589 | *targ++ = '\0'; |
590 | if (*targ == '\0') |
591 | targ = "."; |
592 | /* Substitute "." for emtpy target */ |
593 | |
594 | /* Seperate host and username */ |
595 | user = host; |
596 | host = strrchr(host, '@'); |
597 | if (host == NULL) { |
598 | host = user; |
599 | user = NULL; |
600 | } else { |
601 | *host++ = '\0'; |
602 | if (*user == '\0') |
603 | user = NULL; |
604 | } |
605 | |
606 | if (argc == 2) { |
607 | /* Find out if the source filespec covers multiple files |
608 | if so, we should set the targetshouldbedirectory flag */ |
609 | HANDLE fh; |
610 | WIN32_FIND_DATA fdat; |
611 | if (colon(argv[0]) != NULL) |
612 | bump("%s: Remote to remote not supported", argv[0]); |
613 | fh = FindFirstFile(argv[0], &fdat); |
614 | if (fh == INVALID_HANDLE_VALUE) |
615 | bump("%s: No such file or directory\n", argv[0]); |
616 | if (FindNextFile(fh, &fdat)) |
617 | targetshouldbedirectory = 1; |
618 | FindClose(fh); |
619 | } |
620 | |
621 | cmd = smalloc(strlen(targ) + 100); |
622 | sprintf(cmd, "scp%s%s%s%s -t %s", |
623 | verbose ? " -v" : "", |
624 | recursive ? " -r" : "", |
625 | preserve ? " -p" : "", |
626 | targetshouldbedirectory ? " -d" : "", |
627 | targ); |
628 | do_cmd(host, user, cmd); |
629 | sfree(cmd); |
630 | |
631 | (void) response(); |
632 | |
633 | for (i = 0; i < argc - 1; i++) { |
634 | HANDLE dir; |
635 | WIN32_FIND_DATA fdat; |
636 | src = argv[i]; |
637 | if (colon(src) != NULL) { |
638 | fprintf(stderr, |
639 | "%s: Remote to remote not supported\n", src); |
640 | errs++; |
641 | continue; |
642 | } |
643 | dir = FindFirstFile(src, &fdat); |
644 | if (dir == INVALID_HANDLE_VALUE) { |
645 | run_err("%s: No such file or directory", src); |
646 | continue; |
647 | } |
648 | do { |
649 | char *last; |
650 | char namebuf[2048]; |
651 | if (strlen(src) + strlen(fdat.cFileName) >= |
652 | sizeof(namebuf)) { |
653 | fprintf(stderr, "%s: Name too long", src); |
654 | continue; |
655 | } |
656 | strcpy(namebuf, src); |
657 | if ((last = strrchr(namebuf, '/')) == NULL) |
658 | last = namebuf; |
659 | else |
660 | last++; |
661 | if (strrchr(last, '\\') != NULL) |
662 | last = strrchr(last, '\\') + 1; |
663 | if (last == namebuf && strrchr(namebuf, ':') != NULL) |
664 | last = strchr(namebuf, ':') + 1; |
665 | strcpy(last, fdat.cFileName); |
666 | source(namebuf); |
667 | } while (FindNextFile(dir, &fdat)); |
668 | FindClose(dir); |
669 | } |
670 | } |
671 | |
672 | |
673 | /* |
674 | * We will copy files from a remote server to the local machine. |
675 | */ |
676 | static void tolocal(int argc, char *argv[]) |
677 | { |
678 | char *src, *targ, *host, *user; |
679 | char *cmd; |
680 | |
681 | if (argc != 2) |
682 | bump("More than one remote source not supported"); |
683 | |
684 | src = argv[0]; |
685 | targ = argv[1]; |
686 | |
687 | /* Seperate host from filename */ |
688 | host = src; |
689 | src = colon(src); |
690 | if (src == NULL) |
691 | bump("Local to local copy not supported"); |
692 | *src++ = '\0'; |
693 | if (*src == '\0') |
694 | src = "."; |
695 | /* Substitute "." for empty filename */ |
696 | |
697 | /* Seperate username and hostname */ |
698 | user = host; |
699 | host = strrchr(host, '@'); |
700 | if (host == NULL) { |
701 | host = user; |
702 | user = NULL; |
703 | } else { |
704 | *host++ = '\0'; |
705 | if (*user == '\0') |
706 | user = NULL; |
707 | } |
708 | |
709 | cmd = smalloc(strlen(src) + 100); |
710 | sprintf(cmd, "scp%s%s%s%s -f %s", |
711 | verbose ? " -v" : "", |
712 | recursive ? " -r" : "", |
713 | preserve ? " -p" : "", |
714 | targetshouldbedirectory ? " -d" : "", |
715 | src); |
716 | do_cmd(host, user, cmd); |
717 | sfree(cmd); |
718 | |
719 | sink(targ); |
720 | } |
721 | |
722 | |
723 | /* |
724 | * Initialize the Win$ock driver. |
725 | */ |
726 | static void init_winsock() |
727 | { |
728 | WORD winsock_ver; |
729 | WSADATA wsadata; |
730 | |
731 | winsock_ver = MAKEWORD(1, 1); |
732 | if (WSAStartup(winsock_ver, &wsadata)) |
733 | bump("Unable to initialise WinSock"); |
734 | if (LOBYTE(wsadata.wVersion) != 1 || |
735 | HIBYTE(wsadata.wVersion) != 1) |
736 | bump("WinSock version is incompatible with 1.1"); |
737 | } |
738 | |
739 | |
740 | /* |
741 | * Short description of parameters. |
742 | */ |
743 | static void usage() |
744 | { |
745 | printf("PuTTY Secure Copy client\n"); |
746 | printf("%s\n", ver); |
747 | printf("usage: scp [-p] [-q] [-r] [-v] [user@]host:source target\n"); |
748 | printf(" scp [-p] [-q] [-r] [-v] source [source..]" |
749 | " [user@]host:target\n"); |
750 | exit(1); |
751 | } |
752 | |
753 | |
754 | /* |
755 | * Main program (no, really?) |
756 | */ |
757 | int main(int argc, char *argv[]) |
758 | { |
759 | int i; |
760 | |
761 | init_winsock(); |
762 | |
763 | for (i = 1; i < argc; i++) { |
764 | if (argv[i][0] != '-') |
765 | break; |
766 | if (strcmp(argv[i], "-v") == 0) |
767 | verbose = 1; |
768 | else if (strcmp(argv[i], "-r") == 0) |
769 | recursive = 1; |
770 | else if (strcmp(argv[i], "-p") == 0) |
771 | preserve = 1; |
772 | else if (strcmp(argv[i], "-q") == 0) |
773 | statistics = 0; |
774 | else if (strcmp(argv[i], "-h") == 0 || |
775 | strcmp(argv[i], "-?") == 0) |
776 | usage(); |
777 | else if (strcmp(argv[i], "--") == 0) |
778 | { i++; break; } |
779 | else |
780 | usage(); |
781 | } |
782 | argc -= i; |
783 | argv += i; |
784 | |
785 | if (argc < 2) |
786 | usage(); |
787 | if (argc > 2) |
788 | targetshouldbedirectory = 1; |
789 | |
790 | if (colon(argv[argc-1]) != NULL) |
791 | toremote(argc, argv); |
792 | else |
793 | tolocal(argc, argv); |
794 | |
795 | if (connection_open) { |
796 | char ch; |
797 | ssh_send_eof(); |
798 | ssh_recv(&ch, 1); |
799 | } |
800 | WSACleanup(); |
801 | random_save_seed(); |
802 | |
803 | return (errs == 0 ? 0 : 1); |
804 | } |
805 | |
806 | /* end */ |