Branch data Line data Source code
1 : : /* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */
2 : : /*
3 : : * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 : : *
5 : : * Permission to use, copy, modify, and distribute this software for any
6 : : * purpose with or without fee is hereby granted, provided that the above
7 : : * copyright notice and this permission notice appear in all copies.
8 : : *
9 : : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 : : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 : : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 : : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : : */
17 : :
18 : : #include "includes.h"
19 : :
20 : : #include <sys/types.h>
21 : : #include <sys/ioctl.h>
22 : : #ifdef HAVE_SYS_STAT_H
23 : : # include <sys/stat.h>
24 : : #endif
25 : : #include <sys/param.h>
26 : : #include <sys/socket.h>
27 : : #include <sys/wait.h>
28 : : #ifdef HAVE_SYS_STATVFS_H
29 : : #include <sys/statvfs.h>
30 : : #endif
31 : :
32 : : #include <ctype.h>
33 : : #include <errno.h>
34 : :
35 : : #ifdef HAVE_PATHS_H
36 : : # include <paths.h>
37 : : #endif
38 : : #ifdef HAVE_LIBGEN_H
39 : : #include <libgen.h>
40 : : #endif
41 : : #ifdef HAVE_LOCALE_H
42 : : # include <locale.h>
43 : : #endif
44 : : #ifdef USE_LIBEDIT
45 : : #include <histedit.h>
46 : : #else
47 : : typedef void EditLine;
48 : : #endif
49 : : #include <signal.h>
50 : : #include <stdlib.h>
51 : : #include <stdio.h>
52 : : #include <string.h>
53 : : #include <unistd.h>
54 : : #include <stdarg.h>
55 : :
56 : : #ifdef HAVE_UTIL_H
57 : : # include <util.h>
58 : : #endif
59 : :
60 : : #include "xmalloc.h"
61 : : #include "log.h"
62 : : #include "pathnames.h"
63 : : #include "misc.h"
64 : :
65 : : #include "sftp.h"
66 : : #include "buffer.h"
67 : : #include "sftp-common.h"
68 : : #include "sftp-client.h"
69 : :
70 : : #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
71 : : #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
72 : :
73 : : /* File to read commands from */
74 : : FILE* infile;
75 : :
76 : : /* Are we in batchfile mode? */
77 : : int batchmode = 0;
78 : :
79 : : /* PID of ssh transport process */
80 : : static pid_t sshpid = -1;
81 : :
82 : : /* Suppress diagnositic messages */
83 : : int quiet = 0;
84 : :
85 : : /* This is set to 0 if the progressmeter is not desired. */
86 : : int showprogress = 1;
87 : :
88 : : /* When this option is set, we always recursively download/upload directories */
89 : : int global_rflag = 0;
90 : :
91 : : /* When this option is set, we resume download if possible */
92 : : int global_aflag = 0;
93 : :
94 : : /* When this option is set, the file transfers will always preserve times */
95 : : int global_pflag = 0;
96 : :
97 : : /* When this option is set, transfers will have fsync() called on each file */
98 : : int global_fflag = 0;
99 : :
100 : : /* SIGINT received during command processing */
101 : : volatile sig_atomic_t interrupted = 0;
102 : :
103 : : /* I wish qsort() took a separate ctx for the comparison function...*/
104 : : int sort_flag;
105 : :
106 : : /* Context used for commandline completion */
107 : : struct complete_ctx {
108 : : struct sftp_conn *conn;
109 : : char **remote_pathp;
110 : : };
111 : :
112 : : int remote_glob(struct sftp_conn *, const char *, int,
113 : : int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
114 : :
115 : : extern char *__progname;
116 : :
117 : : /* Separators for interactive commands */
118 : : #define WHITESPACE " \t\r\n"
119 : :
120 : : /* ls flags */
121 : : #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
122 : : #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
123 : : #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
124 : : #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
125 : : #define LS_TIME_SORT 0x0010 /* Sort by mtime */
126 : : #define LS_SIZE_SORT 0x0020 /* Sort by file size */
127 : : #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
128 : : #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
129 : : #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
130 : :
131 : : #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
132 : : #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
133 : :
134 : : /* Commands for interactive mode */
135 : : enum sftp_command {
136 : : I_CHDIR = 1,
137 : : I_CHGRP,
138 : : I_CHMOD,
139 : : I_CHOWN,
140 : : I_DF,
141 : : I_GET,
142 : : I_HELP,
143 : : I_LCHDIR,
144 : : I_LINK,
145 : : I_LLS,
146 : : I_LMKDIR,
147 : : I_LPWD,
148 : : I_LS,
149 : : I_LUMASK,
150 : : I_MKDIR,
151 : : I_PUT,
152 : : I_PWD,
153 : : I_QUIT,
154 : : I_RENAME,
155 : : I_RM,
156 : : I_RMDIR,
157 : : I_SHELL,
158 : : I_SYMLINK,
159 : : I_VERSION,
160 : : I_PROGRESS,
161 : : I_REGET,
162 : : };
163 : :
164 : : struct CMD {
165 : : const char *c;
166 : : const int n;
167 : : const int t;
168 : : };
169 : :
170 : : /* Type of completion */
171 : : #define NOARGS 0
172 : : #define REMOTE 1
173 : : #define LOCAL 2
174 : :
175 : : static const struct CMD cmds[] = {
176 : : { "bye", I_QUIT, NOARGS },
177 : : { "cd", I_CHDIR, REMOTE },
178 : : { "chdir", I_CHDIR, REMOTE },
179 : : { "chgrp", I_CHGRP, REMOTE },
180 : : { "chmod", I_CHMOD, REMOTE },
181 : : { "chown", I_CHOWN, REMOTE },
182 : : { "df", I_DF, REMOTE },
183 : : { "dir", I_LS, REMOTE },
184 : : { "exit", I_QUIT, NOARGS },
185 : : { "get", I_GET, REMOTE },
186 : : { "help", I_HELP, NOARGS },
187 : : { "lcd", I_LCHDIR, LOCAL },
188 : : { "lchdir", I_LCHDIR, LOCAL },
189 : : { "lls", I_LLS, LOCAL },
190 : : { "lmkdir", I_LMKDIR, LOCAL },
191 : : { "ln", I_LINK, REMOTE },
192 : : { "lpwd", I_LPWD, LOCAL },
193 : : { "ls", I_LS, REMOTE },
194 : : { "lumask", I_LUMASK, NOARGS },
195 : : { "mkdir", I_MKDIR, REMOTE },
196 : : { "mget", I_GET, REMOTE },
197 : : { "mput", I_PUT, LOCAL },
198 : : { "progress", I_PROGRESS, NOARGS },
199 : : { "put", I_PUT, LOCAL },
200 : : { "pwd", I_PWD, REMOTE },
201 : : { "quit", I_QUIT, NOARGS },
202 : : { "reget", I_REGET, REMOTE },
203 : : { "rename", I_RENAME, REMOTE },
204 : : { "rm", I_RM, REMOTE },
205 : : { "rmdir", I_RMDIR, REMOTE },
206 : : { "symlink", I_SYMLINK, REMOTE },
207 : : { "version", I_VERSION, NOARGS },
208 : : { "!", I_SHELL, NOARGS },
209 : : { "?", I_HELP, NOARGS },
210 : : { NULL, -1, -1 }
211 : : };
212 : :
213 : : int interactive_loop(struct sftp_conn *, char *file1, char *file2);
214 : :
215 : : /* ARGSUSED */
216 : : static void
217 : 0 : killchild(int signo)
218 : : {
219 [ # # ]: 0 : if (sshpid > 1) {
220 : 0 : kill(sshpid, SIGTERM);
221 : 0 : waitpid(sshpid, NULL, 0);
222 : : }
223 : :
224 : 0 : _exit(1);
225 : : }
226 : :
227 : : /* ARGSUSED */
228 : : static void
229 : 0 : cmd_interrupt(int signo)
230 : : {
231 : 0 : const char msg[] = "\rInterrupt \n";
232 : 0 : int olderrno = errno;
233 : :
234 : 0 : (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
235 : 0 : interrupted = 1;
236 : 0 : errno = olderrno;
237 : 0 : }
238 : :
239 : : static void
240 : : help(void)
241 : : {
242 : : printf("Available commands:\n"
243 : : "bye Quit sftp\n"
244 : : "cd path Change remote directory to 'path'\n"
245 : : "chgrp grp path Change group of file 'path' to 'grp'\n"
246 : : "chmod mode path Change permissions of file 'path' to 'mode'\n"
247 : : "chown own path Change owner of file 'path' to 'own'\n"
248 : : "df [-hi] [path] Display statistics for current directory or\n"
249 : : " filesystem containing 'path'\n"
250 : : "exit Quit sftp\n"
251 : : "get [-Ppr] remote [local] Download file\n"
252 : : "reget remote [local] Resume download file\n"
253 : : "help Display this help text\n"
254 : : "lcd path Change local directory to 'path'\n"
255 : : "lls [ls-options [path]] Display local directory listing\n"
256 : : "lmkdir path Create local directory\n"
257 : : "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
258 : : "lpwd Print local working directory\n"
259 : : "ls [-1afhlnrSt] [path] Display remote directory listing\n"
260 : : "lumask umask Set local umask to 'umask'\n"
261 : : "mkdir path Create remote directory\n"
262 : : "progress Toggle display of progress meter\n"
263 : : "put [-Ppr] local [remote] Upload file\n"
264 : : "pwd Display remote working directory\n"
265 : : "quit Quit sftp\n"
266 : : "rename oldpath newpath Rename remote file\n"
267 : : "rm path Delete remote file\n"
268 : : "rmdir path Remove remote directory\n"
269 : : "symlink oldpath newpath Symlink remote file\n"
270 : : "version Show SFTP version\n"
271 : : "!command Execute 'command' in local shell\n"
272 : : "! Escape to local shell\n"
273 : : "? Synonym for help\n");
274 : : }
275 : :
276 : : static void
277 : 3 : local_do_shell(const char *args)
278 : : {
279 : : int status;
280 : : char *shell;
281 : : pid_t pid;
282 : :
283 [ - + ]: 3 : if (!*args)
284 : 0 : args = NULL;
285 : :
286 [ + - ][ - + ]: 3 : if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
287 : 0 : shell = _PATH_BSHELL;
288 : :
289 [ - + ]: 3 : if ((pid = fork()) == -1)
290 : 0 : fatal("Couldn't fork: %s", strerror(errno));
291 : :
292 [ + + ]: 6 : if (pid == 0) {
293 : : /* XXX: child has pipe fds to ssh subproc open - issue? */
294 [ + - ]: 3 : if (args) {
295 : 3 : debug3("Executing %s -c \"%s\"", shell, args);
296 : 0 : execl(shell, shell, "-c", args, (char *)NULL);
297 : : } else {
298 : 0 : debug3("Executing %s", shell);
299 : 0 : execl(shell, shell, (char *)NULL);
300 : : }
301 : 0 : fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
302 : 0 : strerror(errno));
303 : 0 : _exit(1);
304 : : }
305 [ - + ]: 3 : while (waitpid(pid, &status, 0) == -1)
306 [ # # ]: 0 : if (errno != EINTR)
307 : 3 : fatal("Couldn't wait for child: %s", strerror(errno));
308 [ - + ]: 3 : if (!WIFEXITED(status))
309 : 0 : error("Shell exited abnormally");
310 [ - + ]: 3 : else if (WEXITSTATUS(status))
311 : 0 : error("Shell exited with status %d", WEXITSTATUS(status));
312 : 3 : }
313 : :
314 : : static void
315 : 2 : local_do_ls(const char *args)
316 : : {
317 [ + - ][ + + ]: 2 : if (!args || !*args)
318 : 1 : local_do_shell(_PATH_LS);
319 : : else {
320 : 1 : int len = strlen(_PATH_LS " ") + strlen(args) + 1;
321 : 1 : char *buf = xmalloc(len);
322 : :
323 : : /* XXX: quoting - rip quoting code from ftp? */
324 : 1 : snprintf(buf, len, _PATH_LS " %s", args);
325 : 1 : local_do_shell(buf);
326 : 1 : free(buf);
327 : : }
328 : 2 : }
329 : :
330 : : /* Strip one path (usually the pwd) from the start of another */
331 : : static char *
332 : 755 : path_strip(char *path, char *strip)
333 : : {
334 : : size_t len;
335 : :
336 [ + + ]: 755 : if (strip == NULL)
337 : 631 : return (xstrdup(path));
338 : :
339 : 124 : len = strlen(strip);
340 [ + - ]: 124 : if (strncmp(path, strip, len) == 0) {
341 [ + - ][ + + ]: 124 : if (strip[len - 1] != '/' && path[len] == '/')
342 : 123 : len++;
343 : 124 : return (xstrdup(path + len));
344 : : }
345 : :
346 : 0 : return (xstrdup(path));
347 : : }
348 : :
349 : : static char *
350 : 183 : make_absolute(char *p, char *pwd)
351 : : {
352 : : char *abs_str;
353 : :
354 : : /* Derelativise */
355 [ + - ][ + + ]: 183 : if (p && p[0] != '/') {
356 : 3 : abs_str = path_append(pwd, p);
357 : 3 : free(p);
358 : 3 : return(abs_str);
359 : : } else
360 : : return(p);
361 : : }
362 : :
363 : : static int
364 : 70 : parse_getput_flags(const char *cmd, char **argv, int argc,
365 : : int *aflag, int *fflag, int *pflag, int *rflag)
366 : : {
367 : : extern int opterr, optind, optopt, optreset;
368 : : int ch;
369 : :
370 : 70 : optind = optreset = 1;
371 : 70 : opterr = 0;
372 : :
373 : 70 : *aflag = *fflag = *rflag = *pflag = 0;
374 [ - + ]: 70 : while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
375 [ # # # # : 0 : switch (ch) {
# ]
376 : : case 'a':
377 : 0 : *aflag = 1;
378 : 0 : break;
379 : : case 'f':
380 : 0 : *fflag = 1;
381 : 0 : break;
382 : : case 'p':
383 : : case 'P':
384 : 0 : *pflag = 1;
385 : 0 : break;
386 : : case 'r':
387 : : case 'R':
388 : 0 : *rflag = 1;
389 : 0 : break;
390 : : default:
391 : 0 : error("%s: Invalid flag -%c", cmd, optopt);
392 : 0 : return -1;
393 : : }
394 : : }
395 : :
396 : 70 : return optind;
397 : : }
398 : :
399 : : static int
400 : 14 : parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
401 : : {
402 : : extern int opterr, optind, optopt, optreset;
403 : : int ch;
404 : :
405 : 14 : optind = optreset = 1;
406 : 14 : opterr = 0;
407 : :
408 : 14 : *sflag = 0;
409 [ + + ]: 21 : while ((ch = getopt(argc, argv, "s")) != -1) {
410 [ + - ]: 7 : switch (ch) {
411 : : case 's':
412 : 7 : *sflag = 1;
413 : 7 : break;
414 : : default:
415 : 0 : error("%s: Invalid flag -%c", cmd, optopt);
416 : 0 : return -1;
417 : : }
418 : : }
419 : :
420 : 14 : return optind;
421 : : }
422 : :
423 : : static int
424 : 16 : parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
425 : : {
426 : : extern int opterr, optind, optopt, optreset;
427 : : int ch;
428 : :
429 : 16 : optind = optreset = 1;
430 : 16 : opterr = 0;
431 : :
432 : 16 : *lflag = 0;
433 [ + + ]: 22 : while ((ch = getopt(argc, argv, "l")) != -1) {
434 [ + - ]: 6 : switch (ch) {
435 : : case 'l':
436 : 6 : *lflag = 1;
437 : 6 : break;
438 : : default:
439 : 0 : error("%s: Invalid flag -%c", cmd, optopt);
440 : 0 : return -1;
441 : : }
442 : : }
443 : :
444 : 16 : return optind;
445 : : }
446 : :
447 : : static int
448 : 25 : parse_ls_flags(char **argv, int argc, int *lflag)
449 : : {
450 : : extern int opterr, optind, optopt, optreset;
451 : : int ch;
452 : :
453 : 25 : optind = optreset = 1;
454 : 25 : opterr = 0;
455 : :
456 : 25 : *lflag = LS_NAME_SORT;
457 [ + + ]: 56 : while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
458 [ - - - - : 31 : switch (ch) {
- + + - -
- ]
459 : : case '1':
460 : 0 : *lflag &= ~VIEW_FLAGS;
461 : 0 : *lflag |= LS_SHORT_VIEW;
462 : 0 : break;
463 : : case 'S':
464 : 0 : *lflag &= ~SORT_FLAGS;
465 : 0 : *lflag |= LS_SIZE_SORT;
466 : 0 : break;
467 : : case 'a':
468 : 0 : *lflag |= LS_SHOW_ALL;
469 : 0 : break;
470 : : case 'f':
471 : 0 : *lflag &= ~SORT_FLAGS;
472 : 0 : break;
473 : : case 'h':
474 : 0 : *lflag |= LS_SI_UNITS;
475 : 0 : break;
476 : : case 'l':
477 : 23 : *lflag &= ~LS_SHORT_VIEW;
478 : 23 : *lflag |= LS_LONG_VIEW;
479 : 23 : break;
480 : : case 'n':
481 : 8 : *lflag &= ~LS_SHORT_VIEW;
482 : 8 : *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
483 : 8 : break;
484 : : case 'r':
485 : 0 : *lflag |= LS_REVERSE_SORT;
486 : 0 : break;
487 : : case 't':
488 : 0 : *lflag &= ~SORT_FLAGS;
489 : 0 : *lflag |= LS_TIME_SORT;
490 : 0 : break;
491 : : default:
492 : 0 : error("ls: Invalid flag -%c", optopt);
493 : 31 : return -1;
494 : : }
495 : : }
496 : :
497 : 25 : return optind;
498 : : }
499 : :
500 : : static int
501 : 4 : parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
502 : : {
503 : : extern int opterr, optind, optopt, optreset;
504 : : int ch;
505 : :
506 : 4 : optind = optreset = 1;
507 : 4 : opterr = 0;
508 : :
509 : 4 : *hflag = *iflag = 0;
510 [ - + ]: 4 : while ((ch = getopt(argc, argv, "hi")) != -1) {
511 [ # # # ]: 0 : switch (ch) {
512 : : case 'h':
513 : 0 : *hflag = 1;
514 : 0 : break;
515 : : case 'i':
516 : 0 : *iflag = 1;
517 : 0 : break;
518 : : default:
519 : 0 : error("%s: Invalid flag -%c", cmd, optopt);
520 : 0 : return -1;
521 : : }
522 : : }
523 : :
524 : 4 : return optind;
525 : : }
526 : :
527 : : static int
528 : 52 : parse_no_flags(const char *cmd, char **argv, int argc)
529 : : {
530 : : extern int opterr, optind, optopt, optreset;
531 : : int ch;
532 : :
533 : 52 : optind = optreset = 1;
534 : 52 : opterr = 0;
535 : :
536 [ - + ]: 52 : while ((ch = getopt(argc, argv, "")) != -1) {
537 : : switch (ch) {
538 : : default:
539 : 0 : error("%s: Invalid flag -%c", cmd, optopt);
540 : 0 : return -1;
541 : : }
542 : : }
543 : :
544 : 52 : return optind;
545 : : }
546 : :
547 : : static int
548 : 33 : is_dir(char *path)
549 : : {
550 : : struct stat sb;
551 : :
552 : : /* XXX: report errors? */
553 [ + + ]: 33 : if (stat(path, &sb) == -1)
554 : : return(0);
555 : :
556 : 3 : return(S_ISDIR(sb.st_mode));
557 : : }
558 : :
559 : : static int
560 : 30 : remote_is_dir(struct sftp_conn *conn, char *path)
561 : : {
562 : : Attrib *a;
563 : :
564 : : /* XXX: report errors? */
565 [ + + ]: 30 : if ((a = do_stat(conn, path, 1)) == NULL)
566 : : return(0);
567 [ + - ]: 3 : if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
568 : : return(0);
569 : 3 : return(S_ISDIR(a->perm));
570 : : }
571 : :
572 : : /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
573 : : static int
574 : 87 : pathname_is_dir(char *pathname)
575 : : {
576 : 87 : size_t l = strlen(pathname);
577 : :
578 [ + - ][ + - ]: 87 : return l > 0 && pathname[l - 1] == '/';
579 : : }
580 : :
581 : : static int
582 : 38 : process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
583 : : int pflag, int rflag, int resume, int fflag)
584 : : {
585 : 38 : char *abs_src = NULL;
586 : 38 : char *abs_dst = NULL;
587 : : glob_t g;
588 : 38 : char *filename, *tmp=NULL;
589 : 38 : int i, err = 0;
590 : :
591 : 38 : abs_src = xstrdup(src);
592 : 38 : abs_src = make_absolute(abs_src, pwd);
593 : : memset(&g, 0, sizeof(g));
594 : :
595 : 38 : debug3("Looking up %s", abs_src);
596 [ + + ]: 38 : if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
597 : 3 : error("File \"%s\" not found.", abs_src);
598 : 3 : err = -1;
599 : 3 : goto out;
600 : : }
601 : :
602 : : /*
603 : : * If multiple matches then dst must be a directory or
604 : : * unspecified.
605 : : */
606 [ + + ][ + + ]: 35 : if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
[ + + ]
607 : 1 : error("Multiple source paths, but destination "
608 : : "\"%s\" is not a directory", dst);
609 : 1 : err = -1;
610 : 1 : goto out;
611 : : }
612 : :
613 [ + + ][ + - ]: 92 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
614 : 58 : tmp = xstrdup(g.gl_pathv[i]);
615 [ - + ]: 58 : if ((filename = basename(tmp)) == NULL) {
616 : 0 : error("basename %s: %s", tmp, strerror(errno));
617 : 0 : free(tmp);
618 : 0 : err = -1;
619 : 0 : goto out;
620 : : }
621 : :
622 [ + + ][ + + ]: 58 : if (g.gl_matchc == 1 && dst) {
623 [ + + ]: 31 : if (is_dir(dst)) {
624 : 1 : abs_dst = path_append(dst, filename);
625 : : } else {
626 : 30 : abs_dst = xstrdup(dst);
627 : : }
628 [ + + ]: 27 : } else if (dst) {
629 : 13 : abs_dst = path_append(dst, filename);
630 : : } else {
631 : 14 : abs_dst = xstrdup(filename);
632 : : }
633 : 58 : free(tmp);
634 : :
635 : 58 : resume |= global_aflag;
636 [ + + ][ - + ]: 58 : if (!quiet && resume)
637 : 0 : printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
638 [ + + ]: 58 : else if (!quiet && !resume)
639 : 34 : printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
640 [ - + ][ # # ]: 58 : if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
[ # # ]
641 [ # # ][ # # ]: 0 : if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
[ # # ][ # # ]
[ # # ]
642 : 0 : pflag || global_pflag, 1, resume,
643 : 0 : fflag || global_fflag) == -1)
644 : 0 : err = -1;
645 : : } else {
646 [ + - ][ + - ]: 58 : if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
[ + - ][ + - ]
[ + + ]
647 : 58 : pflag || global_pflag, resume,
648 : 58 : fflag || global_fflag) == -1)
649 : 4 : err = -1;
650 : : }
651 : 58 : free(abs_dst);
652 : 58 : abs_dst = NULL;
653 : : }
654 : :
655 : : out:
656 : 38 : free(abs_src);
657 : 38 : globfree(&g);
658 : 38 : return(err);
659 : : }
660 : :
661 : : static int
662 : 32 : process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
663 : : int pflag, int rflag, int fflag)
664 : : {
665 : 32 : char *tmp_dst = NULL;
666 : 32 : char *abs_dst = NULL;
667 : 32 : char *tmp = NULL, *filename = NULL;
668 : : glob_t g;
669 : 32 : int err = 0;
670 : 32 : int i, dst_is_dir = 1;
671 : : struct stat sb;
672 : :
673 [ + + ]: 32 : if (dst) {
674 : 30 : tmp_dst = xstrdup(dst);
675 : 30 : tmp_dst = make_absolute(tmp_dst, pwd);
676 : : }
677 : :
678 : : memset(&g, 0, sizeof(g));
679 : 32 : debug3("Looking up %s", src);
680 [ - + ]: 32 : if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
681 : 0 : error("File \"%s\" not found.", src);
682 : 0 : err = -1;
683 : 0 : goto out;
684 : : }
685 : :
686 : : /* If we aren't fetching to pwd then stash this status for later */
687 [ + + ]: 32 : if (tmp_dst != NULL)
688 : 30 : dst_is_dir = remote_is_dir(conn, tmp_dst);
689 : :
690 : : /* If multiple matches, dst may be directory or unspecified */
691 [ + + ][ + + ]: 32 : if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
692 : 2 : error("Multiple paths match, but destination "
693 : : "\"%s\" is not a directory", tmp_dst);
694 : 2 : err = -1;
695 : 2 : goto out;
696 : : }
697 : :
698 [ + + ][ + - ]: 62 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
699 [ + + ]: 32 : if (stat(g.gl_pathv[i], &sb) == -1) {
700 : 3 : err = -1;
701 : 3 : error("stat %s: %s", g.gl_pathv[i], strerror(errno));
702 : 3 : continue;
703 : : }
704 : :
705 : 29 : tmp = xstrdup(g.gl_pathv[i]);
706 [ - + ]: 29 : if ((filename = basename(tmp)) == NULL) {
707 : 0 : error("basename %s: %s", tmp, strerror(errno));
708 : 0 : free(tmp);
709 : 0 : err = -1;
710 : 0 : goto out;
711 : : }
712 : :
713 [ + + ][ + + ]: 29 : if (g.gl_matchc == 1 && tmp_dst) {
714 : : /* If directory specified, append filename */
715 [ + + ]: 24 : if (dst_is_dir)
716 : 1 : abs_dst = path_append(tmp_dst, filename);
717 : : else
718 : 23 : abs_dst = xstrdup(tmp_dst);
719 [ + + ]: 5 : } else if (tmp_dst) {
720 : 2 : abs_dst = path_append(tmp_dst, filename);
721 : : } else {
722 : 3 : abs_dst = make_absolute(xstrdup(filename), pwd);
723 : : }
724 : 29 : free(tmp);
725 : :
726 [ + + ]: 29 : if (!quiet)
727 : 9 : printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
728 [ - + ][ # # ]: 29 : if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
[ # # ]
729 [ # # ][ # # ]: 0 : if (upload_dir(conn, g.gl_pathv[i], abs_dst,
[ # # ][ # # ]
[ # # ]
730 : 0 : pflag || global_pflag, 1,
731 : 0 : fflag || global_fflag) == -1)
732 : 0 : err = -1;
733 : : } else {
734 [ + - ][ + - ]: 29 : if (do_upload(conn, g.gl_pathv[i], abs_dst,
[ + - ][ + - ]
[ + + ]
735 : 29 : pflag || global_pflag,
736 : 29 : fflag || global_fflag) == -1)
737 : 3 : err = -1;
738 : : }
739 : : }
740 : :
741 : : out:
742 : 32 : free(abs_dst);
743 : 32 : free(tmp_dst);
744 : 32 : globfree(&g);
745 : 32 : return(err);
746 : : }
747 : :
748 : : static int
749 : 4192 : sdirent_comp(const void *aa, const void *bb)
750 : : {
751 : 4192 : SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
752 : 4192 : SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
753 [ + - ]: 4192 : int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
754 : :
755 : : #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
756 [ + - ]: 4192 : if (sort_flag & LS_NAME_SORT)
757 : 4192 : return (rmul * strcmp(a->filename, b->filename));
758 [ # # ]: 0 : else if (sort_flag & LS_TIME_SORT)
759 [ # # ][ # # ]: 0 : return (rmul * NCMP(a->a.mtime, b->a.mtime));
760 [ # # ]: 0 : else if (sort_flag & LS_SIZE_SORT)
761 [ # # ][ # # ]: 0 : return (rmul * NCMP(a->a.size, b->a.size));
762 : :
763 : 0 : fatal("Unknown ls sort type");
764 : : }
765 : :
766 : : /* sftp ls.1 replacement for directories */
767 : : static int
768 : 11 : do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
769 : : {
770 : : int n;
771 : 11 : u_int c = 1, colspace = 0, columns = 1;
772 : : SFTP_DIRENT **d;
773 : :
774 [ + + ]: 11 : if ((n = do_readdir(conn, path, &d)) != 0)
775 : : return (n);
776 : :
777 [ + - ]: 7 : if (!(lflag & LS_SHORT_VIEW)) {
778 : : u_int m = 0, width = 80;
779 : : struct winsize ws;
780 : : char *tmp;
781 : :
782 : : /* Count entries for sort and find longest filename */
783 [ + + ]: 755 : for (n = 0; d[n] != NULL; n++) {
784 [ + + ][ - + ]: 748 : if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
785 [ + + ]: 734 : m = MAX(m, strlen(d[n]->filename));
786 : : }
787 : :
788 : : /* Add any subpath that also needs to be counted */
789 : 7 : tmp = path_strip(path, strip_path);
790 : 7 : m += strlen(tmp);
791 : 7 : free(tmp);
792 : :
793 [ + + ]: 7 : if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
794 : 1 : width = ws.ws_col;
795 : :
796 : 7 : columns = width / (m + 2);
797 : 7 : columns = MAX(columns, 1);
798 : 7 : colspace = width / columns;
799 : 7 : colspace = MIN(colspace, width);
800 : : }
801 : :
802 [ + - ]: 7 : if (lflag & SORT_FLAGS) {
803 [ + + ]: 755 : for (n = 0; d[n] != NULL; n++)
804 : : ; /* count entries */
805 : 7 : sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
806 : 7 : qsort(d, n, sizeof(*d), sdirent_comp);
807 : : }
808 : :
809 [ + + ][ + - ]: 755 : for (n = 0; d[n] != NULL && !interrupted; n++) {
810 : : char *tmp, *fname;
811 : :
812 [ + + ][ + - ]: 748 : if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
813 : 14 : continue;
814 : :
815 : 734 : tmp = path_append(path, d[n]->filename);
816 : 734 : fname = path_strip(tmp, strip_path);
817 : 734 : free(tmp);
818 : :
819 [ + + ]: 734 : if (lflag & LS_LONG_VIEW) {
820 [ + + ]: 492 : if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
821 : : char *lname;
822 : : struct stat sb;
823 : :
824 : : memset(&sb, 0, sizeof(sb));
825 : 484 : attrib_to_stat(&d[n]->a, &sb);
826 : 484 : lname = ls_file(fname, &sb, 1,
827 : : (lflag & LS_SI_UNITS));
828 : : printf("%s\n", lname);
829 : 484 : free(lname);
830 : : } else
831 : 8 : printf("%s\n", d[n]->longname);
832 : : } else {
833 : : printf("%-*s", colspace, fname);
834 [ + + ]: 242 : if (c >= columns) {
835 : : printf("\n");
836 : 136 : c = 1;
837 : : } else
838 : 106 : c++;
839 : : }
840 : :
841 : 734 : free(fname);
842 : : }
843 : :
844 [ + + ][ + + ]: 7 : if (!(lflag & LS_LONG_VIEW) && (c != 1))
845 : : printf("\n");
846 : :
847 : 7 : free_sftp_dirents(d);
848 : 7 : return (0);
849 : : }
850 : :
851 : : /* sftp ls.1 replacement which handles path globs */
852 : : static int
853 : 24 : do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
854 : : int lflag)
855 : : {
856 : : char *fname, *lname;
857 : : glob_t g;
858 : : int err;
859 : : struct winsize ws;
860 : 24 : u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
861 : :
862 : : memset(&g, 0, sizeof(g));
863 : :
864 [ + - ]: 24 : if (remote_glob(conn, path,
865 : : GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
866 [ + - ]: 24 : NULL, &g) ||
867 [ - + ]: 24 : (g.gl_pathc && !g.gl_matchc)) {
868 [ # # ]: 0 : if (g.gl_pathc)
869 : 0 : globfree(&g);
870 : 0 : error("Can't ls: \"%s\" not found", path);
871 : 0 : return -1;
872 : : }
873 : :
874 [ + - ]: 24 : if (interrupted)
875 : : goto out;
876 : :
877 : : /*
878 : : * If the glob returns a single match and it is a directory,
879 : : * then just list its contents.
880 : : */
881 [ + - ][ + - ]: 24 : if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
[ + + ]
882 : 24 : S_ISDIR(g.gl_statv[0]->st_mode)) {
883 : 10 : err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
884 : 10 : globfree(&g);
885 : 10 : return err;
886 : : }
887 : :
888 [ - + ]: 14 : if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
889 : 0 : width = ws.ws_col;
890 : :
891 [ + - ]: 14 : if (!(lflag & LS_SHORT_VIEW)) {
892 : : /* Count entries for sort and find longest filename */
893 [ + + ]: 28 : for (i = 0; g.gl_pathv[i]; i++)
894 [ + - ]: 14 : m = MAX(m, strlen(g.gl_pathv[i]));
895 : :
896 : 14 : columns = width / (m + 2);
897 : 14 : columns = MAX(columns, 1);
898 : 14 : colspace = width / columns;
899 : : }
900 : :
901 [ + + ][ + - ]: 28 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
902 : 14 : fname = path_strip(g.gl_pathv[i], strip_path);
903 [ + - ]: 14 : if (lflag & LS_LONG_VIEW) {
904 [ - + ]: 14 : if (g.gl_statv[i] == NULL) {
905 : 0 : error("no stat information for %s", fname);
906 : 0 : continue;
907 : : }
908 : 14 : lname = ls_file(fname, g.gl_statv[i], 1,
909 : : (lflag & LS_SI_UNITS));
910 : : printf("%s\n", lname);
911 : 14 : free(lname);
912 : : } else {
913 : : printf("%-*s", colspace, fname);
914 [ # # ]: 0 : if (c >= columns) {
915 : : printf("\n");
916 : 0 : c = 1;
917 : : } else
918 : 0 : c++;
919 : : }
920 : 14 : free(fname);
921 : : }
922 : :
923 [ - + ][ # # ]: 14 : if (!(lflag & LS_LONG_VIEW) && (c != 1))
924 : : printf("\n");
925 : :
926 : : out:
927 [ + - ]: 14 : if (g.gl_pathc)
928 : 14 : globfree(&g);
929 : :
930 : : return 0;
931 : : }
932 : :
933 : : static int
934 : 4 : do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
935 : : {
936 : : struct sftp_statvfs st;
937 : : char s_used[FMT_SCALED_STRSIZE];
938 : : char s_avail[FMT_SCALED_STRSIZE];
939 : : char s_root[FMT_SCALED_STRSIZE];
940 : : char s_total[FMT_SCALED_STRSIZE];
941 : : unsigned long long ffree;
942 : :
943 [ + + ]: 4 : if (do_statvfs(conn, path, &st, 1) == -1)
944 : : return -1;
945 [ - + ]: 2 : if (iflag) {
946 [ # # ]: 0 : ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
947 : : printf(" Inodes Used Avail "
948 : : "(root) %%Capacity\n");
949 : : printf("%11llu %11llu %11llu %11llu %3llu%%\n",
950 : 0 : (unsigned long long)st.f_files,
951 : 0 : (unsigned long long)(st.f_files - st.f_ffree),
952 : 0 : (unsigned long long)st.f_favail,
953 : : (unsigned long long)st.f_ffree, ffree);
954 [ - + ]: 2 : } else if (hflag) {
955 : 0 : strlcpy(s_used, "error", sizeof(s_used));
956 : 0 : strlcpy(s_avail, "error", sizeof(s_avail));
957 : 0 : strlcpy(s_root, "error", sizeof(s_root));
958 : 0 : strlcpy(s_total, "error", sizeof(s_total));
959 : 0 : fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
960 : 0 : fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
961 : 0 : fmt_scaled(st.f_bfree * st.f_frsize, s_root);
962 : 0 : fmt_scaled(st.f_blocks * st.f_frsize, s_total);
963 : : printf(" Size Used Avail (root) %%Capacity\n");
964 : : printf("%7sB %7sB %7sB %7sB %3llu%%\n",
965 : : s_total, s_used, s_avail, s_root,
966 : 0 : (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
967 : : st.f_blocks));
968 : : } else {
969 : : printf(" Size Used Avail "
970 : : "(root) %%Capacity\n");
971 : : printf("%12llu %12llu %12llu %12llu %3llu%%\n",
972 : 2 : (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
973 : 2 : (unsigned long long)(st.f_frsize *
974 : 2 : (st.f_blocks - st.f_bfree) / 1024),
975 : 2 : (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
976 : 2 : (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
977 : 2 : (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
978 : : st.f_blocks));
979 : : }
980 : : return 0;
981 : : }
982 : :
983 : : /*
984 : : * Undo escaping of glob sequences in place. Used to undo extra escaping
985 : : * applied in makeargv() when the string is destined for a function that
986 : : * does not glob it.
987 : : */
988 : : static void
989 : 152 : undo_glob_escape(char *s)
990 : : {
991 : : size_t i, j;
992 : :
993 : 6252 : for (i = j = 0;;) {
994 [ + + ]: 6252 : if (s[i] == '\0') {
995 : 152 : s[j] = '\0';
996 : 152 : return;
997 : : }
998 [ + - ]: 6100 : if (s[i] != '\\') {
999 : 6100 : s[j++] = s[i++];
1000 : 6100 : continue;
1001 : : }
1002 : : /* s[i] == '\\' */
1003 : 0 : ++i;
1004 [ # # # ]: 0 : switch (s[i]) {
1005 : : case '?':
1006 : : case '[':
1007 : : case '*':
1008 : : case '\\':
1009 : 0 : s[j++] = s[i++];
1010 : 0 : break;
1011 : : case '\0':
1012 : 0 : s[j++] = '\\';
1013 : 0 : s[j] = '\0';
1014 : 0 : return;
1015 : : default:
1016 : 0 : s[j++] = '\\';
1017 : 0 : s[j++] = s[i++];
1018 : 0 : break;
1019 : : }
1020 : : }
1021 : : }
1022 : :
1023 : : /*
1024 : : * Split a string into an argument vector using sh(1)-style quoting,
1025 : : * comment and escaping rules, but with some tweaks to handle glob(3)
1026 : : * wildcards.
1027 : : * The "sloppy" flag allows for recovery from missing terminating quote, for
1028 : : * use in parsing incomplete commandlines during tab autocompletion.
1029 : : *
1030 : : * Returns NULL on error or a NULL-terminated array of arguments.
1031 : : *
1032 : : * If "lastquote" is not NULL, the quoting character used for the last
1033 : : * argument is placed in *lastquote ("\0", "'" or "\"").
1034 : : *
1035 : : * If "terminated" is not NULL, *terminated will be set to 1 when the
1036 : : * last argument's quote has been properly terminated or 0 otherwise.
1037 : : * This parameter is only of use if "sloppy" is set.
1038 : : */
1039 : : #define MAXARGS 128
1040 : : #define MAXARGLEN 8192
1041 : : static char **
1042 : 185 : makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1043 : : u_int *terminated)
1044 : : {
1045 : : int argc, quot;
1046 : : size_t i, j;
1047 : : static char argvs[MAXARGLEN];
1048 : : static char *argv[MAXARGS + 1];
1049 : : enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1050 : :
1051 : 185 : *argcp = argc = 0;
1052 [ - + ]: 185 : if (strlen(arg) > sizeof(argvs) - 1) {
1053 : : args_too_longs:
1054 : 0 : error("string too long");
1055 : 0 : return NULL;
1056 : : }
1057 [ - + ]: 185 : if (terminated != NULL)
1058 : 0 : *terminated = 1;
1059 [ - + ]: 185 : if (lastquote != NULL)
1060 : 185 : *lastquote = '\0';
1061 : : state = MA_START;
1062 : : i = j = 0;
1063 : : for (;;) {
1064 [ - + ]: 11633 : if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1065 : 0 : error("Too many arguments.");
1066 : 0 : return NULL;
1067 : : }
1068 [ + + ]: 11633 : if (isspace((unsigned char)arg[i])) {
1069 [ + + ]: 307 : if (state == MA_UNQUOTED) {
1070 : : /* Terminate current argument */
1071 : 305 : argvs[j++] = '\0';
1072 : 305 : argc++;
1073 : 305 : state = MA_START;
1074 [ + - ]: 2 : } else if (state != MA_START)
1075 : 2 : argvs[j++] = arg[i];
1076 [ + + ]: 11326 : } else if (arg[i] == '"' || arg[i] == '\'') {
1077 [ + + ]: 24 : q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1078 [ + + ]: 24 : if (state == MA_START) {
1079 : 10 : argv[argc] = argvs + j;
1080 : 10 : state = q;
1081 [ - + ]: 10 : if (lastquote != NULL)
1082 : 0 : *lastquote = arg[i];
1083 [ + + ]: 14 : } else if (state == MA_UNQUOTED)
1084 : : state = q;
1085 [ + + ]: 13 : else if (state == q)
1086 : : state = MA_UNQUOTED;
1087 : : else
1088 : 2 : argvs[j++] = arg[i];
1089 [ + + ]: 11302 : } else if (arg[i] == '\\') {
1090 [ + + ]: 21 : if (state == MA_SQUOTE || state == MA_DQUOTE) {
1091 [ + + ]: 8 : quot = state == MA_SQUOTE ? '\'' : '"';
1092 : : /* Unescape quote we are in */
1093 : : /* XXX support \n and friends? */
1094 [ + + ]: 8 : if (arg[i + 1] == quot) {
1095 : 5 : i++;
1096 : 5 : argvs[j++] = arg[i];
1097 [ + - ]: 3 : } else if (arg[i + 1] == '?' ||
1098 [ - + ]: 3 : arg[i + 1] == '[' || arg[i + 1] == '*') {
1099 : : /*
1100 : : * Special case for sftp: append
1101 : : * double-escaped glob sequence -
1102 : : * glob will undo one level of
1103 : : * escaping. NB. string can grow here.
1104 : : */
1105 [ # # ]: 0 : if (j >= sizeof(argvs) - 5)
1106 : : goto args_too_longs;
1107 : 0 : argvs[j++] = '\\';
1108 : 0 : argvs[j++] = arg[i++];
1109 : 0 : argvs[j++] = '\\';
1110 : 0 : argvs[j++] = arg[i];
1111 : : } else {
1112 : 3 : argvs[j++] = arg[i++];
1113 : 3 : argvs[j++] = arg[i];
1114 : : }
1115 : : } else {
1116 [ - + ]: 13 : if (state == MA_START) {
1117 : 0 : argv[argc] = argvs + j;
1118 : 0 : state = MA_UNQUOTED;
1119 [ # # ]: 0 : if (lastquote != NULL)
1120 : 0 : *lastquote = '\0';
1121 : : }
1122 [ + - ][ + + ]: 13 : if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1123 [ + + ]: 12 : arg[i + 1] == '*' || arg[i + 1] == '\\') {
1124 : : /*
1125 : : * Special case for sftp: append
1126 : : * escaped glob sequence -
1127 : : * glob will undo one level of
1128 : : * escaping.
1129 : : */
1130 : 4 : argvs[j++] = arg[i++];
1131 : 4 : argvs[j++] = arg[i];
1132 : : } else {
1133 : : /* Unescape everything */
1134 : : /* XXX support \n and friends? */
1135 : 9 : i++;
1136 : 9 : argvs[j++] = arg[i];
1137 : : }
1138 : : }
1139 [ - + ]: 11281 : } else if (arg[i] == '#') {
1140 [ # # ]: 0 : if (state == MA_SQUOTE || state == MA_DQUOTE)
1141 : 0 : argvs[j++] = arg[i];
1142 : : else
1143 : : goto string_done;
1144 [ + + ]: 11281 : } else if (arg[i] == '\0') {
1145 [ - + ]: 185 : if (state == MA_SQUOTE || state == MA_DQUOTE) {
1146 [ # # ]: 0 : if (sloppy) {
1147 : 0 : state = MA_UNQUOTED;
1148 [ # # ]: 0 : if (terminated != NULL)
1149 : 0 : *terminated = 0;
1150 : : goto string_done;
1151 : : }
1152 : 0 : error("Unterminated quoted argument");
1153 : 0 : return NULL;
1154 : : }
1155 : : string_done:
1156 [ + - ]: 185 : if (state == MA_UNQUOTED) {
1157 : 185 : argvs[j++] = '\0';
1158 : 185 : argc++;
1159 : : }
1160 : : break;
1161 : : } else {
1162 [ + + ]: 11096 : if (state == MA_START) {
1163 : 480 : argv[argc] = argvs + j;
1164 : 480 : state = MA_UNQUOTED;
1165 [ - + ]: 480 : if (lastquote != NULL)
1166 : 0 : *lastquote = '\0';
1167 : : }
1168 [ + + ][ + + ]: 11096 : if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1169 [ + + ]: 494 : (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1170 : : /*
1171 : : * Special case for sftp: escape quoted
1172 : : * glob(3) wildcards. NB. string can grow
1173 : : * here.
1174 : : */
1175 [ - + ]: 2 : if (j >= sizeof(argvs) - 3)
1176 : : goto args_too_longs;
1177 : 2 : argvs[j++] = '\\';
1178 : 2 : argvs[j++] = arg[i];
1179 : : } else
1180 : 11094 : argvs[j++] = arg[i];
1181 : : }
1182 : 11448 : i++;
1183 : 11448 : }
1184 : 185 : *argcp = argc;
1185 : 185 : return argv;
1186 : : }
1187 : :
1188 : : static int
1189 : 190 : parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1190 : : int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1191 : : unsigned long *n_arg, char **path1, char **path2)
1192 : : {
1193 : 190 : const char *cmd, *cp = *cpp;
1194 : : char *cp2, **argv;
1195 : 190 : int base = 0;
1196 : : long l;
1197 : : int i, cmdnum, optidx, argc;
1198 : :
1199 : : /* Skip leading whitespace */
1200 : 190 : cp = cp + strspn(cp, WHITESPACE);
1201 : :
1202 : : /* Check for leading '-' (disable error processing) */
1203 : 190 : *ignore_errors = 0;
1204 [ + + ]: 190 : if (*cp == '-') {
1205 : 1 : *ignore_errors = 1;
1206 : 1 : cp++;
1207 : 1 : cp = cp + strspn(cp, WHITESPACE);
1208 : : }
1209 : :
1210 : : /* Ignore blank lines and lines which begin with comment '#' char */
1211 [ + + ]: 190 : if (*cp == '\0' || *cp == '#')
1212 : : return (0);
1213 : :
1214 [ + - ]: 185 : if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1215 : : return -1;
1216 : :
1217 : : /* Figure out which command we have */
1218 [ + + ]: 3542 : for (i = 0; cmds[i].c != NULL; i++) {
1219 [ + - ][ + + ]: 3540 : if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1220 : : break;
1221 : : }
1222 : 185 : cmdnum = cmds[i].n;
1223 : 185 : cmd = cmds[i].c;
1224 : :
1225 : : /* Special case */
1226 [ + + ]: 185 : if (*cp == '!') {
1227 : 1 : cp++;
1228 : 1 : cmdnum = I_SHELL;
1229 [ + + ]: 184 : } else if (cmdnum == -1) {
1230 : 1 : error("Invalid command.");
1231 : 1 : return -1;
1232 : : }
1233 : :
1234 : : /* Get arguments and parse flags */
1235 : 184 : *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1236 : 184 : *rflag = *sflag = 0;
1237 : 184 : *path1 = *path2 = NULL;
1238 : 184 : optidx = 1;
1239 [ + + + - : 184 : switch (cmdnum) {
+ + + + +
- + - + ]
1240 : : case I_GET:
1241 : : case I_REGET:
1242 : : case I_PUT:
1243 [ + - ]: 70 : if ((optidx = parse_getput_flags(cmd, argv, argc,
1244 : : aflag, fflag, pflag, rflag)) == -1)
1245 : : return -1;
1246 : : /* Get first pathname (mandatory) */
1247 [ - + ]: 70 : if (argc - optidx < 1) {
1248 : 0 : error("You must specify at least one path after a "
1249 : : "%s command.", cmd);
1250 : 0 : return -1;
1251 : : }
1252 : 70 : *path1 = xstrdup(argv[optidx]);
1253 : : /* Get second pathname (optional) */
1254 [ + + ]: 70 : if (argc - optidx > 1) {
1255 : 66 : *path2 = xstrdup(argv[optidx + 1]);
1256 : : /* Destination is not globbed */
1257 : 66 : undo_glob_escape(*path2);
1258 : : }
1259 [ - + ][ # # ]: 70 : if (*aflag && cmdnum == I_PUT) {
1260 : : /* XXX implement resume for uploads */
1261 : 0 : error("Resume is not supported for uploads");
1262 : 0 : return -1;
1263 : : }
1264 : : break;
1265 : : case I_LINK:
1266 [ + - ]: 14 : if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1267 : : return -1;
1268 : : goto parse_two_paths;
1269 : : case I_RENAME:
1270 [ + - ]: 16 : if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1271 : : return -1;
1272 : : goto parse_two_paths;
1273 : : case I_SYMLINK:
1274 [ # # ]: 0 : if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1275 : : return -1;
1276 : : parse_two_paths:
1277 [ - + ]: 30 : if (argc - optidx < 2) {
1278 : 0 : error("You must specify two paths after a %s "
1279 : : "command.", cmd);
1280 : 0 : return -1;
1281 : : }
1282 : 30 : *path1 = xstrdup(argv[optidx]);
1283 : 30 : *path2 = xstrdup(argv[optidx + 1]);
1284 : : /* Paths are not globbed */
1285 : 30 : undo_glob_escape(*path1);
1286 : 30 : undo_glob_escape(*path2);
1287 : 30 : break;
1288 : : case I_RM:
1289 : : case I_MKDIR:
1290 : : case I_RMDIR:
1291 : : case I_CHDIR:
1292 : : case I_LCHDIR:
1293 : : case I_LMKDIR:
1294 [ + - ]: 30 : if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1295 : : return -1;
1296 : : /* Get pathname (mandatory) */
1297 [ - + ]: 30 : if (argc - optidx < 1) {
1298 : 0 : error("You must specify a path after a %s command.",
1299 : : cmd);
1300 : 0 : return -1;
1301 : : }
1302 : 30 : *path1 = xstrdup(argv[optidx]);
1303 : : /* Only "rm" globs */
1304 [ + + ]: 30 : if (cmdnum != I_RM)
1305 : 22 : undo_glob_escape(*path1);
1306 : : break;
1307 : : case I_DF:
1308 [ + - ]: 4 : if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1309 : : iflag)) == -1)
1310 : : return -1;
1311 : : /* Default to current directory if no path specified */
1312 [ - + ]: 4 : if (argc - optidx < 1)
1313 : 0 : *path1 = NULL;
1314 : : else {
1315 : 4 : *path1 = xstrdup(argv[optidx]);
1316 : 4 : undo_glob_escape(*path1);
1317 : : }
1318 : : break;
1319 : : case I_LS:
1320 [ + - ]: 25 : if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1321 : : return(-1);
1322 : : /* Path is optional */
1323 [ + + ]: 25 : if (argc - optidx > 0)
1324 : 24 : *path1 = xstrdup(argv[optidx]);
1325 : : break;
1326 : : case I_LLS:
1327 : : /* Skip ls command and following whitespace */
1328 : 2 : cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1329 : : case I_SHELL:
1330 : : /* Uses the rest of the line */
1331 : : break;
1332 : : case I_LUMASK:
1333 : : case I_CHMOD:
1334 : 6 : base = 8;
1335 : : case I_CHOWN:
1336 : : case I_CHGRP:
1337 [ + - ]: 6 : if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1338 : : return -1;
1339 : : /* Get numeric arg (mandatory) */
1340 [ + - ]: 6 : if (argc - optidx < 1)
1341 : : goto need_num_arg;
1342 : 6 : errno = 0;
1343 : 6 : l = strtol(argv[optidx], &cp2, base);
1344 [ + - ][ + - ]: 6 : if (cp2 == argv[optidx] || *cp2 != '\0' ||
[ - + ]
1345 [ # # ][ - + ]: 6 : ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1346 : : l < 0) {
1347 : : need_num_arg:
1348 : 0 : error("You must supply a numeric argument "
1349 : : "to the %s command.", cmd);
1350 : 0 : return -1;
1351 : : }
1352 : 6 : *n_arg = l;
1353 [ + - ]: 6 : if (cmdnum == I_LUMASK)
1354 : : break;
1355 : : /* Get pathname (mandatory) */
1356 [ - + ]: 6 : if (argc - optidx < 2) {
1357 : 0 : error("You must specify a path after a %s command.",
1358 : : cmd);
1359 : 0 : return -1;
1360 : : }
1361 : 6 : *path1 = xstrdup(argv[optidx + 1]);
1362 : 6 : break;
1363 : : case I_QUIT:
1364 : : case I_PWD:
1365 : : case I_LPWD:
1366 : : case I_HELP:
1367 : : case I_VERSION:
1368 : : case I_PROGRESS:
1369 [ + - ]: 16 : if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1370 : : return -1;
1371 : : break;
1372 : : default:
1373 : 0 : fatal("Command not implemented");
1374 : : }
1375 : :
1376 : 184 : *cpp = cp;
1377 : 184 : return(cmdnum);
1378 : : }
1379 : :
1380 : : static int
1381 : 190 : parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1382 : : int err_abort)
1383 : : {
1384 : : char *path1, *path2, *tmp;
1385 : 190 : int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1386 : 190 : int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1387 : : int cmdnum, i;
1388 : 190 : unsigned long n_arg = 0;
1389 : : Attrib a, *aa;
1390 : : char path_buf[MAXPATHLEN];
1391 : 190 : int err = 0;
1392 : : glob_t g;
1393 : :
1394 : 190 : path1 = path2 = NULL;
1395 : 190 : cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1396 : : &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1397 [ + + ]: 190 : if (ignore_errors != 0)
1398 : 1 : err_abort = 0;
1399 : :
1400 : : memset(&g, 0, sizeof(g));
1401 : :
1402 : : /* Perform command */
1403 [ + - + + : 190 : switch (cmdnum) {
+ - + + +
+ + + + +
+ + + - +
- + + + +
- - + ]
1404 : : case 0:
1405 : : /* Blank line */
1406 : : break;
1407 : : case -1:
1408 : : /* Unrecognized command */
1409 : 1 : err = -1;
1410 : 1 : break;
1411 : : case I_REGET:
1412 : 0 : aflag = 1;
1413 : : /* FALLTHROUGH */
1414 : : case I_GET:
1415 : 38 : err = process_get(conn, path1, path2, *pwd, pflag,
1416 : : rflag, aflag, fflag);
1417 : 38 : break;
1418 : : case I_PUT:
1419 : 32 : err = process_put(conn, path1, path2, *pwd, pflag,
1420 : : rflag, fflag);
1421 : 32 : break;
1422 : : case I_RENAME:
1423 : 16 : path1 = make_absolute(path1, *pwd);
1424 : 16 : path2 = make_absolute(path2, *pwd);
1425 : 16 : err = do_rename(conn, path1, path2, lflag);
1426 : 16 : break;
1427 : : case I_SYMLINK:
1428 : 0 : sflag = 1;
1429 : : case I_LINK:
1430 [ + + ]: 14 : if (!sflag)
1431 : 7 : path1 = make_absolute(path1, *pwd);
1432 : 14 : path2 = make_absolute(path2, *pwd);
1433 [ + + ]: 14 : err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1434 : 14 : break;
1435 : : case I_RM:
1436 : 8 : path1 = make_absolute(path1, *pwd);
1437 : 8 : remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1438 [ + + ][ + - ]: 14 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1439 [ - + ]: 9 : if (!quiet)
1440 : 0 : printf("Removing %s\n", g.gl_pathv[i]);
1441 : 9 : err = do_rm(conn, g.gl_pathv[i]);
1442 [ + + ]: 9 : if (err != 0 && err_abort)
1443 : : break;
1444 : : }
1445 : : break;
1446 : : case I_MKDIR:
1447 : 7 : path1 = make_absolute(path1, *pwd);
1448 : 7 : attrib_clear(&a);
1449 : 7 : a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1450 : 7 : a.perm = 0777;
1451 : 7 : err = do_mkdir(conn, path1, &a, 1);
1452 : 7 : break;
1453 : : case I_RMDIR:
1454 : 7 : path1 = make_absolute(path1, *pwd);
1455 : 7 : err = do_rmdir(conn, path1);
1456 : 7 : break;
1457 : : case I_CHDIR:
1458 : 3 : path1 = make_absolute(path1, *pwd);
1459 [ + - ]: 3 : if ((tmp = do_realpath(conn, path1)) == NULL) {
1460 : : err = 1;
1461 : : break;
1462 : : }
1463 [ - + ]: 3 : if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1464 : 0 : free(tmp);
1465 : 0 : err = 1;
1466 : 0 : break;
1467 : : }
1468 [ - + ]: 3 : if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1469 : 0 : error("Can't change directory: Can't check target");
1470 : 0 : free(tmp);
1471 : 0 : err = 1;
1472 : 0 : break;
1473 : : }
1474 [ - + ]: 3 : if (!S_ISDIR(aa->perm)) {
1475 : 0 : error("Can't change directory: \"%s\" is not "
1476 : : "a directory", tmp);
1477 : 0 : free(tmp);
1478 : 0 : err = 1;
1479 : 0 : break;
1480 : : }
1481 : 3 : free(*pwd);
1482 : 3 : *pwd = tmp;
1483 : 3 : break;
1484 : : case I_LS:
1485 [ + + ]: 25 : if (!path1) {
1486 : 1 : do_ls_dir(conn, *pwd, *pwd, lflag);
1487 : 1 : break;
1488 : : }
1489 : :
1490 : : /* Strip pwd off beginning of non-absolute paths */
1491 : 24 : tmp = NULL;
1492 [ - + ]: 24 : if (*path1 != '/')
1493 : 0 : tmp = *pwd;
1494 : :
1495 : 24 : path1 = make_absolute(path1, *pwd);
1496 : 24 : err = do_globbed_ls(conn, path1, tmp, lflag);
1497 : 24 : break;
1498 : : case I_DF:
1499 : : /* Default to current directory if no path specified */
1500 [ - + ]: 4 : if (path1 == NULL)
1501 : 0 : path1 = xstrdup(*pwd);
1502 : 4 : path1 = make_absolute(path1, *pwd);
1503 : 4 : err = do_df(conn, path1, hflag, iflag);
1504 : 4 : break;
1505 : : case I_LCHDIR:
1506 [ - + ]: 4 : if (chdir(path1) == -1) {
1507 : 0 : error("Couldn't change local directory to "
1508 : 0 : "\"%s\": %s", path1, strerror(errno));
1509 : 0 : err = 1;
1510 : : }
1511 : : break;
1512 : : case I_LMKDIR:
1513 [ - + ]: 1 : if (mkdir(path1, 0777) == -1) {
1514 : 0 : error("Couldn't create local directory "
1515 : 0 : "\"%s\": %s", path1, strerror(errno));
1516 : 0 : err = 1;
1517 : : }
1518 : : break;
1519 : : case I_LLS:
1520 : 2 : local_do_ls(cmd);
1521 : 2 : break;
1522 : : case I_SHELL:
1523 : 1 : local_do_shell(cmd);
1524 : 1 : break;
1525 : : case I_LUMASK:
1526 : 0 : umask(n_arg);
1527 : 0 : printf("Local umask: %03lo\n", n_arg);
1528 : : break;
1529 : : case I_CHMOD:
1530 : 6 : path1 = make_absolute(path1, *pwd);
1531 : 6 : attrib_clear(&a);
1532 : 6 : a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1533 : 6 : a.perm = n_arg;
1534 : 6 : remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1535 [ + + ][ + - ]: 9 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1536 [ - + ]: 6 : if (!quiet)
1537 : 0 : printf("Changing mode on %s\n", g.gl_pathv[i]);
1538 : 6 : err = do_setstat(conn, g.gl_pathv[i], &a);
1539 [ + + ]: 6 : if (err != 0 && err_abort)
1540 : : break;
1541 : : }
1542 : : break;
1543 : : case I_CHOWN:
1544 : : case I_CHGRP:
1545 : 0 : path1 = make_absolute(path1, *pwd);
1546 : 0 : remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1547 [ # # ][ # # ]: 0 : for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1548 [ # # ]: 0 : if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1549 [ # # ]: 0 : if (err_abort) {
1550 : : err = -1;
1551 : : break;
1552 : : } else
1553 : 0 : continue;
1554 : : }
1555 [ # # ]: 0 : if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1556 : 0 : error("Can't get current ownership of "
1557 : 0 : "remote file \"%s\"", g.gl_pathv[i]);
1558 [ # # ]: 0 : if (err_abort) {
1559 : : err = -1;
1560 : : break;
1561 : : } else
1562 : 0 : continue;
1563 : : }
1564 : 0 : aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1565 [ # # ]: 0 : if (cmdnum == I_CHOWN) {
1566 [ # # ]: 0 : if (!quiet)
1567 : 0 : printf("Changing owner on %s\n",
1568 : 0 : g.gl_pathv[i]);
1569 : 0 : aa->uid = n_arg;
1570 : : } else {
1571 [ # # ]: 0 : if (!quiet)
1572 : 0 : printf("Changing group on %s\n",
1573 : 0 : g.gl_pathv[i]);
1574 : 0 : aa->gid = n_arg;
1575 : : }
1576 : 0 : err = do_setstat(conn, g.gl_pathv[i], aa);
1577 [ # # ]: 0 : if (err != 0 && err_abort)
1578 : : break;
1579 : : }
1580 : : break;
1581 : : case I_PWD:
1582 : 1 : printf("Remote working directory: %s\n", *pwd);
1583 : : break;
1584 : : case I_LPWD:
1585 [ - + ]: 1 : if (!getcwd(path_buf, sizeof(path_buf))) {
1586 : 0 : error("Couldn't get local cwd: %s", strerror(errno));
1587 : 0 : err = -1;
1588 : 0 : break;
1589 : : }
1590 : : printf("Local working directory: %s\n", path_buf);
1591 : : break;
1592 : : case I_QUIT:
1593 : : /* Processed below */
1594 : : break;
1595 : : case I_HELP:
1596 : : help();
1597 : : break;
1598 : : case I_VERSION:
1599 : 12 : printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1600 : : break;
1601 : : case I_PROGRESS:
1602 : 0 : showprogress = !showprogress;
1603 [ # # ]: 0 : if (showprogress)
1604 : : printf("Progress meter enabled\n");
1605 : : else
1606 : : printf("Progress meter disabled\n");
1607 : : break;
1608 : : default:
1609 : 0 : fatal("%d is not implemented", cmdnum);
1610 : : }
1611 : :
1612 [ + + ]: 190 : if (g.gl_pathc)
1613 : 14 : globfree(&g);
1614 : 190 : free(path1);
1615 : 190 : free(path2);
1616 : :
1617 : : /* If an unignored error occurs in batch mode we should abort. */
1618 [ + + ]: 190 : if (err_abort && err != 0)
1619 : : return (-1);
1620 [ + + ]: 149 : else if (cmdnum == I_QUIT)
1621 : : return (1);
1622 : :
1623 : 148 : return (0);
1624 : : }
1625 : :
1626 : : #ifdef USE_LIBEDIT
1627 : : static char *
1628 : : prompt(EditLine *el)
1629 : : {
1630 : : return ("sftp> ");
1631 : : }
1632 : :
1633 : : /* Display entries in 'list' after skipping the first 'len' chars */
1634 : : static void
1635 : : complete_display(char **list, u_int len)
1636 : : {
1637 : : u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1638 : : struct winsize ws;
1639 : : char *tmp;
1640 : :
1641 : : /* Count entries for sort and find longest */
1642 : : for (y = 0; list[y]; y++)
1643 : : m = MAX(m, strlen(list[y]));
1644 : :
1645 : : if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1646 : : width = ws.ws_col;
1647 : :
1648 : : m = m > len ? m - len : 0;
1649 : : columns = width / (m + 2);
1650 : : columns = MAX(columns, 1);
1651 : : colspace = width / columns;
1652 : : colspace = MIN(colspace, width);
1653 : :
1654 : : printf("\n");
1655 : : m = 1;
1656 : : for (y = 0; list[y]; y++) {
1657 : : llen = strlen(list[y]);
1658 : : tmp = llen > len ? list[y] + len : "";
1659 : : printf("%-*s", colspace, tmp);
1660 : : if (m >= columns) {
1661 : : printf("\n");
1662 : : m = 1;
1663 : : } else
1664 : : m++;
1665 : : }
1666 : : printf("\n");
1667 : : }
1668 : :
1669 : : /*
1670 : : * Given a "list" of words that begin with a common prefix of "word",
1671 : : * attempt to find an autocompletion to extends "word" by the next
1672 : : * characters common to all entries in "list".
1673 : : */
1674 : : static char *
1675 : : complete_ambiguous(const char *word, char **list, size_t count)
1676 : : {
1677 : : if (word == NULL)
1678 : : return NULL;
1679 : :
1680 : : if (count > 0) {
1681 : : u_int y, matchlen = strlen(list[0]);
1682 : :
1683 : : /* Find length of common stem */
1684 : : for (y = 1; list[y]; y++) {
1685 : : u_int x;
1686 : :
1687 : : for (x = 0; x < matchlen; x++)
1688 : : if (list[0][x] != list[y][x])
1689 : : break;
1690 : :
1691 : : matchlen = x;
1692 : : }
1693 : :
1694 : : if (matchlen > strlen(word)) {
1695 : : char *tmp = xstrdup(list[0]);
1696 : :
1697 : : tmp[matchlen] = '\0';
1698 : : return tmp;
1699 : : }
1700 : : }
1701 : :
1702 : : return xstrdup(word);
1703 : : }
1704 : :
1705 : : /* Autocomplete a sftp command */
1706 : : static int
1707 : : complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1708 : : int terminated)
1709 : : {
1710 : : u_int y, count = 0, cmdlen, tmplen;
1711 : : char *tmp, **list, argterm[3];
1712 : : const LineInfo *lf;
1713 : :
1714 : : list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1715 : :
1716 : : /* No command specified: display all available commands */
1717 : : if (cmd == NULL) {
1718 : : for (y = 0; cmds[y].c; y++)
1719 : : list[count++] = xstrdup(cmds[y].c);
1720 : :
1721 : : list[count] = NULL;
1722 : : complete_display(list, 0);
1723 : :
1724 : : for (y = 0; list[y] != NULL; y++)
1725 : : free(list[y]);
1726 : : free(list);
1727 : : return count;
1728 : : }
1729 : :
1730 : : /* Prepare subset of commands that start with "cmd" */
1731 : : cmdlen = strlen(cmd);
1732 : : for (y = 0; cmds[y].c; y++) {
1733 : : if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1734 : : list[count++] = xstrdup(cmds[y].c);
1735 : : }
1736 : : list[count] = NULL;
1737 : :
1738 : : if (count == 0) {
1739 : : free(list);
1740 : : return 0;
1741 : : }
1742 : :
1743 : : /* Complete ambigious command */
1744 : : tmp = complete_ambiguous(cmd, list, count);
1745 : : if (count > 1)
1746 : : complete_display(list, 0);
1747 : :
1748 : : for (y = 0; list[y]; y++)
1749 : : free(list[y]);
1750 : : free(list);
1751 : :
1752 : : if (tmp != NULL) {
1753 : : tmplen = strlen(tmp);
1754 : : cmdlen = strlen(cmd);
1755 : : /* If cmd may be extended then do so */
1756 : : if (tmplen > cmdlen)
1757 : : if (el_insertstr(el, tmp + cmdlen) == -1)
1758 : : fatal("el_insertstr failed.");
1759 : : lf = el_line(el);
1760 : : /* Terminate argument cleanly */
1761 : : if (count == 1) {
1762 : : y = 0;
1763 : : if (!terminated)
1764 : : argterm[y++] = quote;
1765 : : if (lastarg || *(lf->cursor) != ' ')
1766 : : argterm[y++] = ' ';
1767 : : argterm[y] = '\0';
1768 : : if (y > 0 && el_insertstr(el, argterm) == -1)
1769 : : fatal("el_insertstr failed.");
1770 : : }
1771 : : free(tmp);
1772 : : }
1773 : :
1774 : : return count;
1775 : : }
1776 : :
1777 : : /*
1778 : : * Determine whether a particular sftp command's arguments (if any)
1779 : : * represent local or remote files.
1780 : : */
1781 : : static int
1782 : : complete_is_remote(char *cmd) {
1783 : : int i;
1784 : :
1785 : : if (cmd == NULL)
1786 : : return -1;
1787 : :
1788 : : for (i = 0; cmds[i].c; i++) {
1789 : : if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1790 : : return cmds[i].t;
1791 : : }
1792 : :
1793 : : return -1;
1794 : : }
1795 : :
1796 : : /* Autocomplete a filename "file" */
1797 : : static int
1798 : : complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1799 : : char *file, int remote, int lastarg, char quote, int terminated)
1800 : : {
1801 : : glob_t g;
1802 : : char *tmp, *tmp2, ins[8];
1803 : : u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1804 : : int clen;
1805 : : const LineInfo *lf;
1806 : :
1807 : : /* Glob from "file" location */
1808 : : if (file == NULL)
1809 : : tmp = xstrdup("*");
1810 : : else
1811 : : xasprintf(&tmp, "%s*", file);
1812 : :
1813 : : /* Check if the path is absolute. */
1814 : : isabs = tmp[0] == '/';
1815 : :
1816 : : memset(&g, 0, sizeof(g));
1817 : : if (remote != LOCAL) {
1818 : : tmp = make_absolute(tmp, remote_path);
1819 : : remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1820 : : } else
1821 : : glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1822 : :
1823 : : /* Determine length of pwd so we can trim completion display */
1824 : : for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1825 : : /* Terminate counting on first unescaped glob metacharacter */
1826 : : if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1827 : : if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1828 : : hadglob = 1;
1829 : : break;
1830 : : }
1831 : : if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1832 : : tmplen++;
1833 : : if (tmp[tmplen] == '/')
1834 : : pwdlen = tmplen + 1; /* track last seen '/' */
1835 : : }
1836 : : free(tmp);
1837 : :
1838 : : if (g.gl_matchc == 0)
1839 : : goto out;
1840 : :
1841 : : if (g.gl_matchc > 1)
1842 : : complete_display(g.gl_pathv, pwdlen);
1843 : :
1844 : : tmp = NULL;
1845 : : /* Don't try to extend globs */
1846 : : if (file == NULL || hadglob)
1847 : : goto out;
1848 : :
1849 : : tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1850 : : tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1851 : : free(tmp2);
1852 : :
1853 : : if (tmp == NULL)
1854 : : goto out;
1855 : :
1856 : : tmplen = strlen(tmp);
1857 : : filelen = strlen(file);
1858 : :
1859 : : /* Count the number of escaped characters in the input string. */
1860 : : cesc = isesc = 0;
1861 : : for (i = 0; i < filelen; i++) {
1862 : : if (!isesc && file[i] == '\\' && i + 1 < filelen){
1863 : : isesc = 1;
1864 : : cesc++;
1865 : : } else
1866 : : isesc = 0;
1867 : : }
1868 : :
1869 : : if (tmplen > (filelen - cesc)) {
1870 : : tmp2 = tmp + filelen - cesc;
1871 : : len = strlen(tmp2);
1872 : : /* quote argument on way out */
1873 : : for (i = 0; i < len; i += clen) {
1874 : : if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1875 : : (size_t)clen > sizeof(ins) - 2)
1876 : : fatal("invalid multibyte character");
1877 : : ins[0] = '\\';
1878 : : memcpy(ins + 1, tmp2 + i, clen);
1879 : : ins[clen + 1] = '\0';
1880 : : switch (tmp2[i]) {
1881 : : case '\'':
1882 : : case '"':
1883 : : case '\\':
1884 : : case '\t':
1885 : : case '[':
1886 : : case ' ':
1887 : : case '#':
1888 : : case '*':
1889 : : if (quote == '\0' || tmp2[i] == quote) {
1890 : : if (el_insertstr(el, ins) == -1)
1891 : : fatal("el_insertstr "
1892 : : "failed.");
1893 : : break;
1894 : : }
1895 : : /* FALLTHROUGH */
1896 : : default:
1897 : : if (el_insertstr(el, ins + 1) == -1)
1898 : : fatal("el_insertstr failed.");
1899 : : break;
1900 : : }
1901 : : }
1902 : : }
1903 : :
1904 : : lf = el_line(el);
1905 : : if (g.gl_matchc == 1) {
1906 : : i = 0;
1907 : : if (!terminated)
1908 : : ins[i++] = quote;
1909 : : if (*(lf->cursor - 1) != '/' &&
1910 : : (lastarg || *(lf->cursor) != ' '))
1911 : : ins[i++] = ' ';
1912 : : ins[i] = '\0';
1913 : : if (i > 0 && el_insertstr(el, ins) == -1)
1914 : : fatal("el_insertstr failed.");
1915 : : }
1916 : : free(tmp);
1917 : :
1918 : : out:
1919 : : globfree(&g);
1920 : : return g.gl_matchc;
1921 : : }
1922 : :
1923 : : /* tab-completion hook function, called via libedit */
1924 : : static unsigned char
1925 : : complete(EditLine *el, int ch)
1926 : : {
1927 : : char **argv, *line, quote;
1928 : : int argc, carg;
1929 : : u_int cursor, len, terminated, ret = CC_ERROR;
1930 : : const LineInfo *lf;
1931 : : struct complete_ctx *complete_ctx;
1932 : :
1933 : : lf = el_line(el);
1934 : : if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1935 : : fatal("%s: el_get failed", __func__);
1936 : :
1937 : : /* Figure out which argument the cursor points to */
1938 : : cursor = lf->cursor - lf->buffer;
1939 : : line = (char *)xmalloc(cursor + 1);
1940 : : memcpy(line, lf->buffer, cursor);
1941 : : line[cursor] = '\0';
1942 : : argv = makeargv(line, &carg, 1, "e, &terminated);
1943 : : free(line);
1944 : :
1945 : : /* Get all the arguments on the line */
1946 : : len = lf->lastchar - lf->buffer;
1947 : : line = (char *)xmalloc(len + 1);
1948 : : memcpy(line, lf->buffer, len);
1949 : : line[len] = '\0';
1950 : : argv = makeargv(line, &argc, 1, NULL, NULL);
1951 : :
1952 : : /* Ensure cursor is at EOL or a argument boundary */
1953 : : if (line[cursor] != ' ' && line[cursor] != '\0' &&
1954 : : line[cursor] != '\n') {
1955 : : free(line);
1956 : : return ret;
1957 : : }
1958 : :
1959 : : if (carg == 0) {
1960 : : /* Show all available commands */
1961 : : complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1962 : : ret = CC_REDISPLAY;
1963 : : } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1964 : : /* Handle the command parsing */
1965 : : if (complete_cmd_parse(el, argv[0], argc == carg,
1966 : : quote, terminated) != 0)
1967 : : ret = CC_REDISPLAY;
1968 : : } else if (carg >= 1) {
1969 : : /* Handle file parsing */
1970 : : int remote = complete_is_remote(argv[0]);
1971 : : char *filematch = NULL;
1972 : :
1973 : : if (carg > 1 && line[cursor-1] != ' ')
1974 : : filematch = argv[carg - 1];
1975 : :
1976 : : if (remote != 0 &&
1977 : : complete_match(el, complete_ctx->conn,
1978 : : *complete_ctx->remote_pathp, filematch,
1979 : : remote, carg == argc, quote, terminated) != 0)
1980 : : ret = CC_REDISPLAY;
1981 : : }
1982 : :
1983 : : free(line);
1984 : : return ret;
1985 : : }
1986 : : #endif /* USE_LIBEDIT */
1987 : :
1988 : : int
1989 : 150 : interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1990 : : {
1991 : : char *remote_path;
1992 : 150 : char *dir = NULL;
1993 : : char cmd[2048];
1994 : : int err, interactive;
1995 : 150 : EditLine *el = NULL;
1996 : : #ifdef USE_LIBEDIT
1997 : : History *hl = NULL;
1998 : : HistEvent hev;
1999 : : extern char *__progname;
2000 : : struct complete_ctx complete_ctx;
2001 : :
2002 : : if (!batchmode && isatty(STDIN_FILENO)) {
2003 : : if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2004 : : fatal("Couldn't initialise editline");
2005 : : if ((hl = history_init()) == NULL)
2006 : : fatal("Couldn't initialise editline history");
2007 : : history(hl, &hev, H_SETSIZE, 100);
2008 : : el_set(el, EL_HIST, history, hl);
2009 : :
2010 : : el_set(el, EL_PROMPT, prompt);
2011 : : el_set(el, EL_EDITOR, "emacs");
2012 : : el_set(el, EL_TERMINAL, NULL);
2013 : : el_set(el, EL_SIGNAL, 1);
2014 : : el_source(el, NULL);
2015 : :
2016 : : /* Tab Completion */
2017 : : el_set(el, EL_ADDFN, "ftp-complete",
2018 : : "Context sensitive argument completion", complete);
2019 : : complete_ctx.conn = conn;
2020 : : complete_ctx.remote_pathp = &remote_path;
2021 : : el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2022 : : el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2023 : : /* enable ctrl-left-arrow and ctrl-right-arrow */
2024 : : el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2025 : : el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2026 : : el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2027 : : el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2028 : : /* make ^w match ksh behaviour */
2029 : : el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2030 : : }
2031 : : #endif /* USE_LIBEDIT */
2032 : :
2033 : 150 : remote_path = do_realpath(conn, ".");
2034 [ - + ]: 150 : if (remote_path == NULL)
2035 : 0 : fatal("Need cwd");
2036 : :
2037 [ - + ]: 150 : if (file1 != NULL) {
2038 : 0 : dir = xstrdup(file1);
2039 : 0 : dir = make_absolute(dir, remote_path);
2040 : :
2041 [ # # ][ # # ]: 0 : if (remote_is_dir(conn, dir) && file2 == NULL) {
2042 [ # # ]: 0 : if (!quiet)
2043 : : printf("Changing to: %s\n", dir);
2044 : : snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2045 [ # # ]: 0 : if (parse_dispatch_command(conn, cmd,
2046 : : &remote_path, 1) != 0) {
2047 : 0 : free(dir);
2048 : 0 : free(remote_path);
2049 : 0 : free(conn);
2050 : 0 : return (-1);
2051 : : }
2052 : : } else {
2053 : : /* XXX this is wrong wrt quoting */
2054 [ # # ][ # # ]: 0 : snprintf(cmd, sizeof cmd, "get%s %s%s%s",
[ # # ]
2055 : 0 : global_aflag ? " -a" : "", dir,
2056 : : file2 == NULL ? "" : " ",
2057 : : file2 == NULL ? "" : file2);
2058 : 0 : err = parse_dispatch_command(conn, cmd,
2059 : : &remote_path, 1);
2060 : 0 : free(dir);
2061 : 0 : free(remote_path);
2062 : 0 : free(conn);
2063 : 0 : return (err);
2064 : : }
2065 : 0 : free(dir);
2066 : : }
2067 : :
2068 : 150 : setlinebuf(stdout);
2069 : 150 : setlinebuf(infile);
2070 : :
2071 [ + + ][ + - ]: 150 : interactive = !batchmode && isatty(STDIN_FILENO);
2072 : 150 : err = 0;
2073 : : for (;;) {
2074 : : char *cp;
2075 : :
2076 : 298 : signal(SIGINT, SIG_IGN);
2077 : :
2078 : : if (el == NULL) {
2079 [ - + ]: 298 : if (interactive)
2080 : : printf("sftp> ");
2081 [ + + ]: 298 : if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2082 [ - + ]: 108 : if (interactive)
2083 : : printf("\n");
2084 : : break;
2085 : : }
2086 [ + - ]: 190 : if (!interactive) { /* Echo command */
2087 : : printf("sftp> %s", cmd);
2088 [ + - ][ + + ]: 190 : if (strlen(cmd) > 0 &&
2089 : 190 : cmd[strlen(cmd) - 1] != '\n')
2090 : : printf("\n");
2091 : : }
2092 : : } else {
2093 : : #ifdef USE_LIBEDIT
2094 : : const char *line;
2095 : : int count = 0;
2096 : :
2097 : : if ((line = el_gets(el, &count)) == NULL ||
2098 : : count <= 0) {
2099 : : printf("\n");
2100 : : break;
2101 : : }
2102 : : history(hl, &hev, H_ENTER, line);
2103 : : if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2104 : : fprintf(stderr, "Error: input line too long\n");
2105 : : continue;
2106 : : }
2107 : : #endif /* USE_LIBEDIT */
2108 : : }
2109 : :
2110 : 190 : cp = strrchr(cmd, '\n');
2111 [ + + ]: 190 : if (cp)
2112 : 175 : *cp = '\0';
2113 : :
2114 : : /* Handle user interrupts gracefully during commands */
2115 : 190 : interrupted = 0;
2116 : 190 : signal(SIGINT, cmd_interrupt);
2117 : :
2118 : 190 : err = parse_dispatch_command(conn, cmd, &remote_path,
2119 : : batchmode);
2120 [ + + ]: 190 : if (err != 0)
2121 : : break;
2122 : : }
2123 : 150 : free(remote_path);
2124 : 150 : free(conn);
2125 : :
2126 : : #ifdef USE_LIBEDIT
2127 : : if (el != NULL)
2128 : : el_end(el);
2129 : : #endif /* USE_LIBEDIT */
2130 : :
2131 : : /* err == 1 signifies normal "quit" exit */
2132 [ + + ]: 150 : return (err >= 0 ? 0 : -1);
2133 : : }
2134 : :
2135 : : static void
2136 : 150 : connect_to_server(char *path, char **args, int *in, int *out)
2137 : : {
2138 : : int c_in, c_out;
2139 : :
2140 : : #ifdef USE_PIPES
2141 : : int pin[2], pout[2];
2142 : :
2143 : : if ((pipe(pin) == -1) || (pipe(pout) == -1))
2144 : : fatal("pipe: %s", strerror(errno));
2145 : : *in = pin[0];
2146 : : *out = pout[1];
2147 : : c_in = pout[0];
2148 : : c_out = pin[1];
2149 : : #else /* USE_PIPES */
2150 : : int inout[2];
2151 : :
2152 [ - + ]: 150 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2153 : 0 : fatal("socketpair: %s", strerror(errno));
2154 : 150 : *in = *out = inout[0];
2155 : 150 : c_in = c_out = inout[1];
2156 : : #endif /* USE_PIPES */
2157 : :
2158 [ - + ]: 150 : if ((sshpid = fork()) == -1)
2159 : 0 : fatal("fork: %s", strerror(errno));
2160 [ + + ]: 300 : else if (sshpid == 0) {
2161 [ + - - + ]: 300 : if ((dup2(c_in, STDIN_FILENO) == -1) ||
2162 : 150 : (dup2(c_out, STDOUT_FILENO) == -1)) {
2163 : 0 : fprintf(stderr, "dup2: %s\n", strerror(errno));
2164 : 0 : _exit(1);
2165 : : }
2166 : 150 : close(*in);
2167 : 150 : close(*out);
2168 : 150 : close(c_in);
2169 : 150 : close(c_out);
2170 : :
2171 : : /*
2172 : : * The underlying ssh is in the same process group, so we must
2173 : : * ignore SIGINT if we want to gracefully abort commands,
2174 : : * otherwise the signal will make it to the ssh process and
2175 : : * kill it too. Contrawise, since sftp sends SIGTERMs to the
2176 : : * underlying ssh, it must *not* ignore that signal.
2177 : : */
2178 : 150 : signal(SIGINT, SIG_IGN);
2179 : 150 : signal(SIGTERM, SIG_DFL);
2180 : 150 : execvp(path, args);
2181 : 150 : fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2182 : 0 : _exit(1);
2183 : : }
2184 : :
2185 : 150 : signal(SIGTERM, killchild);
2186 : 150 : signal(SIGINT, killchild);
2187 : 150 : signal(SIGHUP, killchild);
2188 : 150 : close(c_in);
2189 : 150 : close(c_out);
2190 : 150 : }
2191 : :
2192 : : static void
2193 : 0 : usage(void)
2194 : : {
2195 : : extern char *__progname;
2196 : :
2197 : 0 : fprintf(stderr,
2198 : : "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2199 : : " [-D sftp_server_path] [-F ssh_config] "
2200 : : "[-i identity_file] [-l limit]\n"
2201 : : " [-o ssh_option] [-P port] [-R num_requests] "
2202 : : "[-S program]\n"
2203 : : " [-s subsystem | sftp_server] host\n"
2204 : : " %s [user@]host[:file ...]\n"
2205 : : " %s [user@]host[:dir[/]]\n"
2206 : : " %s -b batchfile [user@]host\n",
2207 : : __progname, __progname, __progname, __progname);
2208 : 0 : exit(1);
2209 : : }
2210 : :
2211 : : int
2212 : 150 : main(int argc, char **argv)
2213 : : {
2214 : : int in, out, ch, err;
2215 : 150 : char *host = NULL, *userhost, *cp, *file2 = NULL;
2216 : 150 : int debug_level = 0, sshver = 2;
2217 : 150 : char *file1 = NULL, *sftp_server = NULL;
2218 : 150 : char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2219 : : const char *errstr;
2220 : 150 : LogLevel ll = SYSLOG_LEVEL_INFO;
2221 : : arglist args;
2222 : : extern int optind;
2223 : : extern char *optarg;
2224 : : struct sftp_conn *conn;
2225 : 150 : size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2226 : 150 : size_t num_requests = DEFAULT_NUM_REQUESTS;
2227 : 150 : long long limit_kbps = 0;
2228 : :
2229 : : /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2230 : 150 : sanitise_stdfd();
2231 : 150 : setlocale(LC_CTYPE, "");
2232 : :
2233 : 150 : __progname = ssh_get_progname(argv[0]);
2234 : : memset(&args, '\0', sizeof(args));
2235 : : args.list = NULL;
2236 : 150 : addargs(&args, "%s", ssh_program);
2237 : 150 : addargs(&args, "-oForwardX11 no");
2238 : 150 : addargs(&args, "-oForwardAgent no");
2239 : 150 : addargs(&args, "-oPermitLocalCommand no");
2240 : 150 : addargs(&args, "-oClearAllForwardings yes");
2241 : :
2242 : 150 : ll = SYSLOG_LEVEL_INFO;
2243 : 150 : infile = stdin;
2244 : :
2245 [ + + ]: 669 : while ((ch = getopt(argc, argv,
2246 : : "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2247 [ - + - - : 519 : switch (ch) {
+ - - + +
- - + - -
+ - + -
- ]
2248 : : /* Passed through to ssh(1) */
2249 : : case '4':
2250 : : case '6':
2251 : : case 'C':
2252 : 0 : addargs(&args, "-%c", ch);
2253 : 519 : break;
2254 : : /* Passed through to ssh(1) with argument */
2255 : : case 'F':
2256 : : case 'c':
2257 : : case 'i':
2258 : : case 'o':
2259 : 2 : addargs(&args, "-%c", ch);
2260 : 2 : addargs(&args, "%s", optarg);
2261 : 2 : break;
2262 : : case 'q':
2263 : 0 : ll = SYSLOG_LEVEL_ERROR;
2264 : 0 : quiet = 1;
2265 : 0 : showprogress = 0;
2266 : 0 : addargs(&args, "-%c", ch);
2267 : 0 : break;
2268 : : case 'P':
2269 : 0 : addargs(&args, "-oPort %s", optarg);
2270 : 0 : break;
2271 : : case 'v':
2272 [ + - ]: 234 : if (debug_level < 3) {
2273 : 234 : addargs(&args, "-v");
2274 : 234 : ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2275 : : }
2276 : 234 : debug_level++;
2277 : 234 : break;
2278 : : case '1':
2279 : 0 : sshver = 1;
2280 [ # # ]: 0 : if (sftp_server == NULL)
2281 : 0 : sftp_server = _PATH_SFTP_SERVER;
2282 : : break;
2283 : : case '2':
2284 : : sshver = 2;
2285 : : break;
2286 : : case 'a':
2287 : 0 : global_aflag = 1;
2288 : 0 : break;
2289 : : case 'B':
2290 : 12 : copy_buffer_len = strtol(optarg, &cp, 10);
2291 [ + - ][ - + ]: 12 : if (copy_buffer_len == 0 || *cp != '\0')
2292 : 0 : fatal("Invalid buffer size \"%s\"", optarg);
2293 : : break;
2294 : : case 'b':
2295 [ - + ]: 109 : if (batchmode)
2296 : 0 : fatal("Batch file already specified.");
2297 : :
2298 : : /* Allow "-" as stdin */
2299 [ + + ]: 125 : if (strcmp(optarg, "-") != 0 &&
[ + + - + ]
2300 : 16 : (infile = fopen(optarg, "r")) == NULL)
2301 : 0 : fatal("%s (%s).", strerror(errno), optarg);
2302 : 109 : showprogress = 0;
2303 : 109 : quiet = batchmode = 1;
2304 : 109 : addargs(&args, "-obatchmode yes");
2305 : 109 : break;
2306 : : case 'f':
2307 : 0 : global_fflag = 1;
2308 : 0 : break;
2309 : : case 'p':
2310 : 0 : global_pflag = 1;
2311 : 0 : break;
2312 : : case 'D':
2313 : 149 : sftp_direct = optarg;
2314 : 149 : break;
2315 : : case 'l':
2316 : 0 : limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2317 : : &errstr);
2318 [ # # ]: 0 : if (errstr != NULL)
2319 : 0 : usage();
2320 : 0 : limit_kbps *= 1024; /* kbps */
2321 : 0 : break;
2322 : : case 'r':
2323 : 0 : global_rflag = 1;
2324 : 0 : break;
2325 : : case 'R':
2326 : 12 : num_requests = strtol(optarg, &cp, 10);
2327 [ + - ][ - + ]: 12 : if (num_requests == 0 || *cp != '\0')
2328 : 0 : fatal("Invalid number of requests \"%s\"",
2329 : : optarg);
2330 : : break;
2331 : : case 's':
2332 : 0 : sftp_server = optarg;
2333 : 0 : break;
2334 : : case 'S':
2335 : 1 : ssh_program = optarg;
2336 : 1 : replacearg(&args, 0, "%s", ssh_program);
2337 : 1 : break;
2338 : : case 'h':
2339 : : default:
2340 : 0 : usage();
2341 : : }
2342 : : }
2343 : :
2344 [ + - ]: 150 : if (!isatty(STDERR_FILENO))
2345 : 150 : showprogress = 0;
2346 : :
2347 : 150 : log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2348 : :
2349 [ + + ]: 150 : if (sftp_direct == NULL) {
2350 [ + - ][ - + ]: 1 : if (optind == argc || argc > (optind + 2))
2351 : 0 : usage();
2352 : :
2353 : 1 : userhost = xstrdup(argv[optind]);
2354 : 1 : file2 = argv[optind+1];
2355 : :
2356 [ - + ]: 1 : if ((host = strrchr(userhost, '@')) == NULL)
2357 : : host = userhost;
2358 : : else {
2359 : 0 : *host++ = '\0';
2360 [ # # ]: 0 : if (!userhost[0]) {
2361 : 0 : fprintf(stderr, "Missing username\n");
2362 : 0 : usage();
2363 : : }
2364 : 0 : addargs(&args, "-l");
2365 : 0 : addargs(&args, "%s", userhost);
2366 : : }
2367 : :
2368 [ - + ]: 1 : if ((cp = colon(host)) != NULL) {
2369 : 0 : *cp++ = '\0';
2370 : 0 : file1 = cp;
2371 : : }
2372 : :
2373 : 1 : host = cleanhostname(host);
2374 [ - + ]: 1 : if (!*host) {
2375 : 0 : fprintf(stderr, "Missing hostname\n");
2376 : 0 : usage();
2377 : : }
2378 : :
2379 : 1 : addargs(&args, "-oProtocol %d", sshver);
2380 : :
2381 : : /* no subsystem if the server-spec contains a '/' */
2382 [ - + ][ # # ]: 1 : if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2383 : 1 : addargs(&args, "-s");
2384 : :
2385 : 1 : addargs(&args, "--");
2386 : 1 : addargs(&args, "%s", host);
2387 [ - + ]: 1 : addargs(&args, "%s", (sftp_server != NULL ?
2388 : : sftp_server : "sftp"));
2389 : :
2390 : 1 : connect_to_server(ssh_program, args.list, &in, &out);
2391 : : } else {
2392 : 149 : args.list = NULL;
2393 : 149 : addargs(&args, "sftp-server");
2394 : :
2395 : 149 : connect_to_server(sftp_direct, args.list, &in, &out);
2396 : : }
2397 : 150 : freeargs(&args);
2398 : :
2399 : 150 : conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2400 [ - + ]: 150 : if (conn == NULL)
2401 : 0 : fatal("Couldn't initialise connection to server");
2402 : :
2403 [ + + ]: 150 : if (!quiet) {
2404 [ + + ]: 41 : if (sftp_direct == NULL)
2405 : 1 : fprintf(stderr, "Connected to %s.\n", host);
2406 : : else
2407 : 40 : fprintf(stderr, "Attached to %s.\n", sftp_direct);
2408 : : }
2409 : :
2410 : 150 : err = interactive_loop(conn, file1, file2);
2411 : :
2412 : : #if !defined(USE_PIPES)
2413 : 150 : shutdown(in, SHUT_RDWR);
2414 : 150 : shutdown(out, SHUT_RDWR);
2415 : : #endif
2416 : :
2417 : 150 : close(in);
2418 : 150 : close(out);
2419 [ + + ]: 150 : if (batchmode)
2420 : 109 : fclose(infile);
2421 : :
2422 [ - + ]: 150 : while (waitpid(sshpid, NULL, 0) == -1)
2423 [ # # ]: 0 : if (errno != EINTR)
2424 : 150 : fatal("Couldn't wait for ssh process: %s",
2425 : : strerror(errno));
2426 : :
2427 : 150 : exit(err == 0 ? 0 : 1);
2428 : : }
|