blob: 0f3f89d332ac24c47efcfc13aa097a28dd241354 [file] [log] [blame]
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001/* $OpenBSD: sftp.c,v 1.189 2019/01/16 23:23:45 djm Exp $ */
Damien Miller33804262001-02-04 23:20:18 +11002/*
Damien Miller4e60ed72004-02-17 17:07:59 +11003 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
Damien Miller33804262001-02-04 23:20:18 +11004 *
Damien Miller4e60ed72004-02-17 17:07:59 +11005 * 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.
Damien Miller33804262001-02-04 23:20:18 +11008 *
Damien Miller4e60ed72004-02-17 17:07:59 +11009 * 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.
Damien Miller33804262001-02-04 23:20:18 +110016 */
17
18#include "includes.h"
19
Damien Miller9cf6d072006-03-15 11:29:24 +110020#include <sys/types.h>
Damien Millerd7834352006-08-05 12:39:39 +100021#include <sys/ioctl.h>
Damien Millerf17883e2006-03-15 11:45:54 +110022#ifdef HAVE_SYS_STAT_H
23# include <sys/stat.h>
24#endif
Damien Miller8dbffe72006-08-05 11:02:17 +100025#include <sys/param.h>
Damien Millere3b60b52006-07-10 21:08:03 +100026#include <sys/socket.h>
Damien Miller9cf6d072006-03-15 11:29:24 +110027#include <sys/wait.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100028#ifdef HAVE_SYS_STATVFS_H
Damien Millerd671e5a2008-05-19 14:53:33 +100029#include <sys/statvfs.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100030#endif
Darren Tucker2d963d82004-11-07 20:04:10 +110031
Damien Miller1cbc2922007-10-26 14:27:45 +100032#include <ctype.h>
Darren Tucker39972492006-07-12 22:22:46 +100033#include <errno.h>
34
Damien Miller03e20032006-03-15 11:16:59 +110035#ifdef HAVE_PATHS_H
Damien Millera9263d02006-03-15 11:18:26 +110036# include <paths.h>
Damien Miller03e20032006-03-15 11:16:59 +110037#endif
Darren Tucker1b0dd172009-10-07 08:37:48 +110038#ifdef HAVE_LIBGEN_H
39#include <libgen.h>
40#endif
Darren Tuckerae133d42013-06-06 08:30:20 +100041#ifdef HAVE_LOCALE_H
42# include <locale.h>
43#endif
Darren Tucker2d963d82004-11-07 20:04:10 +110044#ifdef USE_LIBEDIT
45#include <histedit.h>
46#else
47typedef void EditLine;
48#endif
deraadt@openbsd.org087266e2015-01-20 23:14:00 +000049#include <limits.h>
Damien Miller6ff3cad2006-03-15 11:52:09 +110050#include <signal.h>
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +000051#include <stdarg.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100052#include <stdlib.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100053#include <stdio.h>
Damien Millere3476ed2006-07-24 14:13:33 +100054#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100055#include <unistd.h>
Damien Millerd7834352006-08-05 12:39:39 +100056#include <stdarg.h>
Damien Miller33804262001-02-04 23:20:18 +110057
Damien Millera7058ec2008-05-20 08:57:06 +100058#ifdef HAVE_UTIL_H
59# include <util.h>
60#endif
61
Damien Miller33804262001-02-04 23:20:18 +110062#include "xmalloc.h"
63#include "log.h"
64#include "pathnames.h"
Ben Lindstrom4529b702001-05-03 23:39:53 +000065#include "misc.h"
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +000066#include "utf8.h"
Damien Miller33804262001-02-04 23:20:18 +110067
68#include "sftp.h"
djm@openbsd.org7d845f42015-01-14 13:54:13 +000069#include "ssherr.h"
70#include "sshbuf.h"
Damien Miller33804262001-02-04 23:20:18 +110071#include "sftp-common.h"
72#include "sftp-client.h"
Damien Millerd7d46bb2004-02-18 14:11:13 +110073
Darren Tucker21063192010-01-08 17:10:36 +110074#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
75#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
76
Damien Miller20e1fab2004-02-18 14:30:55 +110077/* File to read commands from */
78FILE* infile;
79
80/* Are we in batchfile mode? */
81int batchmode = 0;
82
Damien Miller20e1fab2004-02-18 14:30:55 +110083/* PID of ssh transport process */
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +000084static volatile pid_t sshpid = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +110085
Damien Miller9303e652013-04-23 15:22:40 +100086/* Suppress diagnositic messages */
87int quiet = 0;
88
Damien Miller20e1fab2004-02-18 14:30:55 +110089/* This is set to 0 if the progressmeter is not desired. */
Damien Millerc0f27d82004-03-08 23:12:19 +110090int showprogress = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +110091
Darren Tucker1b0dd172009-10-07 08:37:48 +110092/* When this option is set, we always recursively download/upload directories */
93int global_rflag = 0;
94
Damien Millerd8accc02014-05-15 13:46:25 +100095/* When this option is set, we resume download or upload if possible */
Damien Miller0d032412013-07-25 11:56:52 +100096int global_aflag = 0;
97
Darren Tucker1b0dd172009-10-07 08:37:48 +110098/* When this option is set, the file transfers will always preserve times */
99int global_pflag = 0;
100
Damien Millerf29238e2013-10-17 11:48:52 +1100101/* When this option is set, transfers will have fsync() called on each file */
102int global_fflag = 0;
103
Darren Tuckercdf547a2004-05-24 10:12:19 +1000104/* SIGINT received during command processing */
105volatile sig_atomic_t interrupted = 0;
106
Darren Tuckerb9123452004-06-22 13:06:45 +1000107/* I wish qsort() took a separate ctx for the comparison function...*/
108int sort_flag;
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000109glob_t *sort_glob;
Darren Tuckerb9123452004-06-22 13:06:45 +1000110
Darren Tucker909d8582010-01-08 19:02:40 +1100111/* Context used for commandline completion */
112struct complete_ctx {
113 struct sftp_conn *conn;
114 char **remote_pathp;
115};
116
Damien Miller20e1fab2004-02-18 14:30:55 +1100117int remote_glob(struct sftp_conn *, const char *, int,
118 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
Damien Miller33804262001-02-04 23:20:18 +1100119
Kevin Steves12888d12001-03-05 19:50:57 +0000120extern char *__progname;
Kevin Steves12888d12001-03-05 19:50:57 +0000121
Damien Miller20e1fab2004-02-18 14:30:55 +1100122/* Separators for interactive commands */
123#define WHITESPACE " \t\r\n"
Damien Millerd7686fd2001-02-10 00:40:03 +1100124
Darren Tuckerb9123452004-06-22 13:06:45 +1000125/* ls flags */
Darren Tucker2901e2d2010-01-13 22:44:06 +1100126#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
127#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
128#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
129#define LS_NAME_SORT 0x0008 /* Sort by name (default) */
130#define LS_TIME_SORT 0x0010 /* Sort by mtime */
131#define LS_SIZE_SORT 0x0020 /* Sort by file size */
132#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
133#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
134#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
Darren Tuckerb9123452004-06-22 13:06:45 +1000135
Darren Tucker2901e2d2010-01-13 22:44:06 +1100136#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000137#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
Damien Miller20e1fab2004-02-18 14:30:55 +1100138
139/* Commands for interactive mode */
Damien Miller02e87802013-08-21 02:38:51 +1000140enum sftp_command {
141 I_CHDIR = 1,
142 I_CHGRP,
143 I_CHMOD,
144 I_CHOWN,
145 I_DF,
146 I_GET,
147 I_HELP,
148 I_LCHDIR,
149 I_LINK,
150 I_LLS,
151 I_LMKDIR,
152 I_LPWD,
153 I_LS,
154 I_LUMASK,
155 I_MKDIR,
156 I_PUT,
157 I_PWD,
158 I_QUIT,
Damien Millerb15cd7b2014-05-15 13:46:52 +1000159 I_REGET,
Damien Miller02e87802013-08-21 02:38:51 +1000160 I_RENAME,
Damien Millerb15cd7b2014-05-15 13:46:52 +1000161 I_REPUT,
Damien Miller02e87802013-08-21 02:38:51 +1000162 I_RM,
163 I_RMDIR,
164 I_SHELL,
165 I_SYMLINK,
166 I_VERSION,
167 I_PROGRESS,
Damien Miller02e87802013-08-21 02:38:51 +1000168};
Damien Miller20e1fab2004-02-18 14:30:55 +1100169
170struct CMD {
171 const char *c;
172 const int n;
Darren Tucker909d8582010-01-08 19:02:40 +1100173 const int t;
Damien Miller20e1fab2004-02-18 14:30:55 +1100174};
175
Darren Tucker909d8582010-01-08 19:02:40 +1100176/* Type of completion */
177#define NOARGS 0
178#define REMOTE 1
179#define LOCAL 2
180
Damien Miller20e1fab2004-02-18 14:30:55 +1100181static const struct CMD cmds[] = {
Darren Tucker909d8582010-01-08 19:02:40 +1100182 { "bye", I_QUIT, NOARGS },
183 { "cd", I_CHDIR, REMOTE },
184 { "chdir", I_CHDIR, REMOTE },
185 { "chgrp", I_CHGRP, REMOTE },
186 { "chmod", I_CHMOD, REMOTE },
187 { "chown", I_CHOWN, REMOTE },
188 { "df", I_DF, REMOTE },
189 { "dir", I_LS, REMOTE },
190 { "exit", I_QUIT, NOARGS },
191 { "get", I_GET, REMOTE },
192 { "help", I_HELP, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL },
194 { "lchdir", I_LCHDIR, LOCAL },
195 { "lls", I_LLS, LOCAL },
196 { "lmkdir", I_LMKDIR, LOCAL },
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100197 { "ln", I_LINK, REMOTE },
Darren Tucker909d8582010-01-08 19:02:40 +1100198 { "lpwd", I_LPWD, LOCAL },
199 { "ls", I_LS, REMOTE },
200 { "lumask", I_LUMASK, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE },
Damien Miller099fc162010-05-10 11:56:50 +1000202 { "mget", I_GET, REMOTE },
203 { "mput", I_PUT, LOCAL },
Darren Tucker909d8582010-01-08 19:02:40 +1100204 { "progress", I_PROGRESS, NOARGS },
205 { "put", I_PUT, LOCAL },
206 { "pwd", I_PWD, REMOTE },
207 { "quit", I_QUIT, NOARGS },
Damien Miller0d032412013-07-25 11:56:52 +1000208 { "reget", I_REGET, REMOTE },
Darren Tucker909d8582010-01-08 19:02:40 +1100209 { "rename", I_RENAME, REMOTE },
djm@openbsd.org4a459222014-10-06 00:47:15 +0000210 { "reput", I_REPUT, LOCAL },
Darren Tucker909d8582010-01-08 19:02:40 +1100211 { "rm", I_RM, REMOTE },
212 { "rmdir", I_RMDIR, REMOTE },
213 { "symlink", I_SYMLINK, REMOTE },
214 { "version", I_VERSION, NOARGS },
215 { "!", I_SHELL, NOARGS },
216 { "?", I_HELP, NOARGS },
217 { NULL, -1, -1 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100218};
219
Damien Millerb6c85fc2007-01-05 16:30:41 +1100220/* ARGSUSED */
Damien Miller20e1fab2004-02-18 14:30:55 +1100221static void
Darren Tuckercdf547a2004-05-24 10:12:19 +1000222killchild(int signo)
223{
Darren Tuckerba66df82005-01-24 21:57:40 +1100224 if (sshpid > 1) {
Darren Tuckercdf547a2004-05-24 10:12:19 +1000225 kill(sshpid, SIGTERM);
Darren Tuckerba66df82005-01-24 21:57:40 +1100226 waitpid(sshpid, NULL, 0);
227 }
Darren Tuckercdf547a2004-05-24 10:12:19 +1000228
229 _exit(1);
230}
231
Damien Millerb6c85fc2007-01-05 16:30:41 +1100232/* ARGSUSED */
Darren Tuckercdf547a2004-05-24 10:12:19 +1000233static void
millert@openbsd.org2c6697c2016-10-18 12:41:22 +0000234suspchild(int signo)
235{
236 if (sshpid > 1) {
237 kill(sshpid, signo);
238 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
239 continue;
240 }
241 kill(getpid(), SIGSTOP);
242}
243
244/* ARGSUSED */
245static void
Darren Tuckercdf547a2004-05-24 10:12:19 +1000246cmd_interrupt(int signo)
247{
248 const char msg[] = "\rInterrupt \n";
Darren Tuckere2f189a2004-12-06 22:45:53 +1100249 int olderrno = errno;
Darren Tuckercdf547a2004-05-24 10:12:19 +1000250
Darren Tuckerdbee3082013-05-16 20:32:29 +1000251 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
Darren Tuckercdf547a2004-05-24 10:12:19 +1000252 interrupted = 1;
Darren Tuckere2f189a2004-12-06 22:45:53 +1100253 errno = olderrno;
Darren Tuckercdf547a2004-05-24 10:12:19 +1000254}
255
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000256/*ARGSUSED*/
257static void
258sigchld_handler(int sig)
259{
260 int save_errno = errno;
261 pid_t pid;
262 const char msg[] = "\rConnection closed. \n";
263
264 /* Report if ssh transport process dies. */
265 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
266 continue;
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +0000267 if (pid == sshpid) {
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000268 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +0000269 sshpid = -1;
270 }
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000271
272 errno = save_errno;
273}
274
Darren Tuckercdf547a2004-05-24 10:12:19 +1000275static void
Damien Miller20e1fab2004-02-18 14:30:55 +1100276help(void)
277{
Damien Miller62fd18a2009-01-28 16:14:09 +1100278 printf("Available commands:\n"
279 "bye Quit sftp\n"
280 "cd path Change remote directory to 'path'\n"
djm@openbsd.org60d8c842019-01-16 23:23:45 +0000281 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
282 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
283 "chown [-h] own path Change owner of file 'path' to 'own'\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100284 "df [-hi] [path] Display statistics for current directory or\n"
285 " filesystem containing 'path'\n"
286 "exit Quit sftp\n"
djm@openbsd.org4a459222014-10-06 00:47:15 +0000287 "get [-afPpRr] remote [local] Download file\n"
288 "reget [-fPpRr] remote [local] Resume download file\n"
289 "reput [-fPpRr] [local] remote Resume upload file\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100290 "help Display this help text\n"
291 "lcd path Change local directory to 'path'\n"
292 "lls [ls-options [path]] Display local directory listing\n"
293 "lmkdir path Create local directory\n"
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100294 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100295 "lpwd Print local working directory\n"
Darren Tucker75fe6262010-01-15 11:42:51 +1100296 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100297 "lumask umask Set local umask to 'umask'\n"
298 "mkdir path Create remote directory\n"
299 "progress Toggle display of progress meter\n"
djm@openbsd.org4a459222014-10-06 00:47:15 +0000300 "put [-afPpRr] local [remote] Upload file\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100301 "pwd Display remote working directory\n"
302 "quit Quit sftp\n"
303 "rename oldpath newpath Rename remote file\n"
304 "rm path Delete remote file\n"
305 "rmdir path Remove remote directory\n"
306 "symlink oldpath newpath Symlink remote file\n"
307 "version Show SFTP version\n"
308 "!command Execute 'command' in local shell\n"
309 "! Escape to local shell\n"
310 "? Synonym for help\n");
Damien Miller20e1fab2004-02-18 14:30:55 +1100311}
312
313static void
314local_do_shell(const char *args)
315{
316 int status;
317 char *shell;
318 pid_t pid;
319
320 if (!*args)
321 args = NULL;
322
Damien Miller38d9a962010-10-07 22:07:11 +1100323 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
Damien Miller20e1fab2004-02-18 14:30:55 +1100324 shell = _PATH_BSHELL;
325
326 if ((pid = fork()) == -1)
327 fatal("Couldn't fork: %s", strerror(errno));
328
329 if (pid == 0) {
330 /* XXX: child has pipe fds to ssh subproc open - issue? */
331 if (args) {
332 debug3("Executing %s -c \"%s\"", shell, args);
333 execl(shell, shell, "-c", args, (char *)NULL);
334 } else {
335 debug3("Executing %s", shell);
336 execl(shell, shell, (char *)NULL);
337 }
338 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
339 strerror(errno));
340 _exit(1);
341 }
342 while (waitpid(pid, &status, 0) == -1)
343 if (errno != EINTR)
344 fatal("Couldn't wait for child: %s", strerror(errno));
345 if (!WIFEXITED(status))
Damien Miller55b04f12006-03-26 14:23:17 +1100346 error("Shell exited abnormally");
Damien Miller20e1fab2004-02-18 14:30:55 +1100347 else if (WEXITSTATUS(status))
348 error("Shell exited with status %d", WEXITSTATUS(status));
349}
350
351static void
352local_do_ls(const char *args)
353{
354 if (!args || !*args)
355 local_do_shell(_PATH_LS);
356 else {
357 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
358 char *buf = xmalloc(len);
359
360 /* XXX: quoting - rip quoting code from ftp? */
361 snprintf(buf, len, _PATH_LS " %s", args);
362 local_do_shell(buf);
Darren Tuckera627d422013-06-02 07:31:17 +1000363 free(buf);
Damien Miller20e1fab2004-02-18 14:30:55 +1100364 }
365}
366
367/* Strip one path (usually the pwd) from the start of another */
368static char *
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000369path_strip(const char *path, const char *strip)
Damien Miller20e1fab2004-02-18 14:30:55 +1100370{
371 size_t len;
372
373 if (strip == NULL)
374 return (xstrdup(path));
375
376 len = strlen(strip);
Darren Tuckere2f189a2004-12-06 22:45:53 +1100377 if (strncmp(path, strip, len) == 0) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100378 if (strip[len - 1] != '/' && path[len] == '/')
379 len++;
380 return (xstrdup(path + len));
381 }
382
383 return (xstrdup(path));
384}
385
386static char *
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000387make_absolute(char *p, const char *pwd)
Damien Miller20e1fab2004-02-18 14:30:55 +1100388{
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000389 char *abs_str;
Damien Miller20e1fab2004-02-18 14:30:55 +1100390
391 /* Derelativise */
djm@openbsd.org2a358622018-11-16 03:26:01 +0000392 if (p && !path_absolute(p)) {
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000393 abs_str = path_append(pwd, p);
Darren Tuckera627d422013-06-02 07:31:17 +1000394 free(p);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000395 return(abs_str);
Damien Miller20e1fab2004-02-18 14:30:55 +1100396 } else
397 return(p);
398}
399
400static int
Damien Miller0d032412013-07-25 11:56:52 +1000401parse_getput_flags(const char *cmd, char **argv, int argc,
Damien Millerf29238e2013-10-17 11:48:52 +1100402 int *aflag, int *fflag, int *pflag, int *rflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100403{
Damien Millerf184bcf2008-06-29 22:45:13 +1000404 extern int opterr, optind, optopt, optreset;
Damien Miller1cbc2922007-10-26 14:27:45 +1000405 int ch;
Damien Miller20e1fab2004-02-18 14:30:55 +1100406
Damien Miller1cbc2922007-10-26 14:27:45 +1000407 optind = optreset = 1;
408 opterr = 0;
409
Damien Millerf29238e2013-10-17 11:48:52 +1100410 *aflag = *fflag = *rflag = *pflag = 0;
411 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
Damien Miller1cbc2922007-10-26 14:27:45 +1000412 switch (ch) {
Damien Miller0d032412013-07-25 11:56:52 +1000413 case 'a':
414 *aflag = 1;
415 break;
Damien Millerf29238e2013-10-17 11:48:52 +1100416 case 'f':
417 *fflag = 1;
418 break;
Damien Miller20e1fab2004-02-18 14:30:55 +1100419 case 'p':
420 case 'P':
421 *pflag = 1;
422 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100423 case 'r':
424 case 'R':
425 *rflag = 1;
426 break;
Damien Miller20e1fab2004-02-18 14:30:55 +1100427 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000428 error("%s: Invalid flag -%c", cmd, optopt);
Damien Miller1cbc2922007-10-26 14:27:45 +1000429 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100430 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100431 }
432
Damien Miller1cbc2922007-10-26 14:27:45 +1000433 return optind;
Damien Miller20e1fab2004-02-18 14:30:55 +1100434}
435
436static int
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100437parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
438{
439 extern int opterr, optind, optopt, optreset;
440 int ch;
441
442 optind = optreset = 1;
443 opterr = 0;
444
445 *sflag = 0;
446 while ((ch = getopt(argc, argv, "s")) != -1) {
447 switch (ch) {
448 case 's':
449 *sflag = 1;
450 break;
451 default:
452 error("%s: Invalid flag -%c", cmd, optopt);
453 return -1;
454 }
455 }
456
457 return optind;
458}
459
460static int
Damien Millerc7dba122013-08-21 02:41:15 +1000461parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
462{
463 extern int opterr, optind, optopt, optreset;
464 int ch;
465
466 optind = optreset = 1;
467 opterr = 0;
468
469 *lflag = 0;
470 while ((ch = getopt(argc, argv, "l")) != -1) {
471 switch (ch) {
472 case 'l':
473 *lflag = 1;
474 break;
475 default:
476 error("%s: Invalid flag -%c", cmd, optopt);
477 return -1;
478 }
479 }
480
481 return optind;
482}
483
484static int
Damien Miller1cbc2922007-10-26 14:27:45 +1000485parse_ls_flags(char **argv, int argc, int *lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100486{
Damien Millerf184bcf2008-06-29 22:45:13 +1000487 extern int opterr, optind, optopt, optreset;
Damien Miller1cbc2922007-10-26 14:27:45 +1000488 int ch;
Damien Miller20e1fab2004-02-18 14:30:55 +1100489
Damien Miller1cbc2922007-10-26 14:27:45 +1000490 optind = optreset = 1;
491 opterr = 0;
492
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000493 *lflag = LS_NAME_SORT;
Darren Tucker2901e2d2010-01-13 22:44:06 +1100494 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
Damien Miller1cbc2922007-10-26 14:27:45 +1000495 switch (ch) {
496 case '1':
497 *lflag &= ~VIEW_FLAGS;
498 *lflag |= LS_SHORT_VIEW;
499 break;
500 case 'S':
501 *lflag &= ~SORT_FLAGS;
502 *lflag |= LS_SIZE_SORT;
503 break;
504 case 'a':
505 *lflag |= LS_SHOW_ALL;
506 break;
507 case 'f':
508 *lflag &= ~SORT_FLAGS;
509 break;
Darren Tucker2901e2d2010-01-13 22:44:06 +1100510 case 'h':
511 *lflag |= LS_SI_UNITS;
512 break;
Damien Miller1cbc2922007-10-26 14:27:45 +1000513 case 'l':
Darren Tucker2901e2d2010-01-13 22:44:06 +1100514 *lflag &= ~LS_SHORT_VIEW;
Damien Miller1cbc2922007-10-26 14:27:45 +1000515 *lflag |= LS_LONG_VIEW;
516 break;
517 case 'n':
Darren Tucker2901e2d2010-01-13 22:44:06 +1100518 *lflag &= ~LS_SHORT_VIEW;
Damien Miller1cbc2922007-10-26 14:27:45 +1000519 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
520 break;
521 case 'r':
522 *lflag |= LS_REVERSE_SORT;
523 break;
524 case 't':
525 *lflag &= ~SORT_FLAGS;
526 *lflag |= LS_TIME_SORT;
527 break;
528 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000529 error("ls: Invalid flag -%c", optopt);
Damien Miller1cbc2922007-10-26 14:27:45 +1000530 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100531 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100532 }
533
Damien Miller1cbc2922007-10-26 14:27:45 +1000534 return optind;
Damien Miller20e1fab2004-02-18 14:30:55 +1100535}
536
537static int
Damien Millerd671e5a2008-05-19 14:53:33 +1000538parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
539{
Damien Millerf184bcf2008-06-29 22:45:13 +1000540 extern int opterr, optind, optopt, optreset;
Damien Millerd671e5a2008-05-19 14:53:33 +1000541 int ch;
542
543 optind = optreset = 1;
544 opterr = 0;
545
546 *hflag = *iflag = 0;
547 while ((ch = getopt(argc, argv, "hi")) != -1) {
548 switch (ch) {
549 case 'h':
550 *hflag = 1;
551 break;
552 case 'i':
553 *iflag = 1;
554 break;
555 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000556 error("%s: Invalid flag -%c", cmd, optopt);
Damien Millerd671e5a2008-05-19 14:53:33 +1000557 return -1;
558 }
559 }
560
561 return optind;
562}
563
564static int
djm@openbsd.org60d8c842019-01-16 23:23:45 +0000565parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
566{
567 extern int opterr, optind, optopt, optreset;
568 int ch;
569
570 optind = optreset = 1;
571 opterr = 0;
572
573 *hflag = 0;
574 while ((ch = getopt(argc, argv, "h")) != -1) {
575 switch (ch) {
576 case 'h':
577 *hflag = 1;
578 break;
579 default:
580 error("%s: Invalid flag -%c", cmd, optopt);
581 return -1;
582 }
583 }
584
585 return optind;
586}
587
588static int
Damien Miller036d3072013-08-21 02:41:46 +1000589parse_no_flags(const char *cmd, char **argv, int argc)
590{
591 extern int opterr, optind, optopt, optreset;
592 int ch;
593
594 optind = optreset = 1;
595 opterr = 0;
596
597 while ((ch = getopt(argc, argv, "")) != -1) {
598 switch (ch) {
599 default:
600 error("%s: Invalid flag -%c", cmd, optopt);
601 return -1;
602 }
603 }
604
605 return optind;
606}
607
608static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000609is_dir(const char *path)
Damien Miller20e1fab2004-02-18 14:30:55 +1100610{
611 struct stat sb;
612
613 /* XXX: report errors? */
614 if (stat(path, &sb) == -1)
615 return(0);
616
Darren Tucker1e80e402006-09-21 12:59:33 +1000617 return(S_ISDIR(sb.st_mode));
Damien Miller20e1fab2004-02-18 14:30:55 +1100618}
619
620static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000621remote_is_dir(struct sftp_conn *conn, const char *path)
Damien Miller20e1fab2004-02-18 14:30:55 +1100622{
623 Attrib *a;
624
625 /* XXX: report errors? */
626 if ((a = do_stat(conn, path, 1)) == NULL)
627 return(0);
628 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
629 return(0);
Darren Tucker1e80e402006-09-21 12:59:33 +1000630 return(S_ISDIR(a->perm));
Damien Miller20e1fab2004-02-18 14:30:55 +1100631}
632
Darren Tucker1b0dd172009-10-07 08:37:48 +1100633/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
Damien Miller20e1fab2004-02-18 14:30:55 +1100634static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000635pathname_is_dir(const char *pathname)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100636{
637 size_t l = strlen(pathname);
638
639 return l > 0 && pathname[l - 1] == '/';
640}
641
642static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000643process_get(struct sftp_conn *conn, const char *src, const char *dst,
644 const char *pwd, int pflag, int rflag, int resume, int fflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100645{
646 char *abs_src = NULL;
647 char *abs_dst = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +1100648 glob_t g;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100649 char *filename, *tmp=NULL;
Damien Miller00707762014-07-09 13:07:06 +1000650 int i, r, err = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +1100651
652 abs_src = xstrdup(src);
653 abs_src = make_absolute(abs_src, pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +1100654 memset(&g, 0, sizeof(g));
Darren Tucker1b0dd172009-10-07 08:37:48 +1100655
Damien Miller20e1fab2004-02-18 14:30:55 +1100656 debug3("Looking up %s", abs_src);
Damien Miller00707762014-07-09 13:07:06 +1000657 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
658 if (r == GLOB_NOSPACE) {
659 error("Too many matches for \"%s\".", abs_src);
660 } else {
661 error("File \"%s\" not found.", abs_src);
662 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100663 err = -1;
664 goto out;
665 }
666
Darren Tucker1b0dd172009-10-07 08:37:48 +1100667 /*
668 * If multiple matches then dst must be a directory or
669 * unspecified.
670 */
671 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
672 error("Multiple source paths, but destination "
673 "\"%s\" is not a directory", dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100674 err = -1;
675 goto out;
676 }
677
Darren Tuckercdf547a2004-05-24 10:12:19 +1000678 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100679 tmp = xstrdup(g.gl_pathv[i]);
680 if ((filename = basename(tmp)) == NULL) {
681 error("basename %s: %s", tmp, strerror(errno));
Darren Tuckera627d422013-06-02 07:31:17 +1000682 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100683 err = -1;
684 goto out;
685 }
686
687 if (g.gl_matchc == 1 && dst) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100688 if (is_dir(dst)) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100689 abs_dst = path_append(dst, filename);
690 } else {
Damien Miller20e1fab2004-02-18 14:30:55 +1100691 abs_dst = xstrdup(dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100692 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100693 } else if (dst) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100694 abs_dst = path_append(dst, filename);
695 } else {
696 abs_dst = xstrdup(filename);
697 }
Darren Tuckera627d422013-06-02 07:31:17 +1000698 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100699
Damien Miller0d032412013-07-25 11:56:52 +1000700 resume |= global_aflag;
701 if (!quiet && resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000702 mprintf("Resuming %s to %s\n",
703 g.gl_pathv[i], abs_dst);
Damien Miller0d032412013-07-25 11:56:52 +1000704 else if (!quiet && !resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000705 mprintf("Fetching %s to %s\n",
706 g.gl_pathv[i], abs_dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100707 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
Damien Miller0d032412013-07-25 11:56:52 +1000708 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
Damien Millerf29238e2013-10-17 11:48:52 +1100709 pflag || global_pflag, 1, resume,
710 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100711 err = -1;
712 } else {
713 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
Damien Millerf29238e2013-10-17 11:48:52 +1100714 pflag || global_pflag, resume,
715 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100716 err = -1;
717 }
Darren Tuckera627d422013-06-02 07:31:17 +1000718 free(abs_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100719 abs_dst = NULL;
720 }
721
722out:
Darren Tuckera627d422013-06-02 07:31:17 +1000723 free(abs_src);
Damien Miller20e1fab2004-02-18 14:30:55 +1100724 globfree(&g);
725 return(err);
726}
727
728static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000729process_put(struct sftp_conn *conn, const char *src, const char *dst,
730 const char *pwd, int pflag, int rflag, int resume, int fflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100731{
732 char *tmp_dst = NULL;
733 char *abs_dst = NULL;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100734 char *tmp = NULL, *filename = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +1100735 glob_t g;
736 int err = 0;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100737 int i, dst_is_dir = 1;
Damien Milleraec5cf82008-02-10 22:26:24 +1100738 struct stat sb;
Damien Miller20e1fab2004-02-18 14:30:55 +1100739
740 if (dst) {
741 tmp_dst = xstrdup(dst);
742 tmp_dst = make_absolute(tmp_dst, pwd);
743 }
744
745 memset(&g, 0, sizeof(g));
746 debug3("Looking up %s", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100747 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100748 error("File \"%s\" not found.", src);
749 err = -1;
750 goto out;
751 }
752
Darren Tucker1b0dd172009-10-07 08:37:48 +1100753 /* If we aren't fetching to pwd then stash this status for later */
754 if (tmp_dst != NULL)
755 dst_is_dir = remote_is_dir(conn, tmp_dst);
756
Damien Miller20e1fab2004-02-18 14:30:55 +1100757 /* If multiple matches, dst may be directory or unspecified */
Darren Tucker1b0dd172009-10-07 08:37:48 +1100758 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
759 error("Multiple paths match, but destination "
760 "\"%s\" is not a directory", tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100761 err = -1;
762 goto out;
763 }
764
Darren Tuckercdf547a2004-05-24 10:12:19 +1000765 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Milleraec5cf82008-02-10 22:26:24 +1100766 if (stat(g.gl_pathv[i], &sb) == -1) {
767 err = -1;
768 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
769 continue;
770 }
Damien Miller02e87802013-08-21 02:38:51 +1000771
Darren Tucker1b0dd172009-10-07 08:37:48 +1100772 tmp = xstrdup(g.gl_pathv[i]);
773 if ((filename = basename(tmp)) == NULL) {
774 error("basename %s: %s", tmp, strerror(errno));
Darren Tuckera627d422013-06-02 07:31:17 +1000775 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100776 err = -1;
777 goto out;
778 }
779
780 if (g.gl_matchc == 1 && tmp_dst) {
781 /* If directory specified, append filename */
Darren Tucker1b0dd172009-10-07 08:37:48 +1100782 if (dst_is_dir)
783 abs_dst = path_append(tmp_dst, filename);
784 else
Damien Miller20e1fab2004-02-18 14:30:55 +1100785 abs_dst = xstrdup(tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100786 } else if (tmp_dst) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100787 abs_dst = path_append(tmp_dst, filename);
788 } else {
789 abs_dst = make_absolute(xstrdup(filename), pwd);
790 }
Darren Tuckera627d422013-06-02 07:31:17 +1000791 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100792
Damien Millerd8accc02014-05-15 13:46:25 +1000793 resume |= global_aflag;
794 if (!quiet && resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000795 mprintf("Resuming upload of %s to %s\n",
796 g.gl_pathv[i], abs_dst);
Damien Millerd8accc02014-05-15 13:46:25 +1000797 else if (!quiet && !resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000798 mprintf("Uploading %s to %s\n",
799 g.gl_pathv[i], abs_dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100800 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
801 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
Damien Millerd8accc02014-05-15 13:46:25 +1000802 pflag || global_pflag, 1, resume,
Damien Millerf29238e2013-10-17 11:48:52 +1100803 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100804 err = -1;
805 } else {
806 if (do_upload(conn, g.gl_pathv[i], abs_dst,
Damien Millerd8accc02014-05-15 13:46:25 +1000807 pflag || global_pflag, resume,
Damien Millerf29238e2013-10-17 11:48:52 +1100808 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100809 err = -1;
810 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100811 }
812
813out:
Darren Tuckera627d422013-06-02 07:31:17 +1000814 free(abs_dst);
815 free(tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100816 globfree(&g);
817 return(err);
818}
819
820static int
821sdirent_comp(const void *aa, const void *bb)
822{
823 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
824 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000825 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100826
Darren Tuckerb9123452004-06-22 13:06:45 +1000827#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000828 if (sort_flag & LS_NAME_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000829 return (rmul * strcmp(a->filename, b->filename));
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000830 else if (sort_flag & LS_TIME_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000831 return (rmul * NCMP(a->a.mtime, b->a.mtime));
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000832 else if (sort_flag & LS_SIZE_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000833 return (rmul * NCMP(a->a.size, b->a.size));
834
835 fatal("Unknown ls sort type");
Damien Miller20e1fab2004-02-18 14:30:55 +1100836}
837
838/* sftp ls.1 replacement for directories */
839static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000840do_ls_dir(struct sftp_conn *conn, const char *path,
841 const char *strip_path, int lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100842{
Damien Millereccb9de2005-06-17 12:59:34 +1000843 int n;
844 u_int c = 1, colspace = 0, columns = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100845 SFTP_DIRENT **d;
846
847 if ((n = do_readdir(conn, path, &d)) != 0)
848 return (n);
849
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000850 if (!(lflag & LS_SHORT_VIEW)) {
Damien Millereccb9de2005-06-17 12:59:34 +1000851 u_int m = 0, width = 80;
Damien Miller20e1fab2004-02-18 14:30:55 +1100852 struct winsize ws;
853 char *tmp;
854
855 /* Count entries for sort and find longest filename */
Darren Tucker9a526452004-06-22 13:09:55 +1000856 for (n = 0; d[n] != NULL; n++) {
857 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000858 m = MAXIMUM(m, strlen(d[n]->filename));
Darren Tucker9a526452004-06-22 13:09:55 +1000859 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100860
861 /* Add any subpath that also needs to be counted */
862 tmp = path_strip(path, strip_path);
863 m += strlen(tmp);
Darren Tuckera627d422013-06-02 07:31:17 +1000864 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100865
866 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
867 width = ws.ws_col;
868
869 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000870 columns = MAXIMUM(columns, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +1100871 colspace = width / columns;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000872 colspace = MINIMUM(colspace, width);
Damien Miller20e1fab2004-02-18 14:30:55 +1100873 }
874
Darren Tuckerb9123452004-06-22 13:06:45 +1000875 if (lflag & SORT_FLAGS) {
Damien Miller653b93b2005-11-05 15:15:23 +1100876 for (n = 0; d[n] != NULL; n++)
877 ; /* count entries */
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000878 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
Darren Tuckerb9123452004-06-22 13:06:45 +1000879 qsort(d, n, sizeof(*d), sdirent_comp);
880 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100881
Darren Tuckercdf547a2004-05-24 10:12:19 +1000882 for (n = 0; d[n] != NULL && !interrupted; n++) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100883 char *tmp, *fname;
884
Darren Tucker9a526452004-06-22 13:09:55 +1000885 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
886 continue;
887
Damien Miller20e1fab2004-02-18 14:30:55 +1100888 tmp = path_append(path, d[n]->filename);
889 fname = path_strip(tmp, strip_path);
Darren Tuckera627d422013-06-02 07:31:17 +1000890 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100891
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000892 if (lflag & LS_LONG_VIEW) {
Darren Tucker2901e2d2010-01-13 22:44:06 +1100893 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000894 char *lname;
895 struct stat sb;
Damien Miller20e1fab2004-02-18 14:30:55 +1100896
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000897 memset(&sb, 0, sizeof(sb));
898 attrib_to_stat(&d[n]->a, &sb);
Darren Tucker2901e2d2010-01-13 22:44:06 +1100899 lname = ls_file(fname, &sb, 1,
900 (lflag & LS_SI_UNITS));
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000901 mprintf("%s\n", lname);
Darren Tuckera627d422013-06-02 07:31:17 +1000902 free(lname);
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000903 } else
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000904 mprintf("%s\n", d[n]->longname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100905 } else {
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000906 mprintf("%-*s", colspace, fname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100907 if (c >= columns) {
908 printf("\n");
909 c = 1;
910 } else
911 c++;
912 }
913
Darren Tuckera627d422013-06-02 07:31:17 +1000914 free(fname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100915 }
916
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000917 if (!(lflag & LS_LONG_VIEW) && (c != 1))
Damien Miller20e1fab2004-02-18 14:30:55 +1100918 printf("\n");
919
920 free_sftp_dirents(d);
921 return (0);
922}
923
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000924static int
925sglob_comp(const void *aa, const void *bb)
926{
927 u_int a = *(const u_int *)aa;
928 u_int b = *(const u_int *)bb;
929 const char *ap = sort_glob->gl_pathv[a];
930 const char *bp = sort_glob->gl_pathv[b];
931 const struct stat *as = sort_glob->gl_statv[a];
932 const struct stat *bs = sort_glob->gl_statv[b];
933 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
934
935#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
936 if (sort_flag & LS_NAME_SORT)
937 return (rmul * strcmp(ap, bp));
Damien Millerbcd14852017-06-10 23:41:25 +1000938 else if (sort_flag & LS_TIME_SORT) {
939#if defined(HAVE_STRUCT_STAT_ST_MTIM)
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000940 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
Damien Millerbcd14852017-06-10 23:41:25 +1000941#elif defined(HAVE_STRUCT_STAT_ST_MTIME)
942 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
943#else
944 return rmul * 1;
945#endif
946 } else if (sort_flag & LS_SIZE_SORT)
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000947 return (rmul * NCMP(as->st_size, bs->st_size));
948
949 fatal("Unknown ls sort type");
950}
951
Damien Miller20e1fab2004-02-18 14:30:55 +1100952/* sftp ls.1 replacement which handles path globs */
953static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000954do_globbed_ls(struct sftp_conn *conn, const char *path,
955 const char *strip_path, int lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100956{
Damien Millera6e121a2010-10-07 21:39:17 +1100957 char *fname, *lname;
Damien Miller68e2e562010-10-07 21:39:55 +1100958 glob_t g;
Damien Miller00707762014-07-09 13:07:06 +1000959 int err, r;
Damien Miller68e2e562010-10-07 21:39:55 +1100960 struct winsize ws;
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000961 u_int i, j, nentries, *indices = NULL, c = 1;
962 u_int colspace = 0, columns = 1, m = 0, width = 80;
Damien Miller20e1fab2004-02-18 14:30:55 +1100963
964 memset(&g, 0, sizeof(g));
965
Damien Miller00707762014-07-09 13:07:06 +1000966 if ((r = remote_glob(conn, path,
Damien Millerd7be70d2011-09-22 21:43:06 +1000967 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
Damien Miller00707762014-07-09 13:07:06 +1000968 NULL, &g)) != 0 ||
Damien Millera6e121a2010-10-07 21:39:17 +1100969 (g.gl_pathc && !g.gl_matchc)) {
Darren Tucker596dcfa2004-12-11 13:37:22 +1100970 if (g.gl_pathc)
971 globfree(&g);
Damien Miller00707762014-07-09 13:07:06 +1000972 if (r == GLOB_NOSPACE) {
973 error("Can't ls: Too many matches for \"%s\"", path);
974 } else {
975 error("Can't ls: \"%s\" not found", path);
976 }
Damien Millera6e121a2010-10-07 21:39:17 +1100977 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100978 }
979
Darren Tuckercdf547a2004-05-24 10:12:19 +1000980 if (interrupted)
981 goto out;
982
Damien Miller20e1fab2004-02-18 14:30:55 +1100983 /*
Darren Tucker596dcfa2004-12-11 13:37:22 +1100984 * If the glob returns a single match and it is a directory,
985 * then just list its contents.
Damien Miller20e1fab2004-02-18 14:30:55 +1100986 */
Damien Millera6e121a2010-10-07 21:39:17 +1100987 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
988 S_ISDIR(g.gl_statv[0]->st_mode)) {
989 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
990 globfree(&g);
991 return err;
Damien Miller20e1fab2004-02-18 14:30:55 +1100992 }
993
Damien Miller68e2e562010-10-07 21:39:55 +1100994 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
995 width = ws.ws_col;
Damien Miller20e1fab2004-02-18 14:30:55 +1100996
Damien Miller68e2e562010-10-07 21:39:55 +1100997 if (!(lflag & LS_SHORT_VIEW)) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100998 /* Count entries for sort and find longest filename */
999 for (i = 0; g.gl_pathv[i]; i++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001000 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
Damien Miller20e1fab2004-02-18 14:30:55 +11001001
Damien Miller20e1fab2004-02-18 14:30:55 +11001002 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001003 columns = MAXIMUM(columns, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001004 colspace = width / columns;
1005 }
1006
djm@openbsd.org72be5b22017-06-10 06:33:34 +00001007 /*
1008 * Sorting: rather than mess with the contents of glob_t, prepare
1009 * an array of indices into it and sort that. For the usual
1010 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1011 */
1012 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1013 ; /* count entries */
1014 indices = calloc(nentries, sizeof(*indices));
1015 for (i = 0; i < nentries; i++)
1016 indices[i] = i;
1017
1018 if (lflag & SORT_FLAGS) {
1019 sort_glob = &g;
1020 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1021 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1022 sort_glob = NULL;
1023 }
1024
1025 for (j = 0; j < nentries && !interrupted; j++) {
1026 i = indices[j];
Damien Miller20e1fab2004-02-18 14:30:55 +11001027 fname = path_strip(g.gl_pathv[i], strip_path);
Darren Tuckera4e9ffa2004-06-22 13:07:58 +10001028 if (lflag & LS_LONG_VIEW) {
Damien Millera6e121a2010-10-07 21:39:17 +11001029 if (g.gl_statv[i] == NULL) {
1030 error("no stat information for %s", fname);
1031 continue;
1032 }
1033 lname = ls_file(fname, g.gl_statv[i], 1,
1034 (lflag & LS_SI_UNITS));
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001035 mprintf("%s\n", lname);
Darren Tuckera627d422013-06-02 07:31:17 +10001036 free(lname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001037 } else {
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001038 mprintf("%-*s", colspace, fname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001039 if (c >= columns) {
1040 printf("\n");
1041 c = 1;
1042 } else
1043 c++;
1044 }
Darren Tuckera627d422013-06-02 07:31:17 +10001045 free(fname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001046 }
1047
Darren Tuckera4e9ffa2004-06-22 13:07:58 +10001048 if (!(lflag & LS_LONG_VIEW) && (c != 1))
Damien Miller20e1fab2004-02-18 14:30:55 +11001049 printf("\n");
1050
Darren Tuckercdf547a2004-05-24 10:12:19 +10001051 out:
Damien Miller20e1fab2004-02-18 14:30:55 +11001052 if (g.gl_pathc)
1053 globfree(&g);
djm@openbsd.org72be5b22017-06-10 06:33:34 +00001054 free(indices);
Damien Miller20e1fab2004-02-18 14:30:55 +11001055
Damien Millera6e121a2010-10-07 21:39:17 +11001056 return 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001057}
1058
Damien Millerd671e5a2008-05-19 14:53:33 +10001059static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +00001060do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
Damien Millerd671e5a2008-05-19 14:53:33 +10001061{
Darren Tucker7b598892008-06-09 22:49:36 +10001062 struct sftp_statvfs st;
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001063 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1064 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1065 char s_icapacity[16], s_dcapacity[16];
Damien Millerd671e5a2008-05-19 14:53:33 +10001066
1067 if (do_statvfs(conn, path, &st, 1) == -1)
1068 return -1;
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001069 if (st.f_files == 0)
1070 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1071 else {
1072 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1073 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1074 st.f_files));
1075 }
1076 if (st.f_blocks == 0)
1077 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1078 else {
1079 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1080 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1081 st.f_blocks));
1082 }
Damien Millerd671e5a2008-05-19 14:53:33 +10001083 if (iflag) {
1084 printf(" Inodes Used Avail "
1085 "(root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001086 printf("%11llu %11llu %11llu %11llu %s\n",
Damien Millerd671e5a2008-05-19 14:53:33 +10001087 (unsigned long long)st.f_files,
1088 (unsigned long long)(st.f_files - st.f_ffree),
1089 (unsigned long long)st.f_favail,
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001090 (unsigned long long)st.f_ffree, s_icapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001091 } else if (hflag) {
1092 strlcpy(s_used, "error", sizeof(s_used));
1093 strlcpy(s_avail, "error", sizeof(s_avail));
1094 strlcpy(s_root, "error", sizeof(s_root));
1095 strlcpy(s_total, "error", sizeof(s_total));
1096 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1097 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1098 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1099 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1100 printf(" Size Used Avail (root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001101 printf("%7sB %7sB %7sB %7sB %s\n",
1102 s_total, s_used, s_avail, s_root, s_dcapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001103 } else {
1104 printf(" Size Used Avail "
1105 "(root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001106 printf("%12llu %12llu %12llu %12llu %s\n",
Damien Millerd671e5a2008-05-19 14:53:33 +10001107 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1108 (unsigned long long)(st.f_frsize *
1109 (st.f_blocks - st.f_bfree) / 1024),
1110 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1111 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001112 s_dcapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001113 }
1114 return 0;
1115}
1116
Damien Miller1cbc2922007-10-26 14:27:45 +10001117/*
1118 * Undo escaping of glob sequences in place. Used to undo extra escaping
1119 * applied in makeargv() when the string is destined for a function that
1120 * does not glob it.
1121 */
1122static void
1123undo_glob_escape(char *s)
1124{
1125 size_t i, j;
1126
1127 for (i = j = 0;;) {
1128 if (s[i] == '\0') {
1129 s[j] = '\0';
1130 return;
1131 }
1132 if (s[i] != '\\') {
1133 s[j++] = s[i++];
1134 continue;
1135 }
1136 /* s[i] == '\\' */
1137 ++i;
1138 switch (s[i]) {
1139 case '?':
1140 case '[':
1141 case '*':
1142 case '\\':
1143 s[j++] = s[i++];
1144 break;
1145 case '\0':
1146 s[j++] = '\\';
1147 s[j] = '\0';
1148 return;
1149 default:
1150 s[j++] = '\\';
1151 s[j++] = s[i++];
1152 break;
1153 }
1154 }
1155}
1156
1157/*
1158 * Split a string into an argument vector using sh(1)-style quoting,
1159 * comment and escaping rules, but with some tweaks to handle glob(3)
1160 * wildcards.
Darren Tucker909d8582010-01-08 19:02:40 +11001161 * The "sloppy" flag allows for recovery from missing terminating quote, for
1162 * use in parsing incomplete commandlines during tab autocompletion.
1163 *
Damien Miller1cbc2922007-10-26 14:27:45 +10001164 * Returns NULL on error or a NULL-terminated array of arguments.
Darren Tucker909d8582010-01-08 19:02:40 +11001165 *
1166 * If "lastquote" is not NULL, the quoting character used for the last
1167 * argument is placed in *lastquote ("\0", "'" or "\"").
Damien Miller02e87802013-08-21 02:38:51 +10001168 *
Darren Tucker909d8582010-01-08 19:02:40 +11001169 * If "terminated" is not NULL, *terminated will be set to 1 when the
1170 * last argument's quote has been properly terminated or 0 otherwise.
1171 * This parameter is only of use if "sloppy" is set.
Damien Miller1cbc2922007-10-26 14:27:45 +10001172 */
1173#define MAXARGS 128
1174#define MAXARGLEN 8192
1175static char **
Darren Tucker909d8582010-01-08 19:02:40 +11001176makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1177 u_int *terminated)
Damien Miller1cbc2922007-10-26 14:27:45 +10001178{
1179 int argc, quot;
1180 size_t i, j;
1181 static char argvs[MAXARGLEN];
1182 static char *argv[MAXARGS + 1];
1183 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1184
1185 *argcp = argc = 0;
1186 if (strlen(arg) > sizeof(argvs) - 1) {
1187 args_too_longs:
1188 error("string too long");
1189 return NULL;
1190 }
Darren Tucker909d8582010-01-08 19:02:40 +11001191 if (terminated != NULL)
1192 *terminated = 1;
1193 if (lastquote != NULL)
1194 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001195 state = MA_START;
1196 i = j = 0;
1197 for (;;) {
Damien Miller07daed52012-10-31 08:57:55 +11001198 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
Darren Tucker063018d2012-10-05 10:43:58 +10001199 error("Too many arguments.");
1200 return NULL;
1201 }
Damien Millerfdb23062013-11-21 13:57:15 +11001202 if (isspace((unsigned char)arg[i])) {
Damien Miller1cbc2922007-10-26 14:27:45 +10001203 if (state == MA_UNQUOTED) {
1204 /* Terminate current argument */
1205 argvs[j++] = '\0';
1206 argc++;
1207 state = MA_START;
1208 } else if (state != MA_START)
1209 argvs[j++] = arg[i];
1210 } else if (arg[i] == '"' || arg[i] == '\'') {
1211 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1212 if (state == MA_START) {
1213 argv[argc] = argvs + j;
1214 state = q;
Darren Tucker909d8582010-01-08 19:02:40 +11001215 if (lastquote != NULL)
1216 *lastquote = arg[i];
Damien Miller02e87802013-08-21 02:38:51 +10001217 } else if (state == MA_UNQUOTED)
Damien Miller1cbc2922007-10-26 14:27:45 +10001218 state = q;
1219 else if (state == q)
1220 state = MA_UNQUOTED;
1221 else
1222 argvs[j++] = arg[i];
1223 } else if (arg[i] == '\\') {
1224 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1225 quot = state == MA_SQUOTE ? '\'' : '"';
1226 /* Unescape quote we are in */
1227 /* XXX support \n and friends? */
1228 if (arg[i + 1] == quot) {
1229 i++;
1230 argvs[j++] = arg[i];
1231 } else if (arg[i + 1] == '?' ||
1232 arg[i + 1] == '[' || arg[i + 1] == '*') {
1233 /*
1234 * Special case for sftp: append
1235 * double-escaped glob sequence -
1236 * glob will undo one level of
1237 * escaping. NB. string can grow here.
1238 */
1239 if (j >= sizeof(argvs) - 5)
1240 goto args_too_longs;
1241 argvs[j++] = '\\';
1242 argvs[j++] = arg[i++];
1243 argvs[j++] = '\\';
1244 argvs[j++] = arg[i];
1245 } else {
1246 argvs[j++] = arg[i++];
1247 argvs[j++] = arg[i];
1248 }
1249 } else {
1250 if (state == MA_START) {
1251 argv[argc] = argvs + j;
1252 state = MA_UNQUOTED;
Darren Tucker909d8582010-01-08 19:02:40 +11001253 if (lastquote != NULL)
1254 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001255 }
1256 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1257 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1258 /*
1259 * Special case for sftp: append
1260 * escaped glob sequence -
1261 * glob will undo one level of
1262 * escaping.
1263 */
1264 argvs[j++] = arg[i++];
1265 argvs[j++] = arg[i];
1266 } else {
1267 /* Unescape everything */
1268 /* XXX support \n and friends? */
1269 i++;
1270 argvs[j++] = arg[i];
1271 }
1272 }
1273 } else if (arg[i] == '#') {
1274 if (state == MA_SQUOTE || state == MA_DQUOTE)
1275 argvs[j++] = arg[i];
1276 else
1277 goto string_done;
1278 } else if (arg[i] == '\0') {
1279 if (state == MA_SQUOTE || state == MA_DQUOTE) {
Darren Tucker909d8582010-01-08 19:02:40 +11001280 if (sloppy) {
1281 state = MA_UNQUOTED;
1282 if (terminated != NULL)
1283 *terminated = 0;
1284 goto string_done;
1285 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001286 error("Unterminated quoted argument");
1287 return NULL;
1288 }
1289 string_done:
1290 if (state == MA_UNQUOTED) {
1291 argvs[j++] = '\0';
1292 argc++;
1293 }
1294 break;
1295 } else {
1296 if (state == MA_START) {
1297 argv[argc] = argvs + j;
1298 state = MA_UNQUOTED;
Darren Tucker909d8582010-01-08 19:02:40 +11001299 if (lastquote != NULL)
1300 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001301 }
1302 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1303 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1304 /*
1305 * Special case for sftp: escape quoted
1306 * glob(3) wildcards. NB. string can grow
1307 * here.
1308 */
1309 if (j >= sizeof(argvs) - 3)
1310 goto args_too_longs;
1311 argvs[j++] = '\\';
1312 argvs[j++] = arg[i];
1313 } else
1314 argvs[j++] = arg[i];
1315 }
1316 i++;
1317 }
1318 *argcp = argc;
1319 return argv;
1320}
1321
Damien Miller20e1fab2004-02-18 14:30:55 +11001322static int
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001323parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
djm@openbsd.org34a01b22016-04-08 08:19:17 +00001324 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
Damien Millerd8accc02014-05-15 13:46:25 +10001325 int *rflag, int *sflag,
Damien Millerf29238e2013-10-17 11:48:52 +11001326 unsigned long *n_arg, char **path1, char **path2)
Damien Miller20e1fab2004-02-18 14:30:55 +11001327{
1328 const char *cmd, *cp = *cpp;
Damien Miller1cbc2922007-10-26 14:27:45 +10001329 char *cp2, **argv;
Damien Miller20e1fab2004-02-18 14:30:55 +11001330 int base = 0;
1331 long l;
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001332 int path1_mandatory = 0, i, cmdnum, optidx, argc;
Damien Miller20e1fab2004-02-18 14:30:55 +11001333
1334 /* Skip leading whitespace */
1335 cp = cp + strspn(cp, WHITESPACE);
1336
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001337 /*
1338 * Check for leading '-' (disable error processing) and '@' (suppress
1339 * command echo)
1340 */
Damien Millerf29238e2013-10-17 11:48:52 +11001341 *ignore_errors = 0;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001342 *disable_echo = 0;
1343 for (;*cp != '\0'; cp++) {
1344 if (*cp == '-') {
1345 *ignore_errors = 1;
1346 } else if (*cp == '@') {
1347 *disable_echo = 1;
1348 } else {
1349 /* all other characters terminate prefix processing */
1350 break;
1351 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001352 }
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001353 cp = cp + strspn(cp, WHITESPACE);
Damien Miller20e1fab2004-02-18 14:30:55 +11001354
Darren Tucker70cc0922010-01-09 22:28:03 +11001355 /* Ignore blank lines and lines which begin with comment '#' char */
1356 if (*cp == '\0' || *cp == '#')
1357 return (0);
1358
Darren Tucker909d8582010-01-08 19:02:40 +11001359 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
Damien Miller1cbc2922007-10-26 14:27:45 +10001360 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001361
Damien Miller1cbc2922007-10-26 14:27:45 +10001362 /* Figure out which command we have */
1363 for (i = 0; cmds[i].c != NULL; i++) {
Damien Millerd6d9fa02013-02-12 11:02:46 +11001364 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
Damien Miller20e1fab2004-02-18 14:30:55 +11001365 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001366 }
1367 cmdnum = cmds[i].n;
1368 cmd = cmds[i].c;
1369
1370 /* Special case */
1371 if (*cp == '!') {
1372 cp++;
1373 cmdnum = I_SHELL;
1374 } else if (cmdnum == -1) {
1375 error("Invalid command.");
Damien Miller1cbc2922007-10-26 14:27:45 +10001376 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001377 }
1378
1379 /* Get arguments and parse flags */
Damien Millerf29238e2013-10-17 11:48:52 +11001380 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1381 *rflag = *sflag = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001382 *path1 = *path2 = NULL;
Damien Miller1cbc2922007-10-26 14:27:45 +10001383 optidx = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001384 switch (cmdnum) {
1385 case I_GET:
Damien Miller0d032412013-07-25 11:56:52 +10001386 case I_REGET:
Damien Millerd8accc02014-05-15 13:46:25 +10001387 case I_REPUT:
Damien Miller20e1fab2004-02-18 14:30:55 +11001388 case I_PUT:
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001389 if ((optidx = parse_getput_flags(cmd, argv, argc,
Damien Millerf29238e2013-10-17 11:48:52 +11001390 aflag, fflag, pflag, rflag)) == -1)
Damien Miller1cbc2922007-10-26 14:27:45 +10001391 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001392 /* Get first pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001393 if (argc - optidx < 1) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001394 error("You must specify at least one path after a "
1395 "%s command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001396 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001397 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001398 *path1 = xstrdup(argv[optidx]);
1399 /* Get second pathname (optional) */
1400 if (argc - optidx > 1) {
1401 *path2 = xstrdup(argv[optidx + 1]);
1402 /* Destination is not globbed */
1403 undo_glob_escape(*path2);
1404 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001405 break;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001406 case I_LINK:
1407 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1408 return -1;
Damien Millerc7dba122013-08-21 02:41:15 +10001409 goto parse_two_paths;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001410 case I_RENAME:
Damien Millerc7dba122013-08-21 02:41:15 +10001411 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1412 return -1;
1413 goto parse_two_paths;
1414 case I_SYMLINK:
Damien Miller036d3072013-08-21 02:41:46 +10001415 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1416 return -1;
Damien Millerc7dba122013-08-21 02:41:15 +10001417 parse_two_paths:
Damien Miller1cbc2922007-10-26 14:27:45 +10001418 if (argc - optidx < 2) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001419 error("You must specify two paths after a %s "
1420 "command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001421 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001422 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001423 *path1 = xstrdup(argv[optidx]);
1424 *path2 = xstrdup(argv[optidx + 1]);
1425 /* Paths are not globbed */
1426 undo_glob_escape(*path1);
1427 undo_glob_escape(*path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001428 break;
1429 case I_RM:
1430 case I_MKDIR:
1431 case I_RMDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001432 case I_LMKDIR:
1433 path1_mandatory = 1;
1434 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001435 case I_CHDIR:
1436 case I_LCHDIR:
Damien Miller036d3072013-08-21 02:41:46 +10001437 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1438 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001439 /* Get pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001440 if (argc - optidx < 1) {
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001441 if (!path1_mandatory)
1442 break; /* return a NULL path1 */
Damien Miller20e1fab2004-02-18 14:30:55 +11001443 error("You must specify a path after a %s command.",
1444 cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001445 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001446 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001447 *path1 = xstrdup(argv[optidx]);
1448 /* Only "rm" globs */
1449 if (cmdnum != I_RM)
1450 undo_glob_escape(*path1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001451 break;
Damien Millerd671e5a2008-05-19 14:53:33 +10001452 case I_DF:
1453 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1454 iflag)) == -1)
1455 return -1;
1456 /* Default to current directory if no path specified */
1457 if (argc - optidx < 1)
1458 *path1 = NULL;
1459 else {
1460 *path1 = xstrdup(argv[optidx]);
1461 undo_glob_escape(*path1);
1462 }
1463 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001464 case I_LS:
Damien Miller1cbc2922007-10-26 14:27:45 +10001465 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
Damien Miller20e1fab2004-02-18 14:30:55 +11001466 return(-1);
1467 /* Path is optional */
Damien Miller1cbc2922007-10-26 14:27:45 +10001468 if (argc - optidx > 0)
1469 *path1 = xstrdup(argv[optidx]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001470 break;
1471 case I_LLS:
Darren Tucker88b976f2007-12-29 02:40:43 +11001472 /* Skip ls command and following whitespace */
1473 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
Damien Miller20e1fab2004-02-18 14:30:55 +11001474 case I_SHELL:
1475 /* Uses the rest of the line */
1476 break;
1477 case I_LUMASK:
Damien Miller20e1fab2004-02-18 14:30:55 +11001478 case I_CHMOD:
1479 base = 8;
dtucker@openbsd.orgde37ca92018-09-07 04:26:56 +00001480 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001481 case I_CHOWN:
1482 case I_CHGRP:
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001483 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
Damien Miller036d3072013-08-21 02:41:46 +10001484 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001485 /* Get numeric arg (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001486 if (argc - optidx < 1)
1487 goto need_num_arg;
Damien Millere7658a52006-10-24 03:00:12 +10001488 errno = 0;
Damien Miller1cbc2922007-10-26 14:27:45 +10001489 l = strtol(argv[optidx], &cp2, base);
1490 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1491 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1492 l < 0) {
1493 need_num_arg:
Damien Miller20e1fab2004-02-18 14:30:55 +11001494 error("You must supply a numeric argument "
1495 "to the %s command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001496 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001497 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001498 *n_arg = l;
Damien Miller1cbc2922007-10-26 14:27:45 +10001499 if (cmdnum == I_LUMASK)
Damien Miller20e1fab2004-02-18 14:30:55 +11001500 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001501 /* Get pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001502 if (argc - optidx < 2) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001503 error("You must specify a path after a %s command.",
1504 cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001505 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001506 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001507 *path1 = xstrdup(argv[optidx + 1]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001508 break;
1509 case I_QUIT:
1510 case I_PWD:
1511 case I_LPWD:
1512 case I_HELP:
1513 case I_VERSION:
1514 case I_PROGRESS:
Damien Miller036d3072013-08-21 02:41:46 +10001515 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1516 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001517 break;
1518 default:
1519 fatal("Command not implemented");
1520 }
1521
1522 *cpp = cp;
1523 return(cmdnum);
1524}
1525
1526static int
1527parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001528 const char *startdir, int err_abort, int echo_command)
Damien Miller20e1fab2004-02-18 14:30:55 +11001529{
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001530 const char *ocmd = cmd;
Damien Miller20e1fab2004-02-18 14:30:55 +11001531 char *path1, *path2, *tmp;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001532 int ignore_errors = 0, disable_echo = 1;
1533 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
Damien Millerf29238e2013-10-17 11:48:52 +11001534 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001535 int cmdnum, i;
Damien Millerfdd66fc2009-02-14 16:26:19 +11001536 unsigned long n_arg = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001537 Attrib a, *aa;
deraadt@openbsd.org087266e2015-01-20 23:14:00 +00001538 char path_buf[PATH_MAX];
Damien Miller20e1fab2004-02-18 14:30:55 +11001539 int err = 0;
1540 glob_t g;
1541
1542 path1 = path2 = NULL;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001543 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1544 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1545 &path1, &path2);
Damien Millerf29238e2013-10-17 11:48:52 +11001546 if (ignore_errors != 0)
Damien Miller20e1fab2004-02-18 14:30:55 +11001547 err_abort = 0;
1548
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001549 if (echo_command && !disable_echo)
1550 mprintf("sftp> %s\n", ocmd);
1551
Damien Miller20e1fab2004-02-18 14:30:55 +11001552 memset(&g, 0, sizeof(g));
1553
1554 /* Perform command */
1555 switch (cmdnum) {
1556 case 0:
1557 /* Blank line */
1558 break;
1559 case -1:
1560 /* Unrecognized command */
1561 err = -1;
1562 break;
Damien Miller0d032412013-07-25 11:56:52 +10001563 case I_REGET:
1564 aflag = 1;
1565 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001566 case I_GET:
Damien Miller0d032412013-07-25 11:56:52 +10001567 err = process_get(conn, path1, path2, *pwd, pflag,
Damien Millerf29238e2013-10-17 11:48:52 +11001568 rflag, aflag, fflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001569 break;
Damien Millerd8accc02014-05-15 13:46:25 +10001570 case I_REPUT:
1571 aflag = 1;
1572 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001573 case I_PUT:
Damien Millerf29238e2013-10-17 11:48:52 +11001574 err = process_put(conn, path1, path2, *pwd, pflag,
Damien Millerd8accc02014-05-15 13:46:25 +10001575 rflag, aflag, fflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001576 break;
1577 case I_RENAME:
1578 path1 = make_absolute(path1, *pwd);
1579 path2 = make_absolute(path2, *pwd);
Damien Millerc7dba122013-08-21 02:41:15 +10001580 err = do_rename(conn, path1, path2, lflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001581 break;
1582 case I_SYMLINK:
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001583 sflag = 1;
dtucker@openbsd.orgde37ca92018-09-07 04:26:56 +00001584 /* FALLTHROUGH */
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001585 case I_LINK:
Damien Miller034f27a2013-08-21 02:40:44 +10001586 if (!sflag)
1587 path1 = make_absolute(path1, *pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001588 path2 = make_absolute(path2, *pwd);
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001589 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001590 break;
1591 case I_RM:
1592 path1 = make_absolute(path1, *pwd);
1593 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001594 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Miller9303e652013-04-23 15:22:40 +10001595 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001596 mprintf("Removing %s\n", g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001597 err = do_rm(conn, g.gl_pathv[i]);
1598 if (err != 0 && err_abort)
1599 break;
1600 }
1601 break;
1602 case I_MKDIR:
1603 path1 = make_absolute(path1, *pwd);
1604 attrib_clear(&a);
1605 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1606 a.perm = 0777;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001607 err = do_mkdir(conn, path1, &a, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001608 break;
1609 case I_RMDIR:
1610 path1 = make_absolute(path1, *pwd);
1611 err = do_rmdir(conn, path1);
1612 break;
1613 case I_CHDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001614 if (path1 == NULL || *path1 == '\0')
1615 path1 = xstrdup(startdir);
Damien Miller20e1fab2004-02-18 14:30:55 +11001616 path1 = make_absolute(path1, *pwd);
1617 if ((tmp = do_realpath(conn, path1)) == NULL) {
1618 err = 1;
1619 break;
1620 }
1621 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
Darren Tuckera627d422013-06-02 07:31:17 +10001622 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001623 err = 1;
1624 break;
1625 }
1626 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1627 error("Can't change directory: Can't check target");
Darren Tuckera627d422013-06-02 07:31:17 +10001628 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001629 err = 1;
1630 break;
1631 }
1632 if (!S_ISDIR(aa->perm)) {
1633 error("Can't change directory: \"%s\" is not "
1634 "a directory", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10001635 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001636 err = 1;
1637 break;
1638 }
Darren Tuckera627d422013-06-02 07:31:17 +10001639 free(*pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001640 *pwd = tmp;
1641 break;
1642 case I_LS:
1643 if (!path1) {
Damien Miller99ac4e92010-06-26 09:36:58 +10001644 do_ls_dir(conn, *pwd, *pwd, lflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001645 break;
1646 }
1647
1648 /* Strip pwd off beginning of non-absolute paths */
1649 tmp = NULL;
djm@openbsd.org2a358622018-11-16 03:26:01 +00001650 if (!path_absolute(path1))
Damien Miller20e1fab2004-02-18 14:30:55 +11001651 tmp = *pwd;
1652
1653 path1 = make_absolute(path1, *pwd);
1654 err = do_globbed_ls(conn, path1, tmp, lflag);
1655 break;
Damien Millerd671e5a2008-05-19 14:53:33 +10001656 case I_DF:
1657 /* Default to current directory if no path specified */
1658 if (path1 == NULL)
1659 path1 = xstrdup(*pwd);
1660 path1 = make_absolute(path1, *pwd);
1661 err = do_df(conn, path1, hflag, iflag);
1662 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001663 case I_LCHDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001664 if (path1 == NULL || *path1 == '\0')
1665 path1 = xstrdup("~");
deraadt@openbsd.org40ba4c92014-08-20 01:28:55 +00001666 tmp = tilde_expand_filename(path1, getuid());
djm@openbsd.org7ff880e2014-08-19 23:57:18 +00001667 free(path1);
1668 path1 = tmp;
Damien Miller20e1fab2004-02-18 14:30:55 +11001669 if (chdir(path1) == -1) {
1670 error("Couldn't change local directory to "
1671 "\"%s\": %s", path1, strerror(errno));
1672 err = 1;
1673 }
1674 break;
1675 case I_LMKDIR:
1676 if (mkdir(path1, 0777) == -1) {
1677 error("Couldn't create local directory "
1678 "\"%s\": %s", path1, strerror(errno));
1679 err = 1;
1680 }
1681 break;
1682 case I_LLS:
1683 local_do_ls(cmd);
1684 break;
1685 case I_SHELL:
1686 local_do_shell(cmd);
1687 break;
1688 case I_LUMASK:
1689 umask(n_arg);
1690 printf("Local umask: %03lo\n", n_arg);
1691 break;
1692 case I_CHMOD:
1693 path1 = make_absolute(path1, *pwd);
1694 attrib_clear(&a);
1695 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1696 a.perm = n_arg;
1697 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001698 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Miller9303e652013-04-23 15:22:40 +10001699 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001700 mprintf("Changing mode on %s\n",
1701 g.gl_pathv[i]);
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001702 err = (hflag ? do_lsetstat : do_setstat)(conn,
1703 g.gl_pathv[i], &a);
Damien Miller20e1fab2004-02-18 14:30:55 +11001704 if (err != 0 && err_abort)
1705 break;
1706 }
1707 break;
1708 case I_CHOWN:
1709 case I_CHGRP:
1710 path1 = make_absolute(path1, *pwd);
1711 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001712 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001713 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1714 g.gl_pathv[i], 0))) {
Damien Miller1be2cc42008-12-09 14:11:49 +11001715 if (err_abort) {
1716 err = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001717 break;
Damien Miller1be2cc42008-12-09 14:11:49 +11001718 } else
Damien Miller20e1fab2004-02-18 14:30:55 +11001719 continue;
1720 }
1721 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1722 error("Can't get current ownership of "
1723 "remote file \"%s\"", g.gl_pathv[i]);
Damien Miller1be2cc42008-12-09 14:11:49 +11001724 if (err_abort) {
1725 err = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001726 break;
Damien Miller1be2cc42008-12-09 14:11:49 +11001727 } else
Damien Miller20e1fab2004-02-18 14:30:55 +11001728 continue;
1729 }
1730 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1731 if (cmdnum == I_CHOWN) {
Damien Miller9303e652013-04-23 15:22:40 +10001732 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001733 mprintf("Changing owner on %s\n",
Damien Miller9303e652013-04-23 15:22:40 +10001734 g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001735 aa->uid = n_arg;
1736 } else {
Damien Miller9303e652013-04-23 15:22:40 +10001737 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001738 mprintf("Changing group on %s\n",
Damien Miller9303e652013-04-23 15:22:40 +10001739 g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001740 aa->gid = n_arg;
1741 }
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001742 err = (hflag ? do_lsetstat : do_setstat)(conn,
1743 g.gl_pathv[i], aa);
Damien Miller20e1fab2004-02-18 14:30:55 +11001744 if (err != 0 && err_abort)
1745 break;
1746 }
1747 break;
1748 case I_PWD:
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001749 mprintf("Remote working directory: %s\n", *pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001750 break;
1751 case I_LPWD:
1752 if (!getcwd(path_buf, sizeof(path_buf))) {
1753 error("Couldn't get local cwd: %s", strerror(errno));
1754 err = -1;
1755 break;
1756 }
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001757 mprintf("Local working directory: %s\n", path_buf);
Damien Miller20e1fab2004-02-18 14:30:55 +11001758 break;
1759 case I_QUIT:
1760 /* Processed below */
1761 break;
1762 case I_HELP:
1763 help();
1764 break;
1765 case I_VERSION:
1766 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1767 break;
1768 case I_PROGRESS:
1769 showprogress = !showprogress;
1770 if (showprogress)
1771 printf("Progress meter enabled\n");
1772 else
1773 printf("Progress meter disabled\n");
1774 break;
1775 default:
1776 fatal("%d is not implemented", cmdnum);
1777 }
1778
1779 if (g.gl_pathc)
1780 globfree(&g);
Darren Tuckera627d422013-06-02 07:31:17 +10001781 free(path1);
1782 free(path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001783
1784 /* If an unignored error occurs in batch mode we should abort. */
1785 if (err_abort && err != 0)
1786 return (-1);
1787 else if (cmdnum == I_QUIT)
1788 return (1);
1789
1790 return (0);
1791}
1792
Darren Tucker2d963d82004-11-07 20:04:10 +11001793#ifdef USE_LIBEDIT
1794static char *
1795prompt(EditLine *el)
1796{
1797 return ("sftp> ");
1798}
Darren Tucker2d963d82004-11-07 20:04:10 +11001799
Darren Tucker909d8582010-01-08 19:02:40 +11001800/* Display entries in 'list' after skipping the first 'len' chars */
1801static void
1802complete_display(char **list, u_int len)
1803{
1804 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1805 struct winsize ws;
1806 char *tmp;
1807
1808 /* Count entries for sort and find longest */
Damien Miller02e87802013-08-21 02:38:51 +10001809 for (y = 0; list[y]; y++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001810 m = MAXIMUM(m, strlen(list[y]));
Darren Tucker909d8582010-01-08 19:02:40 +11001811
1812 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1813 width = ws.ws_col;
1814
1815 m = m > len ? m - len : 0;
1816 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001817 columns = MAXIMUM(columns, 1);
Darren Tucker909d8582010-01-08 19:02:40 +11001818 colspace = width / columns;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001819 colspace = MINIMUM(colspace, width);
Darren Tucker909d8582010-01-08 19:02:40 +11001820
1821 printf("\n");
1822 m = 1;
1823 for (y = 0; list[y]; y++) {
1824 llen = strlen(list[y]);
1825 tmp = llen > len ? list[y] + len : "";
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001826 mprintf("%-*s", colspace, tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11001827 if (m >= columns) {
1828 printf("\n");
1829 m = 1;
1830 } else
1831 m++;
1832 }
1833 printf("\n");
1834}
1835
1836/*
1837 * Given a "list" of words that begin with a common prefix of "word",
1838 * attempt to find an autocompletion to extends "word" by the next
1839 * characters common to all entries in "list".
1840 */
1841static char *
1842complete_ambiguous(const char *word, char **list, size_t count)
1843{
1844 if (word == NULL)
1845 return NULL;
1846
1847 if (count > 0) {
1848 u_int y, matchlen = strlen(list[0]);
1849
1850 /* Find length of common stem */
1851 for (y = 1; list[y]; y++) {
1852 u_int x;
1853
Damien Miller02e87802013-08-21 02:38:51 +10001854 for (x = 0; x < matchlen; x++)
1855 if (list[0][x] != list[y][x])
Darren Tucker909d8582010-01-08 19:02:40 +11001856 break;
1857
1858 matchlen = x;
1859 }
1860
1861 if (matchlen > strlen(word)) {
1862 char *tmp = xstrdup(list[0]);
1863
Darren Tucker340d1682010-01-09 08:54:31 +11001864 tmp[matchlen] = '\0';
Darren Tucker909d8582010-01-08 19:02:40 +11001865 return tmp;
1866 }
Damien Miller02e87802013-08-21 02:38:51 +10001867 }
Darren Tucker909d8582010-01-08 19:02:40 +11001868
1869 return xstrdup(word);
1870}
1871
1872/* Autocomplete a sftp command */
1873static int
1874complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1875 int terminated)
1876{
1877 u_int y, count = 0, cmdlen, tmplen;
1878 char *tmp, **list, argterm[3];
1879 const LineInfo *lf;
1880
1881 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1882
1883 /* No command specified: display all available commands */
1884 if (cmd == NULL) {
1885 for (y = 0; cmds[y].c; y++)
1886 list[count++] = xstrdup(cmds[y].c);
Damien Miller02e87802013-08-21 02:38:51 +10001887
Darren Tucker909d8582010-01-08 19:02:40 +11001888 list[count] = NULL;
1889 complete_display(list, 0);
1890
Damien Miller02e87802013-08-21 02:38:51 +10001891 for (y = 0; list[y] != NULL; y++)
1892 free(list[y]);
Darren Tuckera627d422013-06-02 07:31:17 +10001893 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001894 return count;
1895 }
1896
1897 /* Prepare subset of commands that start with "cmd" */
1898 cmdlen = strlen(cmd);
1899 for (y = 0; cmds[y].c; y++) {
Damien Miller02e87802013-08-21 02:38:51 +10001900 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
Darren Tucker909d8582010-01-08 19:02:40 +11001901 list[count++] = xstrdup(cmds[y].c);
1902 }
1903 list[count] = NULL;
1904
Damien Miller47d81152011-11-25 13:53:48 +11001905 if (count == 0) {
Darren Tuckera627d422013-06-02 07:31:17 +10001906 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001907 return 0;
Damien Miller47d81152011-11-25 13:53:48 +11001908 }
Darren Tucker909d8582010-01-08 19:02:40 +11001909
djm@openbsd.org001aa552018-04-10 00:10:49 +00001910 /* Complete ambiguous command */
Darren Tucker909d8582010-01-08 19:02:40 +11001911 tmp = complete_ambiguous(cmd, list, count);
1912 if (count > 1)
1913 complete_display(list, 0);
1914
Damien Miller02e87802013-08-21 02:38:51 +10001915 for (y = 0; list[y]; y++)
1916 free(list[y]);
Darren Tuckera627d422013-06-02 07:31:17 +10001917 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001918
1919 if (tmp != NULL) {
1920 tmplen = strlen(tmp);
1921 cmdlen = strlen(cmd);
1922 /* If cmd may be extended then do so */
1923 if (tmplen > cmdlen)
1924 if (el_insertstr(el, tmp + cmdlen) == -1)
1925 fatal("el_insertstr failed.");
1926 lf = el_line(el);
1927 /* Terminate argument cleanly */
1928 if (count == 1) {
1929 y = 0;
1930 if (!terminated)
1931 argterm[y++] = quote;
1932 if (lastarg || *(lf->cursor) != ' ')
1933 argterm[y++] = ' ';
1934 argterm[y] = '\0';
1935 if (y > 0 && el_insertstr(el, argterm) == -1)
1936 fatal("el_insertstr failed.");
1937 }
Darren Tuckera627d422013-06-02 07:31:17 +10001938 free(tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11001939 }
1940
1941 return count;
1942}
1943
1944/*
1945 * Determine whether a particular sftp command's arguments (if any)
1946 * represent local or remote files.
1947 */
1948static int
1949complete_is_remote(char *cmd) {
1950 int i;
1951
1952 if (cmd == NULL)
1953 return -1;
1954
1955 for (i = 0; cmds[i].c; i++) {
Damien Miller02e87802013-08-21 02:38:51 +10001956 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
Darren Tucker909d8582010-01-08 19:02:40 +11001957 return cmds[i].t;
1958 }
1959
1960 return -1;
1961}
1962
1963/* Autocomplete a filename "file" */
1964static int
1965complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1966 char *file, int remote, int lastarg, char quote, int terminated)
1967{
1968 glob_t g;
Darren Tuckerea647212013-06-06 08:19:09 +10001969 char *tmp, *tmp2, ins[8];
Darren Tucker17146d32012-10-05 10:46:16 +10001970 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
Darren Tuckerea647212013-06-06 08:19:09 +10001971 int clen;
Darren Tucker909d8582010-01-08 19:02:40 +11001972 const LineInfo *lf;
Damien Miller02e87802013-08-21 02:38:51 +10001973
Darren Tucker909d8582010-01-08 19:02:40 +11001974 /* Glob from "file" location */
1975 if (file == NULL)
1976 tmp = xstrdup("*");
1977 else
1978 xasprintf(&tmp, "%s*", file);
1979
Darren Tucker191fcc62012-10-05 10:45:01 +10001980 /* Check if the path is absolute. */
djm@openbsd.org2a358622018-11-16 03:26:01 +00001981 isabs = path_absolute(tmp);
Darren Tucker191fcc62012-10-05 10:45:01 +10001982
Darren Tucker909d8582010-01-08 19:02:40 +11001983 memset(&g, 0, sizeof(g));
1984 if (remote != LOCAL) {
1985 tmp = make_absolute(tmp, remote_path);
1986 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
Damien Miller02e87802013-08-21 02:38:51 +10001987 } else
Darren Tucker909d8582010-01-08 19:02:40 +11001988 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
Damien Miller02e87802013-08-21 02:38:51 +10001989
Darren Tucker909d8582010-01-08 19:02:40 +11001990 /* Determine length of pwd so we can trim completion display */
1991 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1992 /* Terminate counting on first unescaped glob metacharacter */
1993 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1994 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1995 hadglob = 1;
1996 break;
1997 }
1998 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1999 tmplen++;
2000 if (tmp[tmplen] == '/')
2001 pwdlen = tmplen + 1; /* track last seen '/' */
2002 }
Darren Tuckera627d422013-06-02 07:31:17 +10002003 free(tmp);
Damien Millerd7fd8be2014-05-15 14:24:59 +10002004 tmp = NULL;
Darren Tucker909d8582010-01-08 19:02:40 +11002005
Damien Miller02e87802013-08-21 02:38:51 +10002006 if (g.gl_matchc == 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002007 goto out;
2008
2009 if (g.gl_matchc > 1)
2010 complete_display(g.gl_pathv, pwdlen);
2011
Darren Tucker909d8582010-01-08 19:02:40 +11002012 /* Don't try to extend globs */
2013 if (file == NULL || hadglob)
2014 goto out;
2015
2016 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
Darren Tucker191fcc62012-10-05 10:45:01 +10002017 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
Darren Tuckera627d422013-06-02 07:31:17 +10002018 free(tmp2);
Darren Tucker909d8582010-01-08 19:02:40 +11002019
2020 if (tmp == NULL)
2021 goto out;
2022
2023 tmplen = strlen(tmp);
2024 filelen = strlen(file);
2025
Darren Tucker17146d32012-10-05 10:46:16 +10002026 /* Count the number of escaped characters in the input string. */
2027 cesc = isesc = 0;
2028 for (i = 0; i < filelen; i++) {
2029 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2030 isesc = 1;
2031 cesc++;
2032 } else
2033 isesc = 0;
2034 }
2035
2036 if (tmplen > (filelen - cesc)) {
2037 tmp2 = tmp + filelen - cesc;
Damien Miller02e87802013-08-21 02:38:51 +10002038 len = strlen(tmp2);
Darren Tucker909d8582010-01-08 19:02:40 +11002039 /* quote argument on way out */
Darren Tuckerea647212013-06-06 08:19:09 +10002040 for (i = 0; i < len; i += clen) {
2041 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2042 (size_t)clen > sizeof(ins) - 2)
2043 fatal("invalid multibyte character");
Darren Tucker909d8582010-01-08 19:02:40 +11002044 ins[0] = '\\';
Darren Tuckerea647212013-06-06 08:19:09 +10002045 memcpy(ins + 1, tmp2 + i, clen);
2046 ins[clen + 1] = '\0';
Darren Tucker909d8582010-01-08 19:02:40 +11002047 switch (tmp2[i]) {
2048 case '\'':
2049 case '"':
2050 case '\\':
2051 case '\t':
Darren Tuckerd78739a2010-10-24 10:56:32 +11002052 case '[':
Darren Tucker909d8582010-01-08 19:02:40 +11002053 case ' ':
Darren Tucker17146d32012-10-05 10:46:16 +10002054 case '#':
2055 case '*':
Darren Tucker909d8582010-01-08 19:02:40 +11002056 if (quote == '\0' || tmp2[i] == quote) {
2057 if (el_insertstr(el, ins) == -1)
2058 fatal("el_insertstr "
2059 "failed.");
2060 break;
2061 }
2062 /* FALLTHROUGH */
2063 default:
2064 if (el_insertstr(el, ins + 1) == -1)
2065 fatal("el_insertstr failed.");
2066 break;
2067 }
2068 }
2069 }
2070
2071 lf = el_line(el);
Darren Tucker909d8582010-01-08 19:02:40 +11002072 if (g.gl_matchc == 1) {
2073 i = 0;
Damien Miller38094812014-05-15 14:25:18 +10002074 if (!terminated && quote != '\0')
Darren Tucker909d8582010-01-08 19:02:40 +11002075 ins[i++] = quote;
Darren Tucker9c3ba072010-01-13 22:45:03 +11002076 if (*(lf->cursor - 1) != '/' &&
2077 (lastarg || *(lf->cursor) != ' '))
Darren Tucker909d8582010-01-08 19:02:40 +11002078 ins[i++] = ' ';
2079 ins[i] = '\0';
2080 if (i > 0 && el_insertstr(el, ins) == -1)
2081 fatal("el_insertstr failed.");
2082 }
Darren Tuckera627d422013-06-02 07:31:17 +10002083 free(tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11002084
2085 out:
2086 globfree(&g);
2087 return g.gl_matchc;
2088}
2089
2090/* tab-completion hook function, called via libedit */
2091static unsigned char
2092complete(EditLine *el, int ch)
2093{
Damien Miller02e87802013-08-21 02:38:51 +10002094 char **argv, *line, quote;
Damien Miller746d1a62013-07-18 16:13:02 +10002095 int argc, carg;
2096 u_int cursor, len, terminated, ret = CC_ERROR;
Darren Tucker909d8582010-01-08 19:02:40 +11002097 const LineInfo *lf;
2098 struct complete_ctx *complete_ctx;
2099
2100 lf = el_line(el);
2101 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2102 fatal("%s: el_get failed", __func__);
2103
2104 /* Figure out which argument the cursor points to */
2105 cursor = lf->cursor - lf->buffer;
deraadt@openbsd.orgce445b02015-08-20 22:32:42 +00002106 line = xmalloc(cursor + 1);
Darren Tucker909d8582010-01-08 19:02:40 +11002107 memcpy(line, lf->buffer, cursor);
2108 line[cursor] = '\0';
2109 argv = makeargv(line, &carg, 1, &quote, &terminated);
Darren Tuckera627d422013-06-02 07:31:17 +10002110 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002111
2112 /* Get all the arguments on the line */
2113 len = lf->lastchar - lf->buffer;
deraadt@openbsd.orgce445b02015-08-20 22:32:42 +00002114 line = xmalloc(len + 1);
Darren Tucker909d8582010-01-08 19:02:40 +11002115 memcpy(line, lf->buffer, len);
2116 line[len] = '\0';
2117 argv = makeargv(line, &argc, 1, NULL, NULL);
2118
2119 /* Ensure cursor is at EOL or a argument boundary */
2120 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2121 line[cursor] != '\n') {
Darren Tuckera627d422013-06-02 07:31:17 +10002122 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002123 return ret;
2124 }
2125
2126 if (carg == 0) {
2127 /* Show all available commands */
2128 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2129 ret = CC_REDISPLAY;
2130 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2131 /* Handle the command parsing */
2132 if (complete_cmd_parse(el, argv[0], argc == carg,
Damien Miller02e87802013-08-21 02:38:51 +10002133 quote, terminated) != 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002134 ret = CC_REDISPLAY;
2135 } else if (carg >= 1) {
2136 /* Handle file parsing */
2137 int remote = complete_is_remote(argv[0]);
2138 char *filematch = NULL;
2139
2140 if (carg > 1 && line[cursor-1] != ' ')
2141 filematch = argv[carg - 1];
2142
2143 if (remote != 0 &&
2144 complete_match(el, complete_ctx->conn,
2145 *complete_ctx->remote_pathp, filematch,
Damien Miller02e87802013-08-21 02:38:51 +10002146 remote, carg == argc, quote, terminated) != 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002147 ret = CC_REDISPLAY;
2148 }
2149
Damien Miller02e87802013-08-21 02:38:51 +10002150 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002151 return ret;
2152}
Darren Tuckere67f7db2010-01-08 19:50:02 +11002153#endif /* USE_LIBEDIT */
Darren Tucker909d8582010-01-08 19:02:40 +11002154
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002155static int
Darren Tucker21063192010-01-08 17:10:36 +11002156interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
Damien Miller20e1fab2004-02-18 14:30:55 +11002157{
Darren Tucker909d8582010-01-08 19:02:40 +11002158 char *remote_path;
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002159 char *dir = NULL, *startdir = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +11002160 char cmd[2048];
Damien Miller0e2c1022005-08-12 22:16:22 +10002161 int err, interactive;
Darren Tucker2d963d82004-11-07 20:04:10 +11002162 EditLine *el = NULL;
2163#ifdef USE_LIBEDIT
2164 History *hl = NULL;
2165 HistEvent hev;
2166 extern char *__progname;
Darren Tucker909d8582010-01-08 19:02:40 +11002167 struct complete_ctx complete_ctx;
Darren Tucker2d963d82004-11-07 20:04:10 +11002168
2169 if (!batchmode && isatty(STDIN_FILENO)) {
2170 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2171 fatal("Couldn't initialise editline");
2172 if ((hl = history_init()) == NULL)
2173 fatal("Couldn't initialise editline history");
2174 history(hl, &hev, H_SETSIZE, 100);
2175 el_set(el, EL_HIST, history, hl);
2176
2177 el_set(el, EL_PROMPT, prompt);
2178 el_set(el, EL_EDITOR, "emacs");
2179 el_set(el, EL_TERMINAL, NULL);
2180 el_set(el, EL_SIGNAL, 1);
2181 el_source(el, NULL);
Darren Tucker909d8582010-01-08 19:02:40 +11002182
2183 /* Tab Completion */
Damien Miller02e87802013-08-21 02:38:51 +10002184 el_set(el, EL_ADDFN, "ftp-complete",
Darren Tuckerd78739a2010-10-24 10:56:32 +11002185 "Context sensitive argument completion", complete);
Darren Tucker909d8582010-01-08 19:02:40 +11002186 complete_ctx.conn = conn;
2187 complete_ctx.remote_pathp = &remote_path;
2188 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2189 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
Damien Millere0ee7272013-08-21 02:42:35 +10002190 /* enable ctrl-left-arrow and ctrl-right-arrow */
2191 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2192 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2193 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2194 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
Damien Miller61353b32013-09-14 09:45:32 +10002195 /* make ^w match ksh behaviour */
2196 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
Darren Tucker2d963d82004-11-07 20:04:10 +11002197 }
2198#endif /* USE_LIBEDIT */
Damien Miller20e1fab2004-02-18 14:30:55 +11002199
Darren Tucker909d8582010-01-08 19:02:40 +11002200 remote_path = do_realpath(conn, ".");
2201 if (remote_path == NULL)
Damien Miller20e1fab2004-02-18 14:30:55 +11002202 fatal("Need cwd");
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002203 startdir = xstrdup(remote_path);
Damien Miller20e1fab2004-02-18 14:30:55 +11002204
2205 if (file1 != NULL) {
2206 dir = xstrdup(file1);
Darren Tucker909d8582010-01-08 19:02:40 +11002207 dir = make_absolute(dir, remote_path);
Damien Miller20e1fab2004-02-18 14:30:55 +11002208
2209 if (remote_is_dir(conn, dir) && file2 == NULL) {
Damien Miller9303e652013-04-23 15:22:40 +10002210 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00002211 mprintf("Changing to: %s\n", dir);
Damien Miller20e1fab2004-02-18 14:30:55 +11002212 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
Darren Tucker909d8582010-01-08 19:02:40 +11002213 if (parse_dispatch_command(conn, cmd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002214 &remote_path, startdir, 1, 0) != 0) {
Darren Tuckera627d422013-06-02 07:31:17 +10002215 free(dir);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002216 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002217 free(remote_path);
2218 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002219 return (-1);
Darren Tuckercd516ef2004-12-06 22:43:43 +11002220 }
Damien Miller20e1fab2004-02-18 14:30:55 +11002221 } else {
Darren Tucker0af24052012-10-05 10:41:25 +10002222 /* XXX this is wrong wrt quoting */
Damien Miller0d032412013-07-25 11:56:52 +10002223 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2224 global_aflag ? " -a" : "", dir,
2225 file2 == NULL ? "" : " ",
2226 file2 == NULL ? "" : file2);
Darren Tucker909d8582010-01-08 19:02:40 +11002227 err = parse_dispatch_command(conn, cmd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002228 &remote_path, startdir, 1, 0);
Darren Tuckera627d422013-06-02 07:31:17 +10002229 free(dir);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002230 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002231 free(remote_path);
2232 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002233 return (err);
2234 }
Darren Tuckera627d422013-06-02 07:31:17 +10002235 free(dir);
Damien Miller20e1fab2004-02-18 14:30:55 +11002236 }
2237
millert@openbsd.orgdb995f22014-11-26 18:34:51 +00002238 setvbuf(stdout, NULL, _IOLBF, 0);
2239 setvbuf(infile, NULL, _IOLBF, 0);
Damien Miller20e1fab2004-02-18 14:30:55 +11002240
Damien Miller0e2c1022005-08-12 22:16:22 +10002241 interactive = !batchmode && isatty(STDIN_FILENO);
Damien Miller20e1fab2004-02-18 14:30:55 +11002242 err = 0;
2243 for (;;) {
Darren Tuckercdf547a2004-05-24 10:12:19 +10002244 signal(SIGINT, SIG_IGN);
2245
Darren Tucker2d963d82004-11-07 20:04:10 +11002246 if (el == NULL) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002247 if (interactive)
2248 printf("sftp> ");
Darren Tucker2d963d82004-11-07 20:04:10 +11002249 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002250 if (interactive)
2251 printf("\n");
Darren Tucker2d963d82004-11-07 20:04:10 +11002252 break;
2253 }
Darren Tucker2d963d82004-11-07 20:04:10 +11002254 } else {
2255#ifdef USE_LIBEDIT
2256 const char *line;
2257 int count = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11002258
Darren Tucker909d8582010-01-08 19:02:40 +11002259 if ((line = el_gets(el, &count)) == NULL ||
2260 count <= 0) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002261 printf("\n");
2262 break;
2263 }
Darren Tucker2d963d82004-11-07 20:04:10 +11002264 history(hl, &hev, H_ENTER, line);
2265 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2266 fprintf(stderr, "Error: input line too long\n");
2267 continue;
2268 }
2269#endif /* USE_LIBEDIT */
Damien Miller20e1fab2004-02-18 14:30:55 +11002270 }
2271
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002272 cmd[strcspn(cmd, "\n")] = '\0';
Damien Miller20e1fab2004-02-18 14:30:55 +11002273
Darren Tuckercdf547a2004-05-24 10:12:19 +10002274 /* Handle user interrupts gracefully during commands */
2275 interrupted = 0;
2276 signal(SIGINT, cmd_interrupt);
2277
Darren Tucker909d8582010-01-08 19:02:40 +11002278 err = parse_dispatch_command(conn, cmd, &remote_path,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002279 startdir, batchmode, !interactive && el == NULL);
Damien Miller20e1fab2004-02-18 14:30:55 +11002280 if (err != 0)
2281 break;
2282 }
djm@openbsd.org3455f1e2018-04-13 05:04:12 +00002283 signal(SIGCHLD, SIG_DFL);
Darren Tuckera627d422013-06-02 07:31:17 +10002284 free(remote_path);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002285 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002286 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002287
Tim Rice027e8b12005-08-15 14:52:50 -07002288#ifdef USE_LIBEDIT
Damien Miller0e2c1022005-08-12 22:16:22 +10002289 if (el != NULL)
2290 el_end(el);
Tim Rice027e8b12005-08-15 14:52:50 -07002291#endif /* USE_LIBEDIT */
Damien Miller0e2c1022005-08-12 22:16:22 +10002292
Damien Miller20e1fab2004-02-18 14:30:55 +11002293 /* err == 1 signifies normal "quit" exit */
2294 return (err >= 0 ? 0 : -1);
2295}
Damien Miller62d57f62003-01-10 21:43:24 +11002296
Ben Lindstrombba81212001-06-25 05:01:22 +00002297static void
Damien Millercc685c12003-06-04 22:51:38 +10002298connect_to_server(char *path, char **args, int *in, int *out)
Damien Miller33804262001-02-04 23:20:18 +11002299{
2300 int c_in, c_out;
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002301
Damien Miller33804262001-02-04 23:20:18 +11002302#ifdef USE_PIPES
2303 int pin[2], pout[2];
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002304
Damien Miller33804262001-02-04 23:20:18 +11002305 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2306 fatal("pipe: %s", strerror(errno));
2307 *in = pin[0];
2308 *out = pout[1];
2309 c_in = pout[0];
2310 c_out = pin[1];
2311#else /* USE_PIPES */
2312 int inout[2];
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002313
Damien Miller33804262001-02-04 23:20:18 +11002314 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2315 fatal("socketpair: %s", strerror(errno));
2316 *in = *out = inout[0];
2317 c_in = c_out = inout[1];
2318#endif /* USE_PIPES */
2319
Damien Millercc685c12003-06-04 22:51:38 +10002320 if ((sshpid = fork()) == -1)
Damien Miller33804262001-02-04 23:20:18 +11002321 fatal("fork: %s", strerror(errno));
Damien Millercc685c12003-06-04 22:51:38 +10002322 else if (sshpid == 0) {
Damien Miller33804262001-02-04 23:20:18 +11002323 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2324 (dup2(c_out, STDOUT_FILENO) == -1)) {
2325 fprintf(stderr, "dup2: %s\n", strerror(errno));
Damien Miller350327c2004-06-15 10:24:13 +10002326 _exit(1);
Damien Miller33804262001-02-04 23:20:18 +11002327 }
2328 close(*in);
2329 close(*out);
2330 close(c_in);
2331 close(c_out);
Darren Tuckercdf547a2004-05-24 10:12:19 +10002332
2333 /*
2334 * The underlying ssh is in the same process group, so we must
Darren Tuckerfc959702004-07-17 16:12:08 +10002335 * ignore SIGINT if we want to gracefully abort commands,
2336 * otherwise the signal will make it to the ssh process and
Darren Tuckerb8b17e92010-01-15 11:46:03 +11002337 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2338 * underlying ssh, it must *not* ignore that signal.
Darren Tuckercdf547a2004-05-24 10:12:19 +10002339 */
2340 signal(SIGINT, SIG_IGN);
Darren Tuckerb8b17e92010-01-15 11:46:03 +11002341 signal(SIGTERM, SIG_DFL);
Darren Tuckerbd12f172004-06-18 16:23:43 +10002342 execvp(path, args);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002343 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
Damien Miller350327c2004-06-15 10:24:13 +10002344 _exit(1);
Damien Miller33804262001-02-04 23:20:18 +11002345 }
2346
Damien Millercc685c12003-06-04 22:51:38 +10002347 signal(SIGTERM, killchild);
2348 signal(SIGINT, killchild);
2349 signal(SIGHUP, killchild);
millert@openbsd.org2c6697c2016-10-18 12:41:22 +00002350 signal(SIGTSTP, suspchild);
2351 signal(SIGTTIN, suspchild);
2352 signal(SIGTTOU, suspchild);
djm@openbsd.org3455f1e2018-04-13 05:04:12 +00002353 signal(SIGCHLD, sigchld_handler);
Damien Miller33804262001-02-04 23:20:18 +11002354 close(c_in);
2355 close(c_out);
2356}
2357
Ben Lindstrombba81212001-06-25 05:01:22 +00002358static void
Damien Miller33804262001-02-04 23:20:18 +11002359usage(void)
2360{
Damien Miller025e01c2002-02-08 22:06:29 +11002361 extern char *__progname;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002362
Ben Lindstrom1e243242001-09-18 05:38:44 +00002363 fprintf(stderr,
djm@openbsd.org3575f0b2017-05-02 08:54:19 +00002364 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
Darren Tuckerc07138e2009-10-07 08:23:44 +11002365 " [-D sftp_server_path] [-F ssh_config] "
Damien Miller56883e12010-09-24 22:15:39 +10002366 "[-i identity_file] [-l limit]\n"
Darren Tuckerc07138e2009-10-07 08:23:44 +11002367 " [-o ssh_option] [-P port] [-R num_requests] "
2368 "[-S program]\n"
millert@openbsd.org887669e2017-10-21 23:06:24 +00002369 " [-s subsystem | sftp_server] destination\n",
2370 __progname);
Damien Miller33804262001-02-04 23:20:18 +11002371 exit(1);
2372}
2373
Kevin Stevesef4eea92001-02-05 12:42:17 +00002374int
Damien Miller33804262001-02-04 23:20:18 +11002375main(int argc, char **argv)
2376{
millert@openbsd.org887669e2017-10-21 23:06:24 +00002377 int in, out, ch, err, tmp, port = -1;
2378 char *host = NULL, *user, *cp, *file2 = NULL;
Ben Lindstrom387c4722001-05-08 20:27:25 +00002379 int debug_level = 0, sshver = 2;
2380 char *file1 = NULL, *sftp_server = NULL;
Damien Millerd14ee1e2002-02-05 12:27:31 +11002381 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
Damien Miller65e42f82010-09-24 22:15:11 +10002382 const char *errstr;
Ben Lindstrom387c4722001-05-08 20:27:25 +00002383 LogLevel ll = SYSLOG_LEVEL_INFO;
2384 arglist args;
Damien Millerd7686fd2001-02-10 00:40:03 +11002385 extern int optind;
2386 extern char *optarg;
Darren Tucker21063192010-01-08 17:10:36 +11002387 struct sftp_conn *conn;
2388 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2389 size_t num_requests = DEFAULT_NUM_REQUESTS;
Damien Miller65e42f82010-09-24 22:15:11 +10002390 long long limit_kbps = 0;
Damien Miller33804262001-02-04 23:20:18 +11002391
dtucker@openbsd.orgffb1e7e2016-02-15 09:47:49 +00002392 ssh_malloc_init(); /* must be called before any mallocs */
Darren Tuckerce321d82005-10-03 18:11:24 +10002393 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2394 sanitise_stdfd();
Damien Millerdda78a02016-12-12 13:57:10 +11002395 msetlocale();
Darren Tuckerce321d82005-10-03 18:11:24 +10002396
Damien Miller42c5ec42018-11-23 10:40:06 +11002397 seed_rng();
2398
Damien Miller59d3d5b2003-08-22 09:34:41 +10002399 __progname = ssh_get_progname(argv[0]);
Damien Miller3eec6b72006-01-31 21:49:27 +11002400 memset(&args, '\0', sizeof(args));
Ben Lindstrom387c4722001-05-08 20:27:25 +00002401 args.list = NULL;
Damien Miller2b5a0de2006-03-31 23:10:31 +11002402 addargs(&args, "%s", ssh_program);
Ben Lindstrom387c4722001-05-08 20:27:25 +00002403 addargs(&args, "-oForwardX11 no");
2404 addargs(&args, "-oForwardAgent no");
Damien Millerd27b9472005-12-13 19:29:02 +11002405 addargs(&args, "-oPermitLocalCommand no");
Ben Lindstrom2b7a0e92001-09-20 00:57:55 +00002406 addargs(&args, "-oClearAllForwardings yes");
Damien Millere4f5a822004-01-21 14:11:05 +11002407
Ben Lindstrom387c4722001-05-08 20:27:25 +00002408 ll = SYSLOG_LEVEL_INFO;
Damien Millere4f5a822004-01-21 14:11:05 +11002409 infile = stdin;
Damien Millerd7686fd2001-02-10 00:40:03 +11002410
Darren Tucker282b4022009-10-07 08:23:06 +11002411 while ((ch = getopt(argc, argv,
Damien Millerf29238e2013-10-17 11:48:52 +11002412 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
Damien Millerd7686fd2001-02-10 00:40:03 +11002413 switch (ch) {
Darren Tucker46bbbe32009-10-07 08:21:48 +11002414 /* Passed through to ssh(1) */
2415 case '4':
2416 case '6':
Damien Millerd7686fd2001-02-10 00:40:03 +11002417 case 'C':
Darren Tucker46bbbe32009-10-07 08:21:48 +11002418 addargs(&args, "-%c", ch);
2419 break;
2420 /* Passed through to ssh(1) with argument */
2421 case 'F':
2422 case 'c':
2423 case 'i':
2424 case 'o':
Darren Tuckerc4dc4f52010-01-08 18:50:04 +11002425 addargs(&args, "-%c", ch);
2426 addargs(&args, "%s", optarg);
Darren Tucker46bbbe32009-10-07 08:21:48 +11002427 break;
2428 case 'q':
Damien Miller9303e652013-04-23 15:22:40 +10002429 ll = SYSLOG_LEVEL_ERROR;
2430 quiet = 1;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002431 showprogress = 0;
2432 addargs(&args, "-%c", ch);
Damien Millerd7686fd2001-02-10 00:40:03 +11002433 break;
Darren Tucker282b4022009-10-07 08:23:06 +11002434 case 'P':
millert@openbsd.org887669e2017-10-21 23:06:24 +00002435 port = a2port(optarg);
2436 if (port <= 0)
2437 fatal("Bad port \"%s\"\n", optarg);
Darren Tucker282b4022009-10-07 08:23:06 +11002438 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002439 case 'v':
Ben Lindstrom387c4722001-05-08 20:27:25 +00002440 if (debug_level < 3) {
2441 addargs(&args, "-v");
2442 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2443 }
2444 debug_level++;
Damien Millerd7686fd2001-02-10 00:40:03 +11002445 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002446 case '1':
Ben Lindstrom387c4722001-05-08 20:27:25 +00002447 sshver = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +11002448 if (sftp_server == NULL)
2449 sftp_server = _PATH_SFTP_SERVER;
2450 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002451 case '2':
2452 sshver = 2;
Damien Millerd7686fd2001-02-10 00:40:03 +11002453 break;
Damien Miller0d032412013-07-25 11:56:52 +10002454 case 'a':
2455 global_aflag = 1;
2456 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002457 case 'B':
2458 copy_buffer_len = strtol(optarg, &cp, 10);
2459 if (copy_buffer_len == 0 || *cp != '\0')
2460 fatal("Invalid buffer size \"%s\"", optarg);
Damien Millerd7686fd2001-02-10 00:40:03 +11002461 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002462 case 'b':
Damien Miller44f75c12004-01-21 10:58:47 +11002463 if (batchmode)
2464 fatal("Batch file already specified.");
2465
2466 /* Allow "-" as stdin */
Darren Tuckerfc959702004-07-17 16:12:08 +10002467 if (strcmp(optarg, "-") != 0 &&
Damien Miller0dc1bef2005-07-17 17:22:45 +10002468 (infile = fopen(optarg, "r")) == NULL)
Damien Miller44f75c12004-01-21 10:58:47 +11002469 fatal("%s (%s).", strerror(errno), optarg);
Damien Miller62d57f62003-01-10 21:43:24 +11002470 showprogress = 0;
Damien Miller9303e652013-04-23 15:22:40 +10002471 quiet = batchmode = 1;
Damien Miller64e8d442005-03-01 21:16:47 +11002472 addargs(&args, "-obatchmode yes");
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002473 break;
Damien Millerf29238e2013-10-17 11:48:52 +11002474 case 'f':
2475 global_fflag = 1;
2476 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +11002477 case 'p':
2478 global_pflag = 1;
2479 break;
Darren Tucker282b4022009-10-07 08:23:06 +11002480 case 'D':
Damien Millerd14ee1e2002-02-05 12:27:31 +11002481 sftp_direct = optarg;
2482 break;
Damien Miller65e42f82010-09-24 22:15:11 +10002483 case 'l':
2484 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2485 &errstr);
2486 if (errstr != NULL)
2487 usage();
2488 limit_kbps *= 1024; /* kbps */
2489 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +11002490 case 'r':
2491 global_rflag = 1;
2492 break;
Damien Miller16a13332002-02-13 14:03:56 +11002493 case 'R':
2494 num_requests = strtol(optarg, &cp, 10);
2495 if (num_requests == 0 || *cp != '\0')
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002496 fatal("Invalid number of requests \"%s\"",
Damien Miller16a13332002-02-13 14:03:56 +11002497 optarg);
2498 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002499 case 's':
2500 sftp_server = optarg;
2501 break;
2502 case 'S':
2503 ssh_program = optarg;
2504 replacearg(&args, 0, "%s", ssh_program);
2505 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002506 case 'h':
2507 default:
Damien Miller33804262001-02-04 23:20:18 +11002508 usage();
2509 }
2510 }
2511
Damien Millerc0f27d82004-03-08 23:12:19 +11002512 if (!isatty(STDERR_FILENO))
2513 showprogress = 0;
2514
Ben Lindstrom2f3d52a2002-04-02 21:06:18 +00002515 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2516
Damien Millerd14ee1e2002-02-05 12:27:31 +11002517 if (sftp_direct == NULL) {
2518 if (optind == argc || argc > (optind + 2))
2519 usage();
millert@openbsd.org887669e2017-10-21 23:06:24 +00002520 argv += optind;
Damien Miller33804262001-02-04 23:20:18 +11002521
millert@openbsd.org887669e2017-10-21 23:06:24 +00002522 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2523 case -1:
2524 usage();
2525 break;
2526 case 0:
2527 if (tmp != -1)
2528 port = tmp;
2529 break;
2530 default:
2531 if (parse_user_host_path(*argv, &user, &host,
2532 &file1) == -1) {
2533 /* Treat as a plain hostname. */
2534 host = xstrdup(*argv);
2535 host = cleanhostname(host);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002536 }
millert@openbsd.org887669e2017-10-21 23:06:24 +00002537 break;
Damien Millerd14ee1e2002-02-05 12:27:31 +11002538 }
millert@openbsd.org887669e2017-10-21 23:06:24 +00002539 file2 = *(argv + 1);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002540
Damien Millerd14ee1e2002-02-05 12:27:31 +11002541 if (!*host) {
2542 fprintf(stderr, "Missing hostname\n");
Damien Miller33804262001-02-04 23:20:18 +11002543 usage();
2544 }
Damien Millerd14ee1e2002-02-05 12:27:31 +11002545
millert@openbsd.org887669e2017-10-21 23:06:24 +00002546 if (port != -1)
2547 addargs(&args, "-oPort %d", port);
2548 if (user != NULL) {
2549 addargs(&args, "-l");
2550 addargs(&args, "%s", user);
2551 }
Damien Millerd14ee1e2002-02-05 12:27:31 +11002552 addargs(&args, "-oProtocol %d", sshver);
2553
2554 /* no subsystem if the server-spec contains a '/' */
2555 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2556 addargs(&args, "-s");
2557
Darren Tuckerb8c884a2010-01-08 18:53:43 +11002558 addargs(&args, "--");
Damien Millerd14ee1e2002-02-05 12:27:31 +11002559 addargs(&args, "%s", host);
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002560 addargs(&args, "%s", (sftp_server != NULL ?
Damien Millerd14ee1e2002-02-05 12:27:31 +11002561 sftp_server : "sftp"));
Damien Millerd14ee1e2002-02-05 12:27:31 +11002562
Damien Millercc685c12003-06-04 22:51:38 +10002563 connect_to_server(ssh_program, args.list, &in, &out);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002564 } else {
2565 args.list = NULL;
2566 addargs(&args, "sftp-server");
2567
Damien Millercc685c12003-06-04 22:51:38 +10002568 connect_to_server(sftp_direct, args.list, &in, &out);
Damien Miller33804262001-02-04 23:20:18 +11002569 }
Damien Miller3eec6b72006-01-31 21:49:27 +11002570 freeargs(&args);
Damien Miller33804262001-02-04 23:20:18 +11002571
Damien Miller65e42f82010-09-24 22:15:11 +10002572 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
Darren Tucker21063192010-01-08 17:10:36 +11002573 if (conn == NULL)
2574 fatal("Couldn't initialise connection to server");
2575
Damien Miller9303e652013-04-23 15:22:40 +10002576 if (!quiet) {
Darren Tucker21063192010-01-08 17:10:36 +11002577 if (sftp_direct == NULL)
2578 fprintf(stderr, "Connected to %s.\n", host);
2579 else
2580 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2581 }
2582
2583 err = interactive_loop(conn, file1, file2);
Damien Miller33804262001-02-04 23:20:18 +11002584
Ben Lindstrom10b9bf92001-02-26 20:04:45 +00002585#if !defined(USE_PIPES)
Damien Miller37294fb2005-07-17 17:18:49 +10002586 shutdown(in, SHUT_RDWR);
2587 shutdown(out, SHUT_RDWR);
Ben Lindstrom10b9bf92001-02-26 20:04:45 +00002588#endif
2589
Damien Miller33804262001-02-04 23:20:18 +11002590 close(in);
2591 close(out);
Damien Miller44f75c12004-01-21 10:58:47 +11002592 if (batchmode)
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002593 fclose(infile);
Damien Miller33804262001-02-04 23:20:18 +11002594
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +00002595 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
Ben Lindstrom47fd8112002-04-02 20:48:19 +00002596 if (errno != EINTR)
2597 fatal("Couldn't wait for ssh process: %s",
2598 strerror(errno));
Damien Miller33804262001-02-04 23:20:18 +11002599
Damien Miller956f3fb2003-01-10 21:40:00 +11002600 exit(err == 0 ? 0 : 1);
Damien Miller33804262001-02-04 23:20:18 +11002601}