Bug Summary

File:src/usr.bin/ssh/sftp/../sftp.c
Warning:line 2043, column 2
Value stored to 'argv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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