OSDN Git Service

Change to specify explicitly code pages.
[ffftp/ffftp.git] / putty / PSCP.C
1 /*\r
2  * scp.c  -  Scp (Secure Copy) client for PuTTY.\r
3  * Joris van Rantwijk, Simon Tatham\r
4  *\r
5  * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen.\r
6  * They, in turn, used stuff from BSD rcp.\r
7  * \r
8  * (SGT, 2001-09-10: Joris van Rantwijk assures me that although\r
9  * this file as originally submitted was inspired by, and\r
10  * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any\r
11  * actual code duplicated, so the above comment shouldn't give rise\r
12  * to licensing issues.)\r
13  */\r
14 \r
15 #include <stdlib.h>\r
16 #include <stdio.h>\r
17 #include <string.h>\r
18 #include <limits.h>\r
19 #include <time.h>\r
20 #include <assert.h>\r
21 \r
22 #define PUTTY_DO_GLOBALS\r
23 #include "putty.h"\r
24 #include "psftp.h"\r
25 #include "ssh.h"\r
26 #include "sftp.h"\r
27 #include "storage.h"\r
28 #include "int64.h"\r
29 \r
30 static int list = 0;\r
31 static int verbose = 0;\r
32 static int recursive = 0;\r
33 static int preserve = 0;\r
34 static int targetshouldbedirectory = 0;\r
35 static int statistics = 1;\r
36 static int prev_stats_len = 0;\r
37 static int scp_unsafe_mode = 0;\r
38 static int errs = 0;\r
39 static int try_scp = 1;\r
40 static int try_sftp = 1;\r
41 static int main_cmd_is_sftp = 0;\r
42 static int fallback_cmd_is_sftp = 0;\r
43 static int using_sftp = 0;\r
44 \r
45 static Backend *back;\r
46 static void *backhandle;\r
47 static Config cfg;\r
48 \r
49 static void source(char *src);\r
50 static void rsource(char *src);\r
51 static void sink(char *targ, char *src);\r
52 \r
53 const char *const appname = "PSCP";\r
54 \r
55 /*\r
56  * The maximum amount of queued data we accept before we stop and\r
57  * wait for the server to process some.\r
58  */\r
59 #define MAX_SCP_BUFSIZE 16384\r
60 \r
61 void ldisc_send(void *handle, char *buf, int len, int interactive)\r
62 {\r
63     /*\r
64      * This is only here because of the calls to ldisc_send(NULL,\r
65      * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc\r
66      * as an ldisc. So if we get called with any real data, I want\r
67      * to know about it.\r
68      */\r
69     assert(len == 0);\r
70 }\r
71 \r
72 static void tell_char(FILE * stream, char c)\r
73 {\r
74     fputc(c, stream);\r
75 }\r
76 \r
77 static void tell_str(FILE * stream, char *str)\r
78 {\r
79     unsigned int i;\r
80 \r
81     for (i = 0; i < strlen(str); ++i)\r
82         tell_char(stream, str[i]);\r
83 }\r
84 \r
85 static void tell_user(FILE * stream, char *fmt, ...)\r
86 {\r
87     char *str, *str2;\r
88     va_list ap;\r
89     va_start(ap, fmt);\r
90     str = dupvprintf(fmt, ap);\r
91     va_end(ap);\r
92     str2 = dupcat(str, "\n", NULL);\r
93     sfree(str);\r
94     tell_str(stream, str2);\r
95     sfree(str2);\r
96 }\r
97 \r
98 /*\r
99  *  Print an error message and perform a fatal exit.\r
100  */\r
101 void fatalbox(char *fmt, ...)\r
102 {\r
103     char *str, *str2;\r
104     va_list ap;\r
105     va_start(ap, fmt);\r
106     str = dupvprintf(fmt, ap);\r
107     str2 = dupcat("Fatal: ", str, "\n", NULL);\r
108     sfree(str);\r
109     va_end(ap);\r
110     tell_str(stderr, str2);\r
111     sfree(str2);\r
112     errs++;\r
113 \r
114     cleanup_exit(1);\r
115 }\r
116 void modalfatalbox(char *fmt, ...)\r
117 {\r
118     char *str, *str2;\r
119     va_list ap;\r
120     va_start(ap, fmt);\r
121     str = dupvprintf(fmt, ap);\r
122     str2 = dupcat("Fatal: ", str, "\n", NULL);\r
123     sfree(str);\r
124     va_end(ap);\r
125     tell_str(stderr, str2);\r
126     sfree(str2);\r
127     errs++;\r
128 \r
129     cleanup_exit(1);\r
130 }\r
131 void connection_fatal(void *frontend, char *fmt, ...)\r
132 {\r
133     char *str, *str2;\r
134     va_list ap;\r
135     va_start(ap, fmt);\r
136     str = dupvprintf(fmt, ap);\r
137     str2 = dupcat("Fatal: ", str, "\n", NULL);\r
138     sfree(str);\r
139     va_end(ap);\r
140     tell_str(stderr, str2);\r
141     sfree(str2);\r
142     errs++;\r
143 \r
144     cleanup_exit(1);\r
145 }\r
146 \r
147 /*\r
148  * In pscp, all agent requests should be synchronous, so this is a\r
149  * never-called stub.\r
150  */\r
151 void agent_schedule_callback(void (*callback)(void *, void *, int),\r
152                              void *callback_ctx, void *data, int len)\r
153 {\r
154     assert(!"We shouldn't be here");\r
155 }\r
156 \r
157 /*\r
158  * Receive a block of data from the SSH link. Block until all data\r
159  * is available.\r
160  *\r
161  * To do this, we repeatedly call the SSH protocol module, with our\r
162  * own trap in from_backend() to catch the data that comes back. We\r
163  * do this until we have enough data.\r
164  */\r
165 \r
166 static unsigned char *outptr;          /* where to put the data */\r
167 static unsigned outlen;                /* how much data required */\r
168 static unsigned char *pending = NULL;  /* any spare data */\r
169 static unsigned pendlen = 0, pendsize = 0;      /* length and phys. size of buffer */\r
170 int from_backend(void *frontend, int is_stderr, const char *data, int datalen)\r
171 {\r
172     unsigned char *p = (unsigned char *) data;\r
173     unsigned len = (unsigned) datalen;\r
174 \r
175     /*\r
176      * stderr data is just spouted to local stderr and otherwise\r
177      * ignored.\r
178      */\r
179     if (is_stderr) {\r
180         if (len > 0)\r
181             if (fwrite(data, 1, len, stderr) < len)\r
182                 /* oh well */;\r
183         return 0;\r
184     }\r
185 \r
186     if ((outlen > 0) && (len > 0)) {\r
187         unsigned used = outlen;\r
188         if (used > len)\r
189             used = len;\r
190         memcpy(outptr, p, used);\r
191         outptr += used;\r
192         outlen -= used;\r
193         p += used;\r
194         len -= used;\r
195     }\r
196 \r
197     if (len > 0) {\r
198         if (pendsize < pendlen + len) {\r
199             pendsize = pendlen + len + 4096;\r
200             pending = sresize(pending, pendsize, unsigned char);\r
201         }\r
202         memcpy(pending + pendlen, p, len);\r
203         pendlen += len;\r
204     }\r
205 \r
206     return 0;\r
207 }\r
208 int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
209 {\r
210     /*\r
211      * No "untrusted" output should get here (the way the code is\r
212      * currently, it's all diverted by FLAG_STDERR).\r
213      */\r
214     assert(!"Unexpected call to from_backend_untrusted()");\r
215     return 0; /* not reached */\r
216 }\r
217 static int ssh_scp_recv(unsigned char *buf, int len)\r
218 {\r
219     outptr = buf;\r
220     outlen = len;\r
221 \r
222     /*\r
223      * See if the pending-input block contains some of what we\r
224      * need.\r
225      */\r
226     if (pendlen > 0) {\r
227         unsigned pendused = pendlen;\r
228         if (pendused > outlen)\r
229             pendused = outlen;\r
230         memcpy(outptr, pending, pendused);\r
231         memmove(pending, pending + pendused, pendlen - pendused);\r
232         outptr += pendused;\r
233         outlen -= pendused;\r
234         pendlen -= pendused;\r
235         if (pendlen == 0) {\r
236             pendsize = 0;\r
237             sfree(pending);\r
238             pending = NULL;\r
239         }\r
240         if (outlen == 0)\r
241             return len;\r
242     }\r
243 \r
244     while (outlen > 0) {\r
245         if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)\r
246             return 0;                  /* doom */\r
247     }\r
248 \r
249     return len;\r
250 }\r
251 \r
252 /*\r
253  * Loop through the ssh connection and authentication process.\r
254  */\r
255 static void ssh_scp_init(void)\r
256 {\r
257     while (!back->sendok(backhandle)) {\r
258         if (back->exitcode(backhandle) >= 0) {\r
259             errs++;\r
260             return;\r
261         }\r
262         if (ssh_sftp_loop_iteration() < 0) {\r
263             errs++;\r
264             return;                    /* doom */\r
265         }\r
266     }\r
267 \r
268     /* Work out which backend we ended up using. */\r
269     if (!ssh_fallback_cmd(backhandle))\r
270         using_sftp = main_cmd_is_sftp;\r
271     else\r
272         using_sftp = fallback_cmd_is_sftp;\r
273 \r
274     if (verbose) {\r
275         if (using_sftp)\r
276             tell_user(stderr, "Using SFTP");\r
277         else\r
278             tell_user(stderr, "Using SCP1");\r
279     }\r
280 }\r
281 \r
282 /*\r
283  *  Print an error message and exit after closing the SSH link.\r
284  */\r
285 static void bump(char *fmt, ...)\r
286 {\r
287     char *str, *str2;\r
288     va_list ap;\r
289     va_start(ap, fmt);\r
290     str = dupvprintf(fmt, ap);\r
291     va_end(ap);\r
292     str2 = dupcat(str, "\n", NULL);\r
293     sfree(str);\r
294     tell_str(stderr, str2);\r
295     sfree(str2);\r
296     errs++;\r
297 \r
298     if (back != NULL && back->connected(backhandle)) {\r
299         char ch;\r
300         back->special(backhandle, TS_EOF);\r
301         ssh_scp_recv((unsigned char *) &ch, 1);\r
302     }\r
303 \r
304     cleanup_exit(1);\r
305 }\r
306 \r
307 /*\r
308  *  Open an SSH connection to user@host and execute cmd.\r
309  */\r
310 static void do_cmd(char *host, char *user, char *cmd)\r
311 {\r
312     const char *err;\r
313     char *realhost;\r
314     void *logctx;\r
315 \r
316     if (host == NULL || host[0] == '\0')\r
317         bump("Empty host name");\r
318 \r
319     /*\r
320      * Remove fiddly bits of address: remove a colon suffix, and\r
321      * the square brackets around an IPv6 literal address.\r
322      */\r
323     if (host[0] == '[') {\r
324         host++;\r
325         host[strcspn(host, "]")] = '\0';\r
326     } else {\r
327         host[strcspn(host, ":")] = '\0';\r
328     }\r
329 \r
330     /*\r
331      * If we haven't loaded session details already (e.g., from -load),\r
332      * try looking for a session called "host".\r
333      */\r
334     if (!loaded_session) {\r
335         /* Try to load settings for `host' into a temporary config */\r
336         Config cfg2;\r
337         cfg2.host[0] = '\0';\r
338         do_defaults(host, &cfg2);\r
339         if (cfg2.host[0] != '\0') {\r
340             /* Settings present and include hostname */\r
341             /* Re-load data into the real config. */\r
342             do_defaults(host, &cfg);\r
343         } else {\r
344             /* Session doesn't exist or mention a hostname. */\r
345             /* Use `host' as a bare hostname. */\r
346             strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
347             cfg.host[sizeof(cfg.host) - 1] = '\0';\r
348         }\r
349     } else {\r
350         /* Patch in hostname `host' to session details. */\r
351         strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
352         cfg.host[sizeof(cfg.host) - 1] = '\0';\r
353     }\r
354 \r
355     /*\r
356      * Force use of SSH. (If they got the protocol wrong we assume the\r
357      * port is useless too.)\r
358      */\r
359     if (cfg.protocol != PROT_SSH) {\r
360         cfg.protocol = PROT_SSH;\r
361         cfg.port = 22;\r
362     }\r
363 \r
364     /*\r
365      * Enact command-line overrides.\r
366      */\r
367     cmdline_run_saved(&cfg);\r
368 \r
369     /*\r
370      * Trim leading whitespace off the hostname if it's there.\r
371      */\r
372     {\r
373         int space = strspn(cfg.host, " \t");\r
374         memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
375     }\r
376 \r
377     /* See if host is of the form user@host */\r
378     if (cfg.host[0] != '\0') {\r
379         char *atsign = strrchr(cfg.host, '@');\r
380         /* Make sure we're not overflowing the user field */\r
381         if (atsign) {\r
382             if (atsign - cfg.host < sizeof cfg.username) {\r
383                 strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
384                 cfg.username[atsign - cfg.host] = '\0';\r
385             }\r
386             memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
387         }\r
388     }\r
389 \r
390     /*\r
391      * Remove any remaining whitespace from the hostname.\r
392      */\r
393     {\r
394         int p1 = 0, p2 = 0;\r
395         while (cfg.host[p2] != '\0') {\r
396             if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
397                 cfg.host[p1] = cfg.host[p2];\r
398                 p1++;\r
399             }\r
400             p2++;\r
401         }\r
402         cfg.host[p1] = '\0';\r
403     }\r
404 \r
405     /* Set username */\r
406     if (user != NULL && user[0] != '\0') {\r
407         strncpy(cfg.username, user, sizeof(cfg.username) - 1);\r
408         cfg.username[sizeof(cfg.username) - 1] = '\0';\r
409     } else if (cfg.username[0] == '\0') {\r
410         user = get_username();\r
411         if (!user)\r
412             bump("Empty user name");\r
413         else {\r
414             if (verbose)\r
415                 tell_user(stderr, "Guessing user name: %s", user);\r
416             strncpy(cfg.username, user, sizeof(cfg.username) - 1);\r
417             cfg.username[sizeof(cfg.username) - 1] = '\0';\r
418             sfree(user);\r
419         }\r
420     }\r
421 \r
422     /*\r
423      * Disable scary things which shouldn't be enabled for simple\r
424      * things like SCP and SFTP: agent forwarding, port forwarding,\r
425      * X forwarding.\r
426      */\r
427     cfg.x11_forward = 0;\r
428     cfg.agentfwd = 0;\r
429     cfg.portfwd[0] = cfg.portfwd[1] = '\0';\r
430     cfg.ssh_simple = TRUE;\r
431 \r
432     /*\r
433      * Set up main and possibly fallback command depending on\r
434      * options specified by user.\r
435      * Attempt to start the SFTP subsystem as a first choice,\r
436      * falling back to the provided scp command if that fails.\r
437      */\r
438     cfg.remote_cmd_ptr2 = NULL;\r
439     if (try_sftp) {\r
440         /* First choice is SFTP subsystem. */\r
441         main_cmd_is_sftp = 1;\r
442         strcpy(cfg.remote_cmd, "sftp");\r
443         cfg.ssh_subsys = TRUE;\r
444         if (try_scp) {\r
445             /* Fallback is to use the provided scp command. */\r
446             fallback_cmd_is_sftp = 0;\r
447             cfg.remote_cmd_ptr2 = cmd;\r
448             cfg.ssh_subsys2 = FALSE;\r
449         } else {\r
450             /* Since we're not going to try SCP, we may as well try\r
451              * harder to find an SFTP server, since in the current\r
452              * implementation we have a spare slot. */\r
453             fallback_cmd_is_sftp = 1;\r
454             /* see psftp.c for full explanation of this kludge */\r
455             cfg.remote_cmd_ptr2 = \r
456                 "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"\r
457                 "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"\r
458                 "exec sftp-server";\r
459             cfg.ssh_subsys2 = FALSE;\r
460         }\r
461     } else {\r
462         /* Don't try SFTP at all; just try the scp command. */\r
463         main_cmd_is_sftp = 0;\r
464         cfg.remote_cmd_ptr = cmd;\r
465         cfg.ssh_subsys = FALSE;\r
466     }\r
467     cfg.nopty = TRUE;\r
468 \r
469     back = &ssh_backend;\r
470 \r
471     err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, \r
472                      0, cfg.tcp_keepalives);\r
473     if (err != NULL)\r
474         bump("ssh_init: %s", err);\r
475     logctx = log_init(NULL, &cfg);\r
476     back->provide_logctx(backhandle, logctx);\r
477     console_provide_logctx(logctx);\r
478     ssh_scp_init();\r
479     if (verbose && realhost != NULL && errs == 0)\r
480         tell_user(stderr, "Connected to %s\n", realhost);\r
481     sfree(realhost);\r
482 }\r
483 \r
484 /*\r
485  *  Update statistic information about current file.\r
486  */\r
487 static void print_stats(char *name, uint64 size, uint64 done,\r
488                         time_t start, time_t now)\r
489 {\r
490     float ratebs;\r
491     unsigned long eta;\r
492     char *etastr;\r
493     int pct;\r
494     int len;\r
495     int elap;\r
496     double donedbl;\r
497     double sizedbl;\r
498 \r
499     elap = (unsigned long) difftime(now, start);\r
500 \r
501     if (now > start)\r
502         ratebs = (float) (uint64_to_double(done) / elap);\r
503     else\r
504         ratebs = (float) uint64_to_double(done);\r
505 \r
506     if (ratebs < 1.0)\r
507         eta = (unsigned long) (uint64_to_double(uint64_subtract(size, done)));\r
508     else {\r
509         eta = (unsigned long)\r
510             ((uint64_to_double(uint64_subtract(size, done)) / ratebs));\r
511     }\r
512 \r
513     etastr = dupprintf("%02ld:%02ld:%02ld",\r
514                        eta / 3600, (eta % 3600) / 60, eta % 60);\r
515 \r
516     donedbl = uint64_to_double(done);\r
517     sizedbl = uint64_to_double(size);\r
518     pct = (int) (100 * (donedbl * 1.0 / sizedbl));\r
519 \r
520     {\r
521         char donekb[40];\r
522         /* divide by 1024 to provide kB */\r
523         uint64_decimal(uint64_shift_right(done, 10), donekb);\r
524         len = printf("\r%-25.25s | %s kB | %5.1f kB/s | ETA: %8s | %3d%%",\r
525                      name,\r
526                      donekb, ratebs / 1024.0, etastr, pct);\r
527         if (len < prev_stats_len)\r
528             printf("%*s", prev_stats_len - len, "");\r
529         prev_stats_len = len;\r
530 \r
531         if (uint64_compare(done, size) == 0)\r
532             printf("\n");\r
533 \r
534         fflush(stdout);\r
535     }\r
536 \r
537     free(etastr);\r
538 }\r
539 \r
540 /*\r
541  *  Find a colon in str and return a pointer to the colon.\r
542  *  This is used to separate hostname from filename.\r
543  */\r
544 static char *colon(char *str)\r
545 {\r
546     /* We ignore a leading colon, since the hostname cannot be\r
547        empty. We also ignore a colon as second character because\r
548        of filenames like f:myfile.txt. */\r
549     if (str[0] == '\0' || str[0] == ':' ||\r
550         (str[0] != '[' && str[1] == ':'))\r
551         return (NULL);\r
552     while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') {\r
553         if (*str == '[') {\r
554             /* Skip over IPv6 literal addresses\r
555              * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */\r
556             char *ipv6_end = strchr(str, ']');\r
557             if (ipv6_end) {\r
558                 str = ipv6_end;\r
559             }\r
560         }\r
561         str++;\r
562     }\r
563     if (*str == ':')\r
564         return (str);\r
565     else\r
566         return (NULL);\r
567 }\r
568 \r
569 /*\r
570  * Return a pointer to the portion of str that comes after the last\r
571  * slash (or backslash or colon, if `local' is TRUE).\r
572  */\r
573 static char *stripslashes(char *str, int local)\r
574 {\r
575     char *p;\r
576 \r
577     if (local) {\r
578         p = strchr(str, ':');\r
579         if (p) str = p+1;\r
580     }\r
581 \r
582     p = strrchr(str, '/');\r
583     if (p) str = p+1;\r
584 \r
585     if (local) {\r
586         p = strrchr(str, '\\');\r
587         if (p) str = p+1;\r
588     }\r
589 \r
590     return str;\r
591 }\r
592 \r
593 /*\r
594  * Determine whether a string is entirely composed of dots.\r
595  */\r
596 static int is_dots(char *str)\r
597 {\r
598     return str[strspn(str, ".")] == '\0';\r
599 }\r
600 \r
601 /*\r
602  *  Wait for a response from the other side.\r
603  *  Return 0 if ok, -1 if error.\r
604  */\r
605 static int response(void)\r
606 {\r
607     char ch, resp, rbuf[2048];\r
608     int p;\r
609 \r
610     if (ssh_scp_recv((unsigned char *) &resp, 1) <= 0)\r
611         bump("Lost connection");\r
612 \r
613     p = 0;\r
614     switch (resp) {\r
615       case 0:                          /* ok */\r
616         return (0);\r
617       default:\r
618         rbuf[p++] = resp;\r
619         /* fallthrough */\r
620       case 1:                          /* error */\r
621       case 2:                          /* fatal error */\r
622         do {\r
623             if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
624                 bump("Protocol error: Lost connection");\r
625             rbuf[p++] = ch;\r
626         } while (p < sizeof(rbuf) && ch != '\n');\r
627         rbuf[p - 1] = '\0';\r
628         if (resp == 1)\r
629             tell_user(stderr, "%s\n", rbuf);\r
630         else\r
631             bump("%s", rbuf);\r
632         errs++;\r
633         return (-1);\r
634     }\r
635 }\r
636 \r
637 int sftp_recvdata(char *buf, int len)\r
638 {\r
639     return ssh_scp_recv((unsigned char *) buf, len);\r
640 }\r
641 int sftp_senddata(char *buf, int len)\r
642 {\r
643     back->send(backhandle, buf, len);\r
644     return 1;\r
645 }\r
646 \r
647 /* ----------------------------------------------------------------------\r
648  * sftp-based replacement for the hacky `pscp -ls'.\r
649  */\r
650 static int sftp_ls_compare(const void *av, const void *bv)\r
651 {\r
652     const struct fxp_name *a = (const struct fxp_name *) av;\r
653     const struct fxp_name *b = (const struct fxp_name *) bv;\r
654     return strcmp(a->filename, b->filename);\r
655 }\r
656 void scp_sftp_listdir(char *dirname)\r
657 {\r
658     struct fxp_handle *dirh;\r
659     struct fxp_names *names;\r
660     struct fxp_name *ournames;\r
661     struct sftp_packet *pktin;\r
662     struct sftp_request *req, *rreq;\r
663     int nnames, namesize;\r
664     int i;\r
665 \r
666     if (!fxp_init()) {\r
667         tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
668         errs++;\r
669         return;\r
670     }\r
671 \r
672     printf("Listing directory %s\n", dirname);\r
673 \r
674     sftp_register(req = fxp_opendir_send(dirname));\r
675     rreq = sftp_find_request(pktin = sftp_recv());\r
676     assert(rreq == req);\r
677     dirh = fxp_opendir_recv(pktin, rreq);\r
678 \r
679     if (dirh == NULL) {\r
680         printf("Unable to open %s: %s\n", dirname, fxp_error());\r
681     } else {\r
682         nnames = namesize = 0;\r
683         ournames = NULL;\r
684 \r
685         while (1) {\r
686 \r
687             sftp_register(req = fxp_readdir_send(dirh));\r
688             rreq = sftp_find_request(pktin = sftp_recv());\r
689             assert(rreq == req);\r
690             names = fxp_readdir_recv(pktin, rreq);\r
691 \r
692             if (names == NULL) {\r
693                 if (fxp_error_type() == SSH_FX_EOF)\r
694                     break;\r
695                 printf("Reading directory %s: %s\n", dirname, fxp_error());\r
696                 break;\r
697             }\r
698             if (names->nnames == 0) {\r
699                 fxp_free_names(names);\r
700                 break;\r
701             }\r
702 \r
703             if (nnames + names->nnames >= namesize) {\r
704                 namesize += names->nnames + 128;\r
705                 ournames = sresize(ournames, namesize, struct fxp_name);\r
706             }\r
707 \r
708             for (i = 0; i < names->nnames; i++)\r
709                 ournames[nnames++] = names->names[i];\r
710             names->nnames = 0;         /* prevent free_names */\r
711             fxp_free_names(names);\r
712         }\r
713         sftp_register(req = fxp_close_send(dirh));\r
714         rreq = sftp_find_request(pktin = sftp_recv());\r
715         assert(rreq == req);\r
716         fxp_close_recv(pktin, rreq);\r
717 \r
718         /*\r
719          * Now we have our filenames. Sort them by actual file\r
720          * name, and then output the longname parts.\r
721          */\r
722         qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);\r
723 \r
724         /*\r
725          * And print them.\r
726          */\r
727         for (i = 0; i < nnames; i++)\r
728             printf("%s\n", ournames[i].longname);\r
729     }\r
730 }\r
731 \r
732 /* ----------------------------------------------------------------------\r
733  * Helper routines that contain the actual SCP protocol elements,\r
734  * implemented both as SCP1 and SFTP.\r
735  */\r
736 \r
737 static struct scp_sftp_dirstack {\r
738     struct scp_sftp_dirstack *next;\r
739     struct fxp_name *names;\r
740     int namepos, namelen;\r
741     char *dirpath;\r
742     char *wildcard;\r
743     int matched_something;             /* wildcard match set was non-empty */\r
744 } *scp_sftp_dirstack_head;\r
745 static char *scp_sftp_remotepath, *scp_sftp_currentname;\r
746 static char *scp_sftp_wildcard;\r
747 static int scp_sftp_targetisdir, scp_sftp_donethistarget;\r
748 static int scp_sftp_preserve, scp_sftp_recursive;\r
749 static unsigned long scp_sftp_mtime, scp_sftp_atime;\r
750 static int scp_has_times;\r
751 static struct fxp_handle *scp_sftp_filehandle;\r
752 static struct fxp_xfer *scp_sftp_xfer;\r
753 static uint64 scp_sftp_fileoffset;\r
754 \r
755 int scp_source_setup(char *target, int shouldbedir)\r
756 {\r
757     if (using_sftp) {\r
758         /*\r
759          * Find out whether the target filespec is in fact a\r
760          * directory.\r
761          */\r
762         struct sftp_packet *pktin;\r
763         struct sftp_request *req, *rreq;\r
764         struct fxp_attrs attrs;\r
765         int ret;\r
766 \r
767         if (!fxp_init()) {\r
768             tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
769             errs++;\r
770             return 1;\r
771         }\r
772 \r
773         sftp_register(req = fxp_stat_send(target));\r
774         rreq = sftp_find_request(pktin = sftp_recv());\r
775         assert(rreq == req);\r
776         ret = fxp_stat_recv(pktin, rreq, &attrs);\r
777 \r
778         if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))\r
779             scp_sftp_targetisdir = 0;\r
780         else\r
781             scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0;\r
782 \r
783         if (shouldbedir && !scp_sftp_targetisdir) {\r
784             bump("pscp: remote filespec %s: not a directory\n", target);\r
785         }\r
786 \r
787         scp_sftp_remotepath = dupstr(target);\r
788 \r
789         scp_has_times = 0;\r
790     } else {\r
791         (void) response();\r
792     }\r
793     return 0;\r
794 }\r
795 \r
796 int scp_send_errmsg(char *str)\r
797 {\r
798     if (using_sftp) {\r
799         /* do nothing; we never need to send our errors to the server */\r
800     } else {\r
801         back->send(backhandle, "\001", 1);/* scp protocol error prefix */\r
802         back->send(backhandle, str, strlen(str));\r
803     }\r
804     return 0;                          /* can't fail */\r
805 }\r
806 \r
807 int scp_send_filetimes(unsigned long mtime, unsigned long atime)\r
808 {\r
809     if (using_sftp) {\r
810         scp_sftp_mtime = mtime;\r
811         scp_sftp_atime = atime;\r
812         scp_has_times = 1;\r
813         return 0;\r
814     } else {\r
815         char buf[80];\r
816         sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);\r
817         back->send(backhandle, buf, strlen(buf));\r
818         return response();\r
819     }\r
820 }\r
821 \r
822 int scp_send_filename(char *name, uint64 size, int modes)\r
823 {\r
824     if (using_sftp) {\r
825         char *fullname;\r
826         struct sftp_packet *pktin;\r
827         struct sftp_request *req, *rreq;\r
828 \r
829         if (scp_sftp_targetisdir) {\r
830             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);\r
831         } else {\r
832             fullname = dupstr(scp_sftp_remotepath);\r
833         }\r
834 \r
835         sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE |\r
836                                           SSH_FXF_CREAT | SSH_FXF_TRUNC));\r
837         rreq = sftp_find_request(pktin = sftp_recv());\r
838         assert(rreq == req);\r
839         scp_sftp_filehandle = fxp_open_recv(pktin, rreq);\r
840 \r
841         if (!scp_sftp_filehandle) {\r
842             tell_user(stderr, "pscp: unable to open %s: %s",\r
843                       fullname, fxp_error());\r
844             errs++;\r
845             return 1;\r
846         }\r
847         scp_sftp_fileoffset = uint64_make(0, 0);\r
848         scp_sftp_xfer = xfer_upload_init(scp_sftp_filehandle,\r
849                                          scp_sftp_fileoffset);\r
850         sfree(fullname);\r
851         return 0;\r
852     } else {\r
853         char buf[40];\r
854         char sizestr[40];\r
855         uint64_decimal(size, sizestr);\r
856         sprintf(buf, "C%04o %s ", modes, sizestr);\r
857         back->send(backhandle, buf, strlen(buf));\r
858         back->send(backhandle, name, strlen(name));\r
859         back->send(backhandle, "\n", 1);\r
860         return response();\r
861     }\r
862 }\r
863 \r
864 int scp_send_filedata(char *data, int len)\r
865 {\r
866     if (using_sftp) {\r
867         int ret;\r
868         struct sftp_packet *pktin;\r
869 \r
870         if (!scp_sftp_filehandle) {\r
871             return 1;\r
872         }\r
873 \r
874         while (!xfer_upload_ready(scp_sftp_xfer)) {\r
875             pktin = sftp_recv();\r
876             ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin);\r
877             if (!ret) {\r
878                 tell_user(stderr, "error while writing: %s\n", fxp_error());\r
879                 errs++;\r
880                 return 1;\r
881             }\r
882         }\r
883 \r
884         xfer_upload_data(scp_sftp_xfer, data, len);\r
885 \r
886         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);\r
887         return 0;\r
888     } else {\r
889         int bufsize = back->send(backhandle, data, len);\r
890 \r
891         /*\r
892          * If the network transfer is backing up - that is, the\r
893          * remote site is not accepting data as fast as we can\r
894          * produce it - then we must loop on network events until\r
895          * we have space in the buffer again.\r
896          */\r
897         while (bufsize > MAX_SCP_BUFSIZE) {\r
898             if (ssh_sftp_loop_iteration() < 0)\r
899                 return 1;\r
900             bufsize = back->sendbuffer(backhandle);\r
901         }\r
902 \r
903         return 0;\r
904     }\r
905 }\r
906 \r
907 int scp_send_finish(void)\r
908 {\r
909     if (using_sftp) {\r
910         struct fxp_attrs attrs;\r
911         struct sftp_packet *pktin;\r
912         struct sftp_request *req, *rreq;\r
913         int ret;\r
914 \r
915         while (!xfer_done(scp_sftp_xfer)) {\r
916             pktin = sftp_recv();\r
917             xfer_upload_gotpkt(scp_sftp_xfer, pktin);\r
918         }\r
919         xfer_cleanup(scp_sftp_xfer);\r
920 \r
921         if (!scp_sftp_filehandle) {\r
922             return 1;\r
923         }\r
924         if (scp_has_times) {\r
925             attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;\r
926             attrs.atime = scp_sftp_atime;\r
927             attrs.mtime = scp_sftp_mtime;\r
928             sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs));\r
929             rreq = sftp_find_request(pktin = sftp_recv());\r
930             assert(rreq == req);\r
931             ret = fxp_fsetstat_recv(pktin, rreq);\r
932             if (!ret) {\r
933                 tell_user(stderr, "unable to set file times: %s\n", fxp_error());\r
934                 errs++;\r
935             }\r
936         }\r
937         sftp_register(req = fxp_close_send(scp_sftp_filehandle));\r
938         rreq = sftp_find_request(pktin = sftp_recv());\r
939         assert(rreq == req);\r
940         fxp_close_recv(pktin, rreq);\r
941         scp_has_times = 0;\r
942         return 0;\r
943     } else {\r
944         back->send(backhandle, "", 1);\r
945         return response();\r
946     }\r
947 }\r
948 \r
949 char *scp_save_remotepath(void)\r
950 {\r
951     if (using_sftp)\r
952         return scp_sftp_remotepath;\r
953     else\r
954         return NULL;\r
955 }\r
956 \r
957 void scp_restore_remotepath(char *data)\r
958 {\r
959     if (using_sftp)\r
960         scp_sftp_remotepath = data;\r
961 }\r
962 \r
963 int scp_send_dirname(char *name, int modes)\r
964 {\r
965     if (using_sftp) {\r
966         char *fullname;\r
967         char const *err;\r
968         struct fxp_attrs attrs;\r
969         struct sftp_packet *pktin;\r
970         struct sftp_request *req, *rreq;\r
971         int ret;\r
972 \r
973         if (scp_sftp_targetisdir) {\r
974             fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);\r
975         } else {\r
976             fullname = dupstr(scp_sftp_remotepath);\r
977         }\r
978 \r
979         /*\r
980          * We don't worry about whether we managed to create the\r
981          * directory, because if it exists already it's OK just to\r
982          * use it. Instead, we will stat it afterwards, and if it\r
983          * exists and is a directory we will assume we were either\r
984          * successful or it didn't matter.\r
985          */\r
986         sftp_register(req = fxp_mkdir_send(fullname));\r
987         rreq = sftp_find_request(pktin = sftp_recv());\r
988         assert(rreq == req);\r
989         ret = fxp_mkdir_recv(pktin, rreq);\r
990 \r
991         if (!ret)\r
992             err = fxp_error();\r
993         else\r
994             err = "server reported no error";\r
995 \r
996         sftp_register(req = fxp_stat_send(fullname));\r
997         rreq = sftp_find_request(pktin = sftp_recv());\r
998         assert(rreq == req);\r
999         ret = fxp_stat_recv(pktin, rreq, &attrs);\r
1000 \r
1001         if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||\r
1002             !(attrs.permissions & 0040000)) {\r
1003             tell_user(stderr, "unable to create directory %s: %s",\r
1004                       fullname, err);\r
1005             errs++;\r
1006             return 1;\r
1007         }\r
1008 \r
1009         scp_sftp_remotepath = fullname;\r
1010 \r
1011         return 0;\r
1012     } else {\r
1013         char buf[40];\r
1014         sprintf(buf, "D%04o 0 ", modes);\r
1015         back->send(backhandle, buf, strlen(buf));\r
1016         back->send(backhandle, name, strlen(name));\r
1017         back->send(backhandle, "\n", 1);\r
1018         return response();\r
1019     }\r
1020 }\r
1021 \r
1022 int scp_send_enddir(void)\r
1023 {\r
1024     if (using_sftp) {\r
1025         sfree(scp_sftp_remotepath);\r
1026         return 0;\r
1027     } else {\r
1028         back->send(backhandle, "E\n", 2);\r
1029         return response();\r
1030     }\r
1031 }\r
1032 \r
1033 /*\r
1034  * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init.\r
1035  * That's bad. The difference is that scp_sink_setup is called once\r
1036  * right at the start, whereas scp_sink_init is called to\r
1037  * initialise every level of recursion in the protocol.\r
1038  */\r
1039 int scp_sink_setup(char *source, int preserve, int recursive)\r
1040 {\r
1041     if (using_sftp) {\r
1042         char *newsource;\r
1043 \r
1044         if (!fxp_init()) {\r
1045             tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
1046             errs++;\r
1047             return 1;\r
1048         }\r
1049         /*\r
1050          * It's possible that the source string we've been given\r
1051          * contains a wildcard. If so, we must split the directory\r
1052          * away from the wildcard itself (throwing an error if any\r
1053          * wildcardness comes before the final slash) and arrange\r
1054          * things so that a dirstack entry will be set up.\r
1055          */\r
1056         newsource = snewn(1+strlen(source), char);\r
1057         if (!wc_unescape(newsource, source)) {\r
1058             /* Yes, here we go; it's a wildcard. Bah. */\r
1059             char *dupsource, *lastpart, *dirpart, *wildcard;\r
1060             dupsource = dupstr(source);\r
1061             lastpart = stripslashes(dupsource, 0);\r
1062             wildcard = dupstr(lastpart);\r
1063             *lastpart = '\0';\r
1064             if (*dupsource && dupsource[1]) {\r
1065                 /*\r
1066                  * The remains of dupsource are at least two\r
1067                  * characters long, meaning the pathname wasn't\r
1068                  * empty or just `/'. Hence, we remove the trailing\r
1069                  * slash.\r
1070                  */\r
1071                 lastpart[-1] = '\0';\r
1072             } else if (!*dupsource) {\r
1073                 /*\r
1074                  * The remains of dupsource are _empty_ - the whole\r
1075                  * pathname was a wildcard. Hence we need to\r
1076                  * replace it with ".".\r
1077                  */\r
1078                 sfree(dupsource);\r
1079                 dupsource = dupstr(".");\r
1080             }\r
1081 \r
1082             /*\r
1083              * Now we have separated our string into dupsource (the\r
1084              * directory part) and wildcard. Both of these will\r
1085              * need freeing at some point. Next step is to remove\r
1086              * wildcard escapes from the directory part, throwing\r
1087              * an error if it contains a real wildcard.\r
1088              */\r
1089             dirpart = snewn(1+strlen(dupsource), char);\r
1090             if (!wc_unescape(dirpart, dupsource)) {\r
1091                 tell_user(stderr, "%s: multiple-level wildcards unsupported",\r
1092                           source);\r
1093                 errs++;\r
1094                 sfree(dirpart);\r
1095                 sfree(wildcard);\r
1096                 sfree(dupsource);\r
1097                 return 1;\r
1098             }\r
1099 \r
1100             /*\r
1101              * Now we have dirpart (unescaped, ie a valid remote\r
1102              * path), and wildcard (a wildcard). This will be\r
1103              * sufficient to arrange a dirstack entry.\r
1104              */\r
1105             scp_sftp_remotepath = dirpart;\r
1106             scp_sftp_wildcard = wildcard;\r
1107             sfree(dupsource);\r
1108         } else {\r
1109             scp_sftp_remotepath = newsource;\r
1110             scp_sftp_wildcard = NULL;\r
1111         }\r
1112         scp_sftp_preserve = preserve;\r
1113         scp_sftp_recursive = recursive;\r
1114         scp_sftp_donethistarget = 0;\r
1115         scp_sftp_dirstack_head = NULL;\r
1116     }\r
1117     return 0;\r
1118 }\r
1119 \r
1120 int scp_sink_init(void)\r
1121 {\r
1122     if (!using_sftp) {\r
1123         back->send(backhandle, "", 1);\r
1124     }\r
1125     return 0;\r
1126 }\r
1127 \r
1128 #define SCP_SINK_FILE   1\r
1129 #define SCP_SINK_DIR    2\r
1130 #define SCP_SINK_ENDDIR 3\r
1131 #define SCP_SINK_RETRY  4              /* not an action; just try again */\r
1132 struct scp_sink_action {\r
1133     int action;                        /* FILE, DIR, ENDDIR */\r
1134     char *buf;                         /* will need freeing after use */\r
1135     char *name;                        /* filename or dirname (not ENDDIR) */\r
1136     int mode;                          /* access mode (not ENDDIR) */\r
1137     uint64 size;                       /* file size (not ENDDIR) */\r
1138     int settime;                       /* 1 if atime and mtime are filled */\r
1139     unsigned long atime, mtime;        /* access times for the file */\r
1140 };\r
1141 \r
1142 int scp_get_sink_action(struct scp_sink_action *act)\r
1143 {\r
1144     if (using_sftp) {\r
1145         char *fname;\r
1146         int must_free_fname;\r
1147         struct fxp_attrs attrs;\r
1148         struct sftp_packet *pktin;\r
1149         struct sftp_request *req, *rreq;\r
1150         int ret;\r
1151 \r
1152         if (!scp_sftp_dirstack_head) {\r
1153             if (!scp_sftp_donethistarget) {\r
1154                 /*\r
1155                  * Simple case: we are only dealing with one file.\r
1156                  */\r
1157                 fname = scp_sftp_remotepath;\r
1158                 must_free_fname = 0;\r
1159                 scp_sftp_donethistarget = 1;\r
1160             } else {\r
1161                 /*\r
1162                  * Even simpler case: one file _which we've done_.\r
1163                  * Return 1 (finished).\r
1164                  */\r
1165                 return 1;\r
1166             }\r
1167         } else {\r
1168             /*\r
1169              * We're now in the middle of stepping through a list\r
1170              * of names returned from fxp_readdir(); so let's carry\r
1171              * on.\r
1172              */\r
1173             struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;\r
1174             while (head->namepos < head->namelen &&\r
1175                    (is_dots(head->names[head->namepos].filename) ||\r
1176                     (head->wildcard &&\r
1177                      !wc_match(head->wildcard,\r
1178                                head->names[head->namepos].filename))))\r
1179                 head->namepos++;       /* skip . and .. */\r
1180             if (head->namepos < head->namelen) {\r
1181                 head->matched_something = 1;\r
1182                 fname = dupcat(head->dirpath, "/",\r
1183                                head->names[head->namepos++].filename,\r
1184                                NULL);\r
1185                 must_free_fname = 1;\r
1186             } else {\r
1187                 /*\r
1188                  * We've come to the end of the list; pop it off\r
1189                  * the stack and return an ENDDIR action (or RETRY\r
1190                  * if this was a wildcard match).\r
1191                  */\r
1192                 if (head->wildcard) {\r
1193                     act->action = SCP_SINK_RETRY;\r
1194                     if (!head->matched_something) {\r
1195                         tell_user(stderr, "pscp: wildcard '%s' matched "\r
1196                                   "no files", head->wildcard);\r
1197                         errs++;\r
1198                     }\r
1199                     sfree(head->wildcard);\r
1200 \r
1201                 } else {\r
1202                     act->action = SCP_SINK_ENDDIR;\r
1203                 }\r
1204 \r
1205                 sfree(head->dirpath);\r
1206                 sfree(head->names);\r
1207                 scp_sftp_dirstack_head = head->next;\r
1208                 sfree(head);\r
1209 \r
1210                 return 0;\r
1211             }\r
1212         }\r
1213 \r
1214         /*\r
1215          * Now we have a filename. Stat it, and see if it's a file\r
1216          * or a directory.\r
1217          */\r
1218         sftp_register(req = fxp_stat_send(fname));\r
1219         rreq = sftp_find_request(pktin = sftp_recv());\r
1220         assert(rreq == req);\r
1221         ret = fxp_stat_recv(pktin, rreq, &attrs);\r
1222 \r
1223         if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {\r
1224             tell_user(stderr, "unable to identify %s: %s", fname,\r
1225                       ret ? "file type not supplied" : fxp_error());\r
1226             errs++;\r
1227             return 1;\r
1228         }\r
1229 \r
1230         if (attrs.permissions & 0040000) {\r
1231             struct scp_sftp_dirstack *newitem;\r
1232             struct fxp_handle *dirhandle;\r
1233             int nnames, namesize;\r
1234             struct fxp_name *ournames;\r
1235             struct fxp_names *names;\r
1236 \r
1237             /*\r
1238              * It's a directory. If we're not in recursive mode,\r
1239              * this merits a complaint (which is fatal if the name\r
1240              * was specified directly, but not if it was matched by\r
1241              * a wildcard).\r
1242              * \r
1243              * We skip this complaint completely if\r
1244              * scp_sftp_wildcard is set, because that's an\r
1245              * indication that we're not actually supposed to\r
1246              * _recursively_ transfer the dir, just scan it for\r
1247              * things matching the wildcard.\r
1248              */\r
1249             if (!scp_sftp_recursive && !scp_sftp_wildcard) {\r
1250                 tell_user(stderr, "pscp: %s: is a directory", fname);\r
1251                 errs++;\r
1252                 if (must_free_fname) sfree(fname);\r
1253                 if (scp_sftp_dirstack_head) {\r
1254                     act->action = SCP_SINK_RETRY;\r
1255                     return 0;\r
1256                 } else {\r
1257                     return 1;\r
1258                 }\r
1259             }\r
1260 \r
1261             /*\r
1262              * Otherwise, the fun begins. We must fxp_opendir() the\r
1263              * directory, slurp the filenames into memory, return\r
1264              * SCP_SINK_DIR (unless this is a wildcard match), and\r
1265              * set targetisdir. The next time we're called, we will\r
1266              * run through the list of filenames one by one,\r
1267              * matching them against a wildcard if present.\r
1268              * \r
1269              * If targetisdir is _already_ set (meaning we're\r
1270              * already in the middle of going through another such\r
1271              * list), we must push the other (target,namelist) pair\r
1272              * on a stack.\r
1273              */\r
1274             sftp_register(req = fxp_opendir_send(fname));\r
1275             rreq = sftp_find_request(pktin = sftp_recv());\r
1276             assert(rreq == req);\r
1277             dirhandle = fxp_opendir_recv(pktin, rreq);\r
1278 \r
1279             if (!dirhandle) {\r
1280                 tell_user(stderr, "scp: unable to open directory %s: %s",\r
1281                           fname, fxp_error());\r
1282                 if (must_free_fname) sfree(fname);\r
1283                 errs++;\r
1284                 return 1;\r
1285             }\r
1286             nnames = namesize = 0;\r
1287             ournames = NULL;\r
1288             while (1) {\r
1289                 int i;\r
1290 \r
1291                 sftp_register(req = fxp_readdir_send(dirhandle));\r
1292                 rreq = sftp_find_request(pktin = sftp_recv());\r
1293                 assert(rreq == req);\r
1294                 names = fxp_readdir_recv(pktin, rreq);\r
1295 \r
1296                 if (names == NULL) {\r
1297                     if (fxp_error_type() == SSH_FX_EOF)\r
1298                         break;\r
1299                     tell_user(stderr, "scp: reading directory %s: %s\n",\r
1300                               fname, fxp_error());\r
1301                     if (must_free_fname) sfree(fname);\r
1302                     sfree(ournames);\r
1303                     errs++;\r
1304                     return 1;\r
1305                 }\r
1306                 if (names->nnames == 0) {\r
1307                     fxp_free_names(names);\r
1308                     break;\r
1309                 }\r
1310                 if (nnames + names->nnames >= namesize) {\r
1311                     namesize += names->nnames + 128;\r
1312                     ournames = sresize(ournames, namesize, struct fxp_name);\r
1313                 }\r
1314                 for (i = 0; i < names->nnames; i++) {\r
1315                     if (!strcmp(names->names[i].filename, ".") ||\r
1316                         !strcmp(names->names[i].filename, "..")) {\r
1317                         /*\r
1318                          * . and .. are normal consequences of\r
1319                          * reading a directory, and aren't worth\r
1320                          * complaining about.\r
1321                          */\r
1322                     } else if (!vet_filename(names->names[i].filename)) {\r
1323                         tell_user(stderr, "ignoring potentially dangerous server-"\r
1324                                   "supplied filename '%s'\n",\r
1325                                   names->names[i].filename);\r
1326                     } else\r
1327                         ournames[nnames++] = names->names[i];\r
1328                 }\r
1329                 names->nnames = 0;             /* prevent free_names */\r
1330                 fxp_free_names(names);\r
1331             }\r
1332             sftp_register(req = fxp_close_send(dirhandle));\r
1333             rreq = sftp_find_request(pktin = sftp_recv());\r
1334             assert(rreq == req);\r
1335             fxp_close_recv(pktin, rreq);\r
1336 \r
1337             newitem = snew(struct scp_sftp_dirstack);\r
1338             newitem->next = scp_sftp_dirstack_head;\r
1339             newitem->names = ournames;\r
1340             newitem->namepos = 0;\r
1341             newitem->namelen = nnames;\r
1342             if (must_free_fname)\r
1343                 newitem->dirpath = fname;\r
1344             else\r
1345                 newitem->dirpath = dupstr(fname);\r
1346             if (scp_sftp_wildcard) {\r
1347                 newitem->wildcard = scp_sftp_wildcard;\r
1348                 newitem->matched_something = 0;\r
1349                 scp_sftp_wildcard = NULL;\r
1350             } else {\r
1351                 newitem->wildcard = NULL;\r
1352             }\r
1353             scp_sftp_dirstack_head = newitem;\r
1354 \r
1355             if (newitem->wildcard) {\r
1356                 act->action = SCP_SINK_RETRY;\r
1357             } else {\r
1358                 act->action = SCP_SINK_DIR;\r
1359                 act->buf = dupstr(stripslashes(fname, 0));\r
1360                 act->name = act->buf;\r
1361                 act->size = uint64_make(0,0);     /* duhh, it's a directory */\r
1362                 act->mode = 07777 & attrs.permissions;\r
1363                 if (scp_sftp_preserve &&\r
1364                     (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {\r
1365                     act->atime = attrs.atime;\r
1366                     act->mtime = attrs.mtime;\r
1367                     act->settime = 1;\r
1368                 } else\r
1369                     act->settime = 0;\r
1370             }\r
1371             return 0;\r
1372 \r
1373         } else {\r
1374             /*\r
1375              * It's a file. Return SCP_SINK_FILE.\r
1376              */\r
1377             act->action = SCP_SINK_FILE;\r
1378             act->buf = dupstr(stripslashes(fname, 0));\r
1379             act->name = act->buf;\r
1380             if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {\r
1381                 act->size = attrs.size;\r
1382             } else\r
1383                 act->size = uint64_make(ULONG_MAX,ULONG_MAX);   /* no idea */\r
1384             act->mode = 07777 & attrs.permissions;\r
1385             if (scp_sftp_preserve &&\r
1386                 (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {\r
1387                 act->atime = attrs.atime;\r
1388                 act->mtime = attrs.mtime;\r
1389                 act->settime = 1;\r
1390             } else\r
1391                 act->settime = 0;\r
1392             if (must_free_fname)\r
1393                 scp_sftp_currentname = fname;\r
1394             else\r
1395                 scp_sftp_currentname = dupstr(fname);\r
1396             return 0;\r
1397         }\r
1398 \r
1399     } else {\r
1400         int done = 0;\r
1401         int i, bufsize;\r
1402         int action;\r
1403         char ch;\r
1404 \r
1405         act->settime = 0;\r
1406         act->buf = NULL;\r
1407         bufsize = 0;\r
1408 \r
1409         while (!done) {\r
1410             if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
1411                 return 1;\r
1412             if (ch == '\n')\r
1413                 bump("Protocol error: Unexpected newline");\r
1414             i = 0;\r
1415             action = ch;\r
1416             do {\r
1417                 if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
1418                     bump("Lost connection");\r
1419                 if (i >= bufsize) {\r
1420                     bufsize = i + 128;\r
1421                     act->buf = sresize(act->buf, bufsize, char);\r
1422                 }\r
1423                 act->buf[i++] = ch;\r
1424             } while (ch != '\n');\r
1425             act->buf[i - 1] = '\0';\r
1426             switch (action) {\r
1427               case '\01':                      /* error */\r
1428                 tell_user(stderr, "%s\n", act->buf);\r
1429                 errs++;\r
1430                 continue;                      /* go round again */\r
1431               case '\02':                      /* fatal error */\r
1432                 bump("%s", act->buf);\r
1433               case 'E':\r
1434                 back->send(backhandle, "", 1);\r
1435                 act->action = SCP_SINK_ENDDIR;\r
1436                 return 0;\r
1437               case 'T':\r
1438                 if (sscanf(act->buf, "%ld %*d %ld %*d",\r
1439                            &act->mtime, &act->atime) == 2) {\r
1440                     act->settime = 1;\r
1441                     back->send(backhandle, "", 1);\r
1442                     continue;          /* go round again */\r
1443                 }\r
1444                 bump("Protocol error: Illegal time format");\r
1445               case 'C':\r
1446               case 'D':\r
1447                 act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);\r
1448                 break;\r
1449               default:\r
1450                 bump("Protocol error: Expected control record");\r
1451             }\r
1452             /*\r
1453              * We will go round this loop only once, unless we hit\r
1454              * `continue' above.\r
1455              */\r
1456             done = 1;\r
1457         }\r
1458 \r
1459         /*\r
1460          * If we get here, we must have seen SCP_SINK_FILE or\r
1461          * SCP_SINK_DIR.\r
1462          */\r
1463         {\r
1464             char sizestr[40];\r
1465         \r
1466             if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2)\r
1467                 bump("Protocol error: Illegal file descriptor format");\r
1468             act->size = uint64_from_decimal(sizestr);\r
1469             act->name = act->buf + i;\r
1470             return 0;\r
1471         }\r
1472     }\r
1473 }\r
1474 \r
1475 int scp_accept_filexfer(void)\r
1476 {\r
1477     if (using_sftp) {\r
1478         struct sftp_packet *pktin;\r
1479         struct sftp_request *req, *rreq;\r
1480 \r
1481         sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ));\r
1482         rreq = sftp_find_request(pktin = sftp_recv());\r
1483         assert(rreq == req);\r
1484         scp_sftp_filehandle = fxp_open_recv(pktin, rreq);\r
1485 \r
1486         if (!scp_sftp_filehandle) {\r
1487             tell_user(stderr, "pscp: unable to open %s: %s",\r
1488                       scp_sftp_currentname, fxp_error());\r
1489             errs++;\r
1490             return 1;\r
1491         }\r
1492         scp_sftp_fileoffset = uint64_make(0, 0);\r
1493         scp_sftp_xfer = xfer_download_init(scp_sftp_filehandle,\r
1494                                            scp_sftp_fileoffset);\r
1495         sfree(scp_sftp_currentname);\r
1496         return 0;\r
1497     } else {\r
1498         back->send(backhandle, "", 1);\r
1499         return 0;                      /* can't fail */\r
1500     }\r
1501 }\r
1502 \r
1503 int scp_recv_filedata(char *data, int len)\r
1504 {\r
1505     if (using_sftp) {\r
1506         struct sftp_packet *pktin;\r
1507         int ret, actuallen;\r
1508         void *vbuf;\r
1509 \r
1510         xfer_download_queue(scp_sftp_xfer);\r
1511         pktin = sftp_recv();\r
1512         ret = xfer_download_gotpkt(scp_sftp_xfer, pktin);\r
1513 \r
1514         if (ret < 0) {\r
1515             tell_user(stderr, "pscp: error while reading: %s", fxp_error());\r
1516             errs++;\r
1517             return -1;\r
1518         }\r
1519 \r
1520         if (xfer_download_data(scp_sftp_xfer, &vbuf, &actuallen)) {\r
1521             /*\r
1522              * This assertion relies on the fact that the natural\r
1523              * block size used in the xfer manager is at most that\r
1524              * used in this module. I don't like crossing layers in\r
1525              * this way, but it'll do for now.\r
1526              */\r
1527             assert(actuallen <= len);\r
1528             memcpy(data, vbuf, actuallen);\r
1529             sfree(vbuf);\r
1530         } else\r
1531             actuallen = 0;\r
1532 \r
1533         scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, actuallen);\r
1534 \r
1535         return actuallen;\r
1536     } else {\r
1537         return ssh_scp_recv((unsigned char *) data, len);\r
1538     }\r
1539 }\r
1540 \r
1541 int scp_finish_filerecv(void)\r
1542 {\r
1543     if (using_sftp) {\r
1544         struct sftp_packet *pktin;\r
1545         struct sftp_request *req, *rreq;\r
1546 \r
1547         /*\r
1548          * Ensure that xfer_done() will work correctly, so we can\r
1549          * clean up any outstanding requests from the file\r
1550          * transfer.\r
1551          */\r
1552         xfer_set_error(scp_sftp_xfer);\r
1553         while (!xfer_done(scp_sftp_xfer)) {\r
1554             void *vbuf;\r
1555             int len;\r
1556 \r
1557             pktin = sftp_recv();\r
1558             xfer_download_gotpkt(scp_sftp_xfer, pktin);\r
1559             if (xfer_download_data(scp_sftp_xfer, &vbuf, &len))\r
1560                 sfree(vbuf);\r
1561         }\r
1562         xfer_cleanup(scp_sftp_xfer);\r
1563 \r
1564         sftp_register(req = fxp_close_send(scp_sftp_filehandle));\r
1565         rreq = sftp_find_request(pktin = sftp_recv());\r
1566         assert(rreq == req);\r
1567         fxp_close_recv(pktin, rreq);\r
1568         return 0;\r
1569     } else {\r
1570         back->send(backhandle, "", 1);\r
1571         return response();\r
1572     }\r
1573 }\r
1574 \r
1575 /* ----------------------------------------------------------------------\r
1576  *  Send an error message to the other side and to the screen.\r
1577  *  Increment error counter.\r
1578  */\r
1579 static void run_err(const char *fmt, ...)\r
1580 {\r
1581     char *str, *str2;\r
1582     va_list ap;\r
1583     va_start(ap, fmt);\r
1584     errs++;\r
1585     str = dupvprintf(fmt, ap);\r
1586     str2 = dupcat("scp: ", str, "\n", NULL);\r
1587     sfree(str);\r
1588     scp_send_errmsg(str2);\r
1589     tell_user(stderr, "%s", str2);\r
1590     va_end(ap);\r
1591     sfree(str2);\r
1592 }\r
1593 \r
1594 /*\r
1595  *  Execute the source part of the SCP protocol.\r
1596  */\r
1597 static void source(char *src)\r
1598 {\r
1599     uint64 size;\r
1600     unsigned long mtime, atime;\r
1601     char *last;\r
1602     RFile *f;\r
1603     int attr;\r
1604     uint64 i;\r
1605     uint64 stat_bytes;\r
1606     time_t stat_starttime, stat_lasttime;\r
1607 \r
1608     attr = file_type(src);\r
1609     if (attr == FILE_TYPE_NONEXISTENT ||\r
1610         attr == FILE_TYPE_WEIRD) {\r
1611         run_err("%s: %s file or directory", src,\r
1612                 (attr == FILE_TYPE_WEIRD ? "Not a" : "No such"));\r
1613         return;\r
1614     }\r
1615 \r
1616     if (attr == FILE_TYPE_DIRECTORY) {\r
1617         if (recursive) {\r
1618             /*\r
1619              * Avoid . and .. directories.\r
1620              */\r
1621             char *p;\r
1622             p = strrchr(src, '/');\r
1623             if (!p)\r
1624                 p = strrchr(src, '\\');\r
1625             if (!p)\r
1626                 p = src;\r
1627             else\r
1628                 p++;\r
1629             if (!strcmp(p, ".") || !strcmp(p, ".."))\r
1630                 /* skip . and .. */ ;\r
1631             else\r
1632                 rsource(src);\r
1633         } else {\r
1634             run_err("%s: not a regular file", src);\r
1635         }\r
1636         return;\r
1637     }\r
1638 \r
1639     if ((last = strrchr(src, '/')) == NULL)\r
1640         last = src;\r
1641     else\r
1642         last++;\r
1643     if (strrchr(last, '\\') != NULL)\r
1644         last = strrchr(last, '\\') + 1;\r
1645     if (last == src && strchr(src, ':') != NULL)\r
1646         last = strchr(src, ':') + 1;\r
1647 \r
1648     f = open_existing_file(src, &size, &mtime, &atime);\r
1649     if (f == NULL) {\r
1650         run_err("%s: Cannot open file", src);\r
1651         return;\r
1652     }\r
1653     if (preserve) {\r
1654         if (scp_send_filetimes(mtime, atime))\r
1655             return;\r
1656     }\r
1657 \r
1658     if (verbose) {\r
1659         char sizestr[40];\r
1660         uint64_decimal(size, sizestr);\r
1661         tell_user(stderr, "Sending file %s, size=%s", last, sizestr);\r
1662     }\r
1663     if (scp_send_filename(last, size, 0644))\r
1664         return;\r
1665 \r
1666     stat_bytes = uint64_make(0,0);\r
1667     stat_starttime = time(NULL);\r
1668     stat_lasttime = 0;\r
1669 \r
1670     for (i = uint64_make(0,0);\r
1671          uint64_compare(i,size) < 0;\r
1672          i = uint64_add32(i,4096)) {\r
1673         char transbuf[4096];\r
1674         int j, k = 4096;\r
1675 \r
1676         if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */ \r
1677             k = (uint64_subtract(size, i)).lo;  /* k = size - i; */\r
1678         if ((j = read_from_file(f, transbuf, k)) != k) {\r
1679             if (statistics)\r
1680                 printf("\n");\r
1681             bump("%s: Read error", src);\r
1682         }\r
1683         if (scp_send_filedata(transbuf, k))\r
1684             bump("%s: Network error occurred", src);\r
1685 \r
1686         if (statistics) {\r
1687             stat_bytes = uint64_add32(stat_bytes, k);\r
1688             if (time(NULL) != stat_lasttime ||\r
1689                 (uint64_compare(uint64_add32(i, k), size) == 0)) {\r
1690                 stat_lasttime = time(NULL);\r
1691                 print_stats(last, size, stat_bytes,\r
1692                             stat_starttime, stat_lasttime);\r
1693             }\r
1694         }\r
1695 \r
1696     }\r
1697     close_rfile(f);\r
1698 \r
1699     (void) scp_send_finish();\r
1700 }\r
1701 \r
1702 /*\r
1703  *  Recursively send the contents of a directory.\r
1704  */\r
1705 static void rsource(char *src)\r
1706 {\r
1707     char *last;\r
1708     char *save_target;\r
1709     DirHandle *dir;\r
1710 \r
1711     if ((last = strrchr(src, '/')) == NULL)\r
1712         last = src;\r
1713     else\r
1714         last++;\r
1715     if (strrchr(last, '\\') != NULL)\r
1716         last = strrchr(last, '\\') + 1;\r
1717     if (last == src && strchr(src, ':') != NULL)\r
1718         last = strchr(src, ':') + 1;\r
1719 \r
1720     /* maybe send filetime */\r
1721 \r
1722     save_target = scp_save_remotepath();\r
1723 \r
1724     if (verbose)\r
1725         tell_user(stderr, "Entering directory: %s", last);\r
1726     if (scp_send_dirname(last, 0755))\r
1727         return;\r
1728 \r
1729     dir = open_directory(src);\r
1730     if (dir != NULL) {\r
1731         char *filename;\r
1732         while ((filename = read_filename(dir)) != NULL) {\r
1733             char *foundfile = dupcat(src, "/", filename, NULL);\r
1734             source(foundfile);\r
1735             sfree(foundfile);\r
1736             sfree(filename);\r
1737         }\r
1738     }\r
1739     close_directory(dir);\r
1740 \r
1741     (void) scp_send_enddir();\r
1742 \r
1743     scp_restore_remotepath(save_target);\r
1744 }\r
1745 \r
1746 /*\r
1747  * Execute the sink part of the SCP protocol.\r
1748  */\r
1749 static void sink(char *targ, char *src)\r
1750 {\r
1751     char *destfname;\r
1752     int targisdir = 0;\r
1753     int exists;\r
1754     int attr;\r
1755     WFile *f;\r
1756     uint64 received;\r
1757     int wrerror = 0;\r
1758     uint64 stat_bytes;\r
1759     time_t stat_starttime, stat_lasttime;\r
1760     char *stat_name;\r
1761 \r
1762     attr = file_type(targ);\r
1763     if (attr == FILE_TYPE_DIRECTORY)\r
1764         targisdir = 1;\r
1765 \r
1766     if (targetshouldbedirectory && !targisdir)\r
1767         bump("%s: Not a directory", targ);\r
1768 \r
1769     scp_sink_init();\r
1770     while (1) {\r
1771         struct scp_sink_action act;\r
1772         if (scp_get_sink_action(&act))\r
1773             return;\r
1774 \r
1775         if (act.action == SCP_SINK_ENDDIR)\r
1776             return;\r
1777 \r
1778         if (act.action == SCP_SINK_RETRY)\r
1779             continue;\r
1780 \r
1781         if (targisdir) {\r
1782             /*\r
1783              * Prevent the remote side from maliciously writing to\r
1784              * files outside the target area by sending a filename\r
1785              * containing `../'. In fact, it shouldn't be sending\r
1786              * filenames with any slashes or colons in at all; so\r
1787              * we'll find the last slash, backslash or colon in the\r
1788              * filename and use only the part after that. (And\r
1789              * warn!)\r
1790              * \r
1791              * In addition, we also ensure here that if we're\r
1792              * copying a single file and the target is a directory\r
1793              * (common usage: `pscp host:filename .') the remote\r
1794              * can't send us a _different_ file name. We can\r
1795              * distinguish this case because `src' will be non-NULL\r
1796              * and the last component of that will fail to match\r
1797              * (the last component of) the name sent.\r
1798              * \r
1799              * Well, not always; if `src' is a wildcard, we do\r
1800              * expect to get back filenames that don't correspond\r
1801              * exactly to it. Ideally in this case, we would like\r
1802              * to ensure that the returned filename actually\r
1803              * matches the wildcard pattern - but one of SCP's\r
1804              * protocol infelicities is that wildcard matching is\r
1805              * done at the server end _by the server's rules_ and\r
1806              * so in general this is infeasible. Hence, we only\r
1807              * accept filenames that don't correspond to `src' if\r
1808              * unsafe mode is enabled or we are using SFTP (which\r
1809              * resolves remote wildcards on the client side and can\r
1810              * be trusted).\r
1811              */\r
1812             char *striptarget, *stripsrc;\r
1813 \r
1814             striptarget = stripslashes(act.name, 1);\r
1815             if (striptarget != act.name) {\r
1816                 tell_user(stderr, "warning: remote host sent a compound"\r
1817                           " pathname '%s'", act.name);\r
1818                 tell_user(stderr, "         renaming local file to '%s'",\r
1819                           striptarget);\r
1820             }\r
1821 \r
1822             /*\r
1823              * Also check to see if the target filename is '.' or\r
1824              * '..', or indeed '...' and so on because Windows\r
1825              * appears to interpret those like '..'.\r
1826              */\r
1827             if (is_dots(striptarget)) {\r
1828                 bump("security violation: remote host attempted to write to"\r
1829                      " a '.' or '..' path!");\r
1830             }\r
1831 \r
1832             if (src) {\r
1833                 stripsrc = stripslashes(src, 1);\r
1834                 if (strcmp(striptarget, stripsrc) &&\r
1835                     !using_sftp && !scp_unsafe_mode) {\r
1836                     tell_user(stderr, "warning: remote host tried to write "\r
1837                               "to a file called '%s'", striptarget);\r
1838                     tell_user(stderr, "         when we requested a file "\r
1839                               "called '%s'.", stripsrc);\r
1840                     tell_user(stderr, "         If this is a wildcard, "\r
1841                               "consider upgrading to SSH-2 or using");\r
1842                     tell_user(stderr, "         the '-unsafe' option. Renaming"\r
1843                               " of this file has been disallowed.");\r
1844                     /* Override the name the server provided with our own. */\r
1845                     striptarget = stripsrc;\r
1846                 }\r
1847             }\r
1848 \r
1849             if (targ[0] != '\0')\r
1850                 destfname = dir_file_cat(targ, striptarget);\r
1851             else\r
1852                 destfname = dupstr(striptarget);\r
1853         } else {\r
1854             /*\r
1855              * In this branch of the if, the target area is a\r
1856              * single file with an explicitly specified name in any\r
1857              * case, so there's no danger.\r
1858              */\r
1859             destfname = dupstr(targ);\r
1860         }\r
1861         attr = file_type(destfname);\r
1862         exists = (attr != FILE_TYPE_NONEXISTENT);\r
1863 \r
1864         if (act.action == SCP_SINK_DIR) {\r
1865             if (exists && attr != FILE_TYPE_DIRECTORY) {\r
1866                 run_err("%s: Not a directory", destfname);\r
1867                 continue;\r
1868             }\r
1869             if (!exists) {\r
1870                 if (!create_directory(destfname)) {\r
1871                     run_err("%s: Cannot create directory", destfname);\r
1872                     continue;\r
1873                 }\r
1874             }\r
1875             sink(destfname, NULL);\r
1876             /* can we set the timestamp for directories ? */\r
1877             continue;\r
1878         }\r
1879 \r
1880         f = open_new_file(destfname);\r
1881         if (f == NULL) {\r
1882             run_err("%s: Cannot create file", destfname);\r
1883             continue;\r
1884         }\r
1885 \r
1886         if (scp_accept_filexfer())\r
1887             return;\r
1888 \r
1889         stat_bytes = uint64_make(0, 0);\r
1890         stat_starttime = time(NULL);\r
1891         stat_lasttime = 0;\r
1892         stat_name = stripslashes(destfname, 1);\r
1893 \r
1894         received = uint64_make(0, 0);\r
1895         while (uint64_compare(received,act.size) < 0) {\r
1896             char transbuf[32768];\r
1897             uint64 blksize;\r
1898             int read;\r
1899             blksize = uint64_make(0, 32768);\r
1900             if (uint64_compare(blksize,uint64_subtract(act.size,received)) > 0)\r
1901               blksize = uint64_subtract(act.size,received);\r
1902             read = scp_recv_filedata(transbuf, (int)blksize.lo);\r
1903             if (read <= 0)\r
1904                 bump("Lost connection");\r
1905             if (wrerror)\r
1906                 continue;\r
1907             if (write_to_file(f, transbuf, read) != (int)read) {\r
1908                 wrerror = 1;\r
1909                 /* FIXME: in sftp we can actually abort the transfer */\r
1910                 if (statistics)\r
1911                     printf("\r%-25.25s | %50s\n",\r
1912                            stat_name,\r
1913                            "Write error.. waiting for end of file");\r
1914                 continue;\r
1915             }\r
1916             if (statistics) {\r
1917                 stat_bytes = uint64_add32(stat_bytes,read);\r
1918                 if (time(NULL) > stat_lasttime ||\r
1919                     uint64_compare(uint64_add32(received, read), act.size) == 0) {\r
1920                     stat_lasttime = time(NULL);\r
1921                     print_stats(stat_name, act.size, stat_bytes,\r
1922                                 stat_starttime, stat_lasttime);\r
1923                 }\r
1924             }\r
1925             received = uint64_add32(received, read);\r
1926         }\r
1927         if (act.settime) {\r
1928             set_file_times(f, act.mtime, act.atime);\r
1929         }\r
1930 \r
1931         close_wfile(f);\r
1932         if (wrerror) {\r
1933             run_err("%s: Write error", destfname);\r
1934             continue;\r
1935         }\r
1936         (void) scp_finish_filerecv();\r
1937         sfree(destfname);\r
1938         sfree(act.buf);\r
1939     }\r
1940 }\r
1941 \r
1942 /*\r
1943  * We will copy local files to a remote server.\r
1944  */\r
1945 static void toremote(int argc, char *argv[])\r
1946 {\r
1947     char *src, *targ, *host, *user;\r
1948     char *cmd;\r
1949     int i, wc_type;\r
1950 \r
1951     targ = argv[argc - 1];\r
1952 \r
1953     /* Separate host from filename */\r
1954     host = targ;\r
1955     targ = colon(targ);\r
1956     if (targ == NULL)\r
1957         bump("targ == NULL in toremote()");\r
1958     *targ++ = '\0';\r
1959     if (*targ == '\0')\r
1960         targ = ".";\r
1961     /* Substitute "." for empty target */\r
1962 \r
1963     /* Separate host and username */\r
1964     user = host;\r
1965     host = strrchr(host, '@');\r
1966     if (host == NULL) {\r
1967         host = user;\r
1968         user = NULL;\r
1969     } else {\r
1970         *host++ = '\0';\r
1971         if (*user == '\0')\r
1972             user = NULL;\r
1973     }\r
1974 \r
1975     if (argc == 2) {\r
1976         if (colon(argv[0]) != NULL)\r
1977             bump("%s: Remote to remote not supported", argv[0]);\r
1978 \r
1979         wc_type = test_wildcard(argv[0], 1);\r
1980         if (wc_type == WCTYPE_NONEXISTENT)\r
1981             bump("%s: No such file or directory\n", argv[0]);\r
1982         else if (wc_type == WCTYPE_WILDCARD)\r
1983             targetshouldbedirectory = 1;\r
1984     }\r
1985 \r
1986     cmd = dupprintf("scp%s%s%s%s -t %s",\r
1987                     verbose ? " -v" : "",\r
1988                     recursive ? " -r" : "",\r
1989                     preserve ? " -p" : "",\r
1990                     targetshouldbedirectory ? " -d" : "", targ);\r
1991     do_cmd(host, user, cmd);\r
1992     sfree(cmd);\r
1993 \r
1994     if (scp_source_setup(targ, targetshouldbedirectory))\r
1995         return;\r
1996 \r
1997     for (i = 0; i < argc - 1; i++) {\r
1998         src = argv[i];\r
1999         if (colon(src) != NULL) {\r
2000             tell_user(stderr, "%s: Remote to remote not supported\n", src);\r
2001             errs++;\r
2002             continue;\r
2003         }\r
2004 \r
2005         wc_type = test_wildcard(src, 1);\r
2006         if (wc_type == WCTYPE_NONEXISTENT) {\r
2007             run_err("%s: No such file or directory", src);\r
2008             continue;\r
2009         } else if (wc_type == WCTYPE_FILENAME) {\r
2010             source(src);\r
2011             continue;\r
2012         } else {\r
2013             WildcardMatcher *wc;\r
2014             char *filename;\r
2015 \r
2016             wc = begin_wildcard_matching(src);\r
2017             if (wc == NULL) {\r
2018                 run_err("%s: No such file or directory", src);\r
2019                 continue;\r
2020             }\r
2021 \r
2022             while ((filename = wildcard_get_filename(wc)) != NULL) {\r
2023                 source(filename);\r
2024                 sfree(filename);\r
2025             }\r
2026 \r
2027             finish_wildcard_matching(wc);\r
2028         }\r
2029     }\r
2030 }\r
2031 \r
2032 /*\r
2033  *  We will copy files from a remote server to the local machine.\r
2034  */\r
2035 static void tolocal(int argc, char *argv[])\r
2036 {\r
2037     char *src, *targ, *host, *user;\r
2038     char *cmd;\r
2039 \r
2040     if (argc != 2)\r
2041         bump("More than one remote source not supported");\r
2042 \r
2043     src = argv[0];\r
2044     targ = argv[1];\r
2045 \r
2046     /* Separate host from filename */\r
2047     host = src;\r
2048     src = colon(src);\r
2049     if (src == NULL)\r
2050         bump("Local to local copy not supported");\r
2051     *src++ = '\0';\r
2052     if (*src == '\0')\r
2053         src = ".";\r
2054     /* Substitute "." for empty filename */\r
2055 \r
2056     /* Separate username and hostname */\r
2057     user = host;\r
2058     host = strrchr(host, '@');\r
2059     if (host == NULL) {\r
2060         host = user;\r
2061         user = NULL;\r
2062     } else {\r
2063         *host++ = '\0';\r
2064         if (*user == '\0')\r
2065             user = NULL;\r
2066     }\r
2067 \r
2068     cmd = dupprintf("scp%s%s%s%s -f %s",\r
2069                     verbose ? " -v" : "",\r
2070                     recursive ? " -r" : "",\r
2071                     preserve ? " -p" : "",\r
2072                     targetshouldbedirectory ? " -d" : "", src);\r
2073     do_cmd(host, user, cmd);\r
2074     sfree(cmd);\r
2075 \r
2076     if (scp_sink_setup(src, preserve, recursive))\r
2077         return;\r
2078 \r
2079     sink(targ, src);\r
2080 }\r
2081 \r
2082 /*\r
2083  *  We will issue a list command to get a remote directory.\r
2084  */\r
2085 static void get_dir_list(int argc, char *argv[])\r
2086 {\r
2087     char *src, *host, *user;\r
2088     char *cmd, *p, *q;\r
2089     char c;\r
2090 \r
2091     src = argv[0];\r
2092 \r
2093     /* Separate host from filename */\r
2094     host = src;\r
2095     src = colon(src);\r
2096     if (src == NULL)\r
2097         bump("Local file listing not supported");\r
2098     *src++ = '\0';\r
2099     if (*src == '\0')\r
2100         src = ".";\r
2101     /* Substitute "." for empty filename */\r
2102 \r
2103     /* Separate username and hostname */\r
2104     user = host;\r
2105     host = strrchr(host, '@');\r
2106     if (host == NULL) {\r
2107         host = user;\r
2108         user = NULL;\r
2109     } else {\r
2110         *host++ = '\0';\r
2111         if (*user == '\0')\r
2112             user = NULL;\r
2113     }\r
2114 \r
2115     cmd = snewn(4 * strlen(src) + 100, char);\r
2116     strcpy(cmd, "ls -la '");\r
2117     p = cmd + strlen(cmd);\r
2118     for (q = src; *q; q++) {\r
2119         if (*q == '\'') {\r
2120             *p++ = '\'';\r
2121             *p++ = '\\';\r
2122             *p++ = '\'';\r
2123             *p++ = '\'';\r
2124         } else {\r
2125             *p++ = *q;\r
2126         }\r
2127     }\r
2128     *p++ = '\'';\r
2129     *p = '\0';\r
2130 \r
2131     do_cmd(host, user, cmd);\r
2132     sfree(cmd);\r
2133 \r
2134     if (using_sftp) {\r
2135         scp_sftp_listdir(src);\r
2136     } else {\r
2137         while (ssh_scp_recv((unsigned char *) &c, 1) > 0)\r
2138             tell_char(stdout, c);\r
2139     }\r
2140 }\r
2141 \r
2142 /*\r
2143  *  Short description of parameters.\r
2144  */\r
2145 static void usage(void)\r
2146 {\r
2147     printf("PuTTY Secure Copy client\n");\r
2148     printf("%s\n", ver);\r
2149     printf("Usage: pscp [options] [user@]host:source target\n");\r
2150     printf\r
2151         ("       pscp [options] source [source...] [user@]host:target\n");\r
2152     printf("       pscp [options] -ls [user@]host:filespec\n");\r
2153     printf("Options:\n");\r
2154     printf("  -V        print version information and exit\n");\r
2155     printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
2156     printf("  -p        preserve file attributes\n");\r
2157     printf("  -q        quiet, don't show statistics\n");\r
2158     printf("  -r        copy directories recursively\n");\r
2159     printf("  -v        show verbose messages\n");\r
2160     printf("  -load sessname  Load settings from saved session\n");\r
2161     printf("  -P port   connect to specified port\n");\r
2162     printf("  -l user   connect with specified username\n");\r
2163     printf("  -pw passw login with specified password\n");\r
2164     printf("  -1 -2     force use of particular SSH protocol version\n");\r
2165     printf("  -4 -6     force use of IPv4 or IPv6\n");\r
2166     printf("  -C        enable compression\n");\r
2167     printf("  -i key    private key file for authentication\n");\r
2168     printf("  -noagent  disable use of Pageant\n");\r
2169     printf("  -agent    enable use of Pageant\n");\r
2170     printf("  -batch    disable all interactive prompts\n");\r
2171     printf("  -unsafe   allow server-side wildcards (DANGEROUS)\n");\r
2172     printf("  -sftp     force use of SFTP protocol\n");\r
2173     printf("  -scp      force use of SCP protocol\n");\r
2174 #if 0\r
2175     /*\r
2176      * -gui is an internal option, used by GUI front ends to get\r
2177      * pscp to pass progress reports back to them. It's not an\r
2178      * ordinary user-accessible option, so it shouldn't be part of\r
2179      * the command-line help. The only people who need to know\r
2180      * about it are programmers, and they can read the source.\r
2181      */\r
2182     printf\r
2183         ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");\r
2184 #endif\r
2185     cleanup_exit(1);\r
2186 }\r
2187 \r
2188 void version(void)\r
2189 {\r
2190     printf("pscp: %s\n", ver);\r
2191     cleanup_exit(1);\r
2192 }\r
2193 \r
2194 void cmdline_error(char *p, ...)\r
2195 {\r
2196     va_list ap;\r
2197     fprintf(stderr, "pscp: ");\r
2198     va_start(ap, p);\r
2199     vfprintf(stderr, p, ap);\r
2200     va_end(ap);\r
2201     fprintf(stderr, "\n      try typing just \"pscp\" for help\n");\r
2202     exit(1);\r
2203 }\r
2204 \r
2205 /*\r
2206  * Main program. (Called `psftp_main' because it gets called from\r
2207  * *sftp.c; bit silly, I know, but it had to be called _something_.)\r
2208  */\r
2209 int psftp_main(int argc, char *argv[])\r
2210 {\r
2211     int i;\r
2212 \r
2213     default_protocol = PROT_TELNET;\r
2214 \r
2215     flags = FLAG_STDERR\r
2216 #ifdef FLAG_SYNCAGENT\r
2217         | FLAG_SYNCAGENT\r
2218 #endif\r
2219         ;\r
2220     cmdline_tooltype = TOOLTYPE_FILETRANSFER;\r
2221     sk_init();\r
2222 \r
2223     /* Load Default Settings before doing anything else. */\r
2224     do_defaults(NULL, &cfg);\r
2225     loaded_session = FALSE;\r
2226 \r
2227     for (i = 1; i < argc; i++) {\r
2228         int ret;\r
2229         if (argv[i][0] != '-')\r
2230             break;\r
2231         ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);\r
2232         if (ret == -2) {\r
2233             cmdline_error("option \"%s\" requires an argument", argv[i]);\r
2234         } else if (ret == 2) {\r
2235             i++;               /* skip next argument */\r
2236         } else if (ret == 1) {\r
2237             /* We have our own verbosity in addition to `flags'. */\r
2238             if (flags & FLAG_VERBOSE)\r
2239                 verbose = 1;\r
2240         } else if (strcmp(argv[i], "-pgpfp") == 0) {\r
2241             pgp_fingerprints();\r
2242             return 1;\r
2243         } else if (strcmp(argv[i], "-r") == 0) {\r
2244             recursive = 1;\r
2245         } else if (strcmp(argv[i], "-p") == 0) {\r
2246             preserve = 1;\r
2247         } else if (strcmp(argv[i], "-q") == 0) {\r
2248             statistics = 0;\r
2249         } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) {\r
2250             usage();\r
2251         } else if (strcmp(argv[i], "-V") == 0) {\r
2252             version();\r
2253         } else if (strcmp(argv[i], "-ls") == 0) {\r
2254             list = 1;\r
2255         } else if (strcmp(argv[i], "-batch") == 0) {\r
2256             console_batch_mode = 1;\r
2257         } else if (strcmp(argv[i], "-unsafe") == 0) {\r
2258             scp_unsafe_mode = 1;\r
2259         } else if (strcmp(argv[i], "-sftp") == 0) {\r
2260             try_scp = 0; try_sftp = 1;\r
2261         } else if (strcmp(argv[i], "-scp") == 0) {\r
2262             try_scp = 1; try_sftp = 0;\r
2263         } else if (strcmp(argv[i], "--") == 0) {\r
2264             i++;\r
2265             break;\r
2266         } else {\r
2267             cmdline_error("unknown option \"%s\"", argv[i]);\r
2268         }\r
2269     }\r
2270     argc -= i;\r
2271     argv += i;\r
2272     back = NULL;\r
2273 \r
2274     if (list) {\r
2275         if (argc != 1)\r
2276             usage();\r
2277         get_dir_list(argc, argv);\r
2278 \r
2279     } else {\r
2280 \r
2281         if (argc < 2)\r
2282             usage();\r
2283         if (argc > 2)\r
2284             targetshouldbedirectory = 1;\r
2285 \r
2286         if (colon(argv[argc - 1]) != NULL)\r
2287             toremote(argc, argv);\r
2288         else\r
2289             tolocal(argc, argv);\r
2290     }\r
2291 \r
2292     if (back != NULL && back->connected(backhandle)) {\r
2293         char ch;\r
2294         back->special(backhandle, TS_EOF);\r
2295         ssh_scp_recv((unsigned char *) &ch, 1);\r
2296     }\r
2297     random_save_seed();\r
2298 \r
2299     cmdline_cleanup();\r
2300     console_provide_logctx(NULL);\r
2301     back->free(backhandle);\r
2302     backhandle = NULL;\r
2303     back = NULL;\r
2304     sk_cleanup();\r
2305     return (errs == 0 ? 0 : 1);\r
2306 }\r
2307 \r
2308 /* end */\r