blob: 54538ff96316484a35214d6dd368bf8c9f01e7a1 [file] [log] [blame]
djm@openbsd.org4332b4f2019-11-01 03:54:33 +00001/* $OpenBSD: sftp.c,v 1.196 2019/11/01 03:54:33 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 Miller33804262001-02-04 23:20:18 +110056
Damien Millera7058ec2008-05-20 08:57:06 +100057#ifdef HAVE_UTIL_H
58# include <util.h>
59#endif
60
Damien Miller33804262001-02-04 23:20:18 +110061#include "xmalloc.h"
62#include "log.h"
63#include "pathnames.h"
Ben Lindstrom4529b702001-05-03 23:39:53 +000064#include "misc.h"
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +000065#include "utf8.h"
Damien Miller33804262001-02-04 23:20:18 +110066
67#include "sftp.h"
djm@openbsd.org7d845f42015-01-14 13:54:13 +000068#include "ssherr.h"
69#include "sshbuf.h"
Damien Miller33804262001-02-04 23:20:18 +110070#include "sftp-common.h"
71#include "sftp-client.h"
Damien Millerd7d46bb2004-02-18 14:11:13 +110072
Darren Tucker21063192010-01-08 17:10:36 +110073#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
74#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
75
Damien Miller20e1fab2004-02-18 14:30:55 +110076/* File to read commands from */
77FILE* infile;
78
79/* Are we in batchfile mode? */
80int batchmode = 0;
81
Damien Miller20e1fab2004-02-18 14:30:55 +110082/* PID of ssh transport process */
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +000083static volatile pid_t sshpid = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +110084
Damien Miller9303e652013-04-23 15:22:40 +100085/* Suppress diagnositic messages */
86int quiet = 0;
87
Damien Miller20e1fab2004-02-18 14:30:55 +110088/* This is set to 0 if the progressmeter is not desired. */
Damien Millerc0f27d82004-03-08 23:12:19 +110089int showprogress = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +110090
Darren Tucker1b0dd172009-10-07 08:37:48 +110091/* When this option is set, we always recursively download/upload directories */
92int global_rflag = 0;
93
Damien Millerd8accc02014-05-15 13:46:25 +100094/* When this option is set, we resume download or upload if possible */
Damien Miller0d032412013-07-25 11:56:52 +100095int global_aflag = 0;
96
Darren Tucker1b0dd172009-10-07 08:37:48 +110097/* When this option is set, the file transfers will always preserve times */
98int global_pflag = 0;
99
Damien Millerf29238e2013-10-17 11:48:52 +1100100/* When this option is set, transfers will have fsync() called on each file */
101int global_fflag = 0;
102
Darren Tuckercdf547a2004-05-24 10:12:19 +1000103/* SIGINT received during command processing */
104volatile sig_atomic_t interrupted = 0;
105
Darren Tuckerb9123452004-06-22 13:06:45 +1000106/* I wish qsort() took a separate ctx for the comparison function...*/
107int sort_flag;
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000108glob_t *sort_glob;
Darren Tuckerb9123452004-06-22 13:06:45 +1000109
Darren Tucker909d8582010-01-08 19:02:40 +1100110/* Context used for commandline completion */
111struct complete_ctx {
112 struct sftp_conn *conn;
113 char **remote_pathp;
114};
115
Damien Miller20e1fab2004-02-18 14:30:55 +1100116int remote_glob(struct sftp_conn *, const char *, int,
117 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
Damien Miller33804262001-02-04 23:20:18 +1100118
Kevin Steves12888d12001-03-05 19:50:57 +0000119extern char *__progname;
Kevin Steves12888d12001-03-05 19:50:57 +0000120
Damien Miller20e1fab2004-02-18 14:30:55 +1100121/* Separators for interactive commands */
122#define WHITESPACE " \t\r\n"
Damien Millerd7686fd2001-02-10 00:40:03 +1100123
Darren Tuckerb9123452004-06-22 13:06:45 +1000124/* ls flags */
Darren Tucker2901e2d2010-01-13 22:44:06 +1100125#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
126#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
127#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
128#define LS_NAME_SORT 0x0008 /* Sort by name (default) */
129#define LS_TIME_SORT 0x0010 /* Sort by mtime */
130#define LS_SIZE_SORT 0x0020 /* Sort by file size */
131#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
132#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
133#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
Darren Tuckerb9123452004-06-22 13:06:45 +1000134
Darren Tucker2901e2d2010-01-13 22:44:06 +1100135#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000136#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
Damien Miller20e1fab2004-02-18 14:30:55 +1100137
138/* Commands for interactive mode */
Damien Miller02e87802013-08-21 02:38:51 +1000139enum sftp_command {
140 I_CHDIR = 1,
141 I_CHGRP,
142 I_CHMOD,
143 I_CHOWN,
144 I_DF,
145 I_GET,
146 I_HELP,
147 I_LCHDIR,
148 I_LINK,
149 I_LLS,
150 I_LMKDIR,
151 I_LPWD,
152 I_LS,
153 I_LUMASK,
154 I_MKDIR,
155 I_PUT,
156 I_PWD,
157 I_QUIT,
Damien Millerb15cd7b2014-05-15 13:46:52 +1000158 I_REGET,
Damien Miller02e87802013-08-21 02:38:51 +1000159 I_RENAME,
Damien Millerb15cd7b2014-05-15 13:46:52 +1000160 I_REPUT,
Damien Miller02e87802013-08-21 02:38:51 +1000161 I_RM,
162 I_RMDIR,
163 I_SHELL,
164 I_SYMLINK,
165 I_VERSION,
166 I_PROGRESS,
Damien Miller02e87802013-08-21 02:38:51 +1000167};
Damien Miller20e1fab2004-02-18 14:30:55 +1100168
169struct CMD {
170 const char *c;
171 const int n;
Darren Tucker909d8582010-01-08 19:02:40 +1100172 const int t;
Damien Miller20e1fab2004-02-18 14:30:55 +1100173};
174
Darren Tucker909d8582010-01-08 19:02:40 +1100175/* Type of completion */
176#define NOARGS 0
177#define REMOTE 1
178#define LOCAL 2
179
Damien Miller20e1fab2004-02-18 14:30:55 +1100180static const struct CMD cmds[] = {
Darren Tucker909d8582010-01-08 19:02:40 +1100181 { "bye", I_QUIT, NOARGS },
182 { "cd", I_CHDIR, REMOTE },
183 { "chdir", I_CHDIR, REMOTE },
184 { "chgrp", I_CHGRP, REMOTE },
185 { "chmod", I_CHMOD, REMOTE },
186 { "chown", I_CHOWN, REMOTE },
187 { "df", I_DF, REMOTE },
188 { "dir", I_LS, REMOTE },
189 { "exit", I_QUIT, NOARGS },
190 { "get", I_GET, REMOTE },
191 { "help", I_HELP, NOARGS },
192 { "lcd", I_LCHDIR, LOCAL },
193 { "lchdir", I_LCHDIR, LOCAL },
194 { "lls", I_LLS, LOCAL },
195 { "lmkdir", I_LMKDIR, LOCAL },
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100196 { "ln", I_LINK, REMOTE },
Darren Tucker909d8582010-01-08 19:02:40 +1100197 { "lpwd", I_LPWD, LOCAL },
198 { "ls", I_LS, REMOTE },
199 { "lumask", I_LUMASK, NOARGS },
200 { "mkdir", I_MKDIR, REMOTE },
Damien Miller099fc162010-05-10 11:56:50 +1000201 { "mget", I_GET, REMOTE },
202 { "mput", I_PUT, LOCAL },
Darren Tucker909d8582010-01-08 19:02:40 +1100203 { "progress", I_PROGRESS, NOARGS },
204 { "put", I_PUT, LOCAL },
205 { "pwd", I_PWD, REMOTE },
206 { "quit", I_QUIT, NOARGS },
Damien Miller0d032412013-07-25 11:56:52 +1000207 { "reget", I_REGET, REMOTE },
Darren Tucker909d8582010-01-08 19:02:40 +1100208 { "rename", I_RENAME, REMOTE },
djm@openbsd.org4a459222014-10-06 00:47:15 +0000209 { "reput", I_REPUT, LOCAL },
Darren Tucker909d8582010-01-08 19:02:40 +1100210 { "rm", I_RM, REMOTE },
211 { "rmdir", I_RMDIR, REMOTE },
212 { "symlink", I_SYMLINK, REMOTE },
213 { "version", I_VERSION, NOARGS },
214 { "!", I_SHELL, NOARGS },
215 { "?", I_HELP, NOARGS },
216 { NULL, -1, -1 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100217};
218
Damien Millerb6c85fc2007-01-05 16:30:41 +1100219/* ARGSUSED */
Damien Miller20e1fab2004-02-18 14:30:55 +1100220static void
Darren Tuckercdf547a2004-05-24 10:12:19 +1000221killchild(int signo)
222{
djm@openbsd.org4332b4f2019-11-01 03:54:33 +0000223 pid_t pid;
224
225 pid = sshpid;
226 if (pid > 1) {
227 kill(pid, SIGTERM);
228 waitpid(pid, NULL, 0);
Darren Tuckerba66df82005-01-24 21:57:40 +1100229 }
Darren Tuckercdf547a2004-05-24 10:12:19 +1000230
231 _exit(1);
232}
233
Damien Millerb6c85fc2007-01-05 16:30:41 +1100234/* ARGSUSED */
Darren Tuckercdf547a2004-05-24 10:12:19 +1000235static void
millert@openbsd.org2c6697c2016-10-18 12:41:22 +0000236suspchild(int signo)
237{
238 if (sshpid > 1) {
239 kill(sshpid, signo);
240 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
241 continue;
242 }
243 kill(getpid(), SIGSTOP);
244}
245
246/* ARGSUSED */
247static void
Darren Tuckercdf547a2004-05-24 10:12:19 +1000248cmd_interrupt(int signo)
249{
250 const char msg[] = "\rInterrupt \n";
Darren Tuckere2f189a2004-12-06 22:45:53 +1100251 int olderrno = errno;
Darren Tuckercdf547a2004-05-24 10:12:19 +1000252
Darren Tuckerdbee3082013-05-16 20:32:29 +1000253 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
Darren Tuckercdf547a2004-05-24 10:12:19 +1000254 interrupted = 1;
Darren Tuckere2f189a2004-12-06 22:45:53 +1100255 errno = olderrno;
Darren Tuckercdf547a2004-05-24 10:12:19 +1000256}
257
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000258/*ARGSUSED*/
259static void
260sigchld_handler(int sig)
261{
262 int save_errno = errno;
263 pid_t pid;
264 const char msg[] = "\rConnection closed. \n";
265
266 /* Report if ssh transport process dies. */
267 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
268 continue;
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +0000269 if (pid == sshpid) {
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000270 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +0000271 sshpid = -1;
272 }
djm@openbsd.org3455f1e2018-04-13 05:04:12 +0000273
274 errno = save_errno;
275}
276
Darren Tuckercdf547a2004-05-24 10:12:19 +1000277static void
Damien Miller20e1fab2004-02-18 14:30:55 +1100278help(void)
279{
Damien Miller62fd18a2009-01-28 16:14:09 +1100280 printf("Available commands:\n"
281 "bye Quit sftp\n"
282 "cd path Change remote directory to 'path'\n"
djm@openbsd.org60d8c842019-01-16 23:23:45 +0000283 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
284 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
285 "chown [-h] own path Change owner of file 'path' to 'own'\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100286 "df [-hi] [path] Display statistics for current directory or\n"
287 " filesystem containing 'path'\n"
288 "exit Quit sftp\n"
jmc@openbsd.org5f68ab42019-06-19 20:12:44 +0000289 "get [-afpR] remote [local] Download 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"
jmc@openbsd.org5f68ab42019-06-19 20:12:44 +0000300 "put [-afpR] local [remote] Upload file\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100301 "pwd Display remote working directory\n"
302 "quit Quit sftp\n"
jmc@openbsd.org5f68ab42019-06-19 20:12:44 +0000303 "reget [-fpR] remote [local] Resume download file\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100304 "rename oldpath newpath Rename remote file\n"
jmc@openbsd.org5f68ab42019-06-19 20:12:44 +0000305 "reput [-fpR] local [remote] Resume upload file\n"
Damien Miller62fd18a2009-01-28 16:14:09 +1100306 "rm path Delete remote file\n"
307 "rmdir path Remove remote directory\n"
308 "symlink oldpath newpath Symlink remote file\n"
309 "version Show SFTP version\n"
310 "!command Execute 'command' in local shell\n"
311 "! Escape to local shell\n"
312 "? Synonym for help\n");
Damien Miller20e1fab2004-02-18 14:30:55 +1100313}
314
315static void
316local_do_shell(const char *args)
317{
318 int status;
319 char *shell;
320 pid_t pid;
321
322 if (!*args)
323 args = NULL;
324
Damien Miller38d9a962010-10-07 22:07:11 +1100325 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
Damien Miller20e1fab2004-02-18 14:30:55 +1100326 shell = _PATH_BSHELL;
327
328 if ((pid = fork()) == -1)
329 fatal("Couldn't fork: %s", strerror(errno));
330
331 if (pid == 0) {
332 /* XXX: child has pipe fds to ssh subproc open - issue? */
333 if (args) {
334 debug3("Executing %s -c \"%s\"", shell, args);
335 execl(shell, shell, "-c", args, (char *)NULL);
336 } else {
337 debug3("Executing %s", shell);
338 execl(shell, shell, (char *)NULL);
339 }
340 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
341 strerror(errno));
342 _exit(1);
343 }
344 while (waitpid(pid, &status, 0) == -1)
345 if (errno != EINTR)
346 fatal("Couldn't wait for child: %s", strerror(errno));
347 if (!WIFEXITED(status))
Damien Miller55b04f12006-03-26 14:23:17 +1100348 error("Shell exited abnormally");
Damien Miller20e1fab2004-02-18 14:30:55 +1100349 else if (WEXITSTATUS(status))
350 error("Shell exited with status %d", WEXITSTATUS(status));
351}
352
353static void
354local_do_ls(const char *args)
355{
356 if (!args || !*args)
357 local_do_shell(_PATH_LS);
358 else {
359 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
360 char *buf = xmalloc(len);
361
362 /* XXX: quoting - rip quoting code from ftp? */
363 snprintf(buf, len, _PATH_LS " %s", args);
364 local_do_shell(buf);
Darren Tuckera627d422013-06-02 07:31:17 +1000365 free(buf);
Damien Miller20e1fab2004-02-18 14:30:55 +1100366 }
367}
368
369/* Strip one path (usually the pwd) from the start of another */
370static char *
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000371path_strip(const char *path, const char *strip)
Damien Miller20e1fab2004-02-18 14:30:55 +1100372{
373 size_t len;
374
375 if (strip == NULL)
376 return (xstrdup(path));
377
378 len = strlen(strip);
Darren Tuckere2f189a2004-12-06 22:45:53 +1100379 if (strncmp(path, strip, len) == 0) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100380 if (strip[len - 1] != '/' && path[len] == '/')
381 len++;
382 return (xstrdup(path + len));
383 }
384
385 return (xstrdup(path));
386}
387
388static char *
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000389make_absolute(char *p, const char *pwd)
Damien Miller20e1fab2004-02-18 14:30:55 +1100390{
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000391 char *abs_str;
Damien Miller20e1fab2004-02-18 14:30:55 +1100392
393 /* Derelativise */
djm@openbsd.org2a358622018-11-16 03:26:01 +0000394 if (p && !path_absolute(p)) {
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000395 abs_str = path_append(pwd, p);
Darren Tuckera627d422013-06-02 07:31:17 +1000396 free(p);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000397 return(abs_str);
Damien Miller20e1fab2004-02-18 14:30:55 +1100398 } else
399 return(p);
400}
401
402static int
Damien Miller0d032412013-07-25 11:56:52 +1000403parse_getput_flags(const char *cmd, char **argv, int argc,
Damien Millerf29238e2013-10-17 11:48:52 +1100404 int *aflag, int *fflag, int *pflag, int *rflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100405{
Damien Millerf184bcf2008-06-29 22:45:13 +1000406 extern int opterr, optind, optopt, optreset;
Damien Miller1cbc2922007-10-26 14:27:45 +1000407 int ch;
Damien Miller20e1fab2004-02-18 14:30:55 +1100408
Damien Miller1cbc2922007-10-26 14:27:45 +1000409 optind = optreset = 1;
410 opterr = 0;
411
Damien Millerf29238e2013-10-17 11:48:52 +1100412 *aflag = *fflag = *rflag = *pflag = 0;
413 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
Damien Miller1cbc2922007-10-26 14:27:45 +1000414 switch (ch) {
Damien Miller0d032412013-07-25 11:56:52 +1000415 case 'a':
416 *aflag = 1;
417 break;
Damien Millerf29238e2013-10-17 11:48:52 +1100418 case 'f':
419 *fflag = 1;
420 break;
Damien Miller20e1fab2004-02-18 14:30:55 +1100421 case 'p':
422 case 'P':
423 *pflag = 1;
424 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100425 case 'r':
426 case 'R':
427 *rflag = 1;
428 break;
Damien Miller20e1fab2004-02-18 14:30:55 +1100429 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000430 error("%s: Invalid flag -%c", cmd, optopt);
Damien Miller1cbc2922007-10-26 14:27:45 +1000431 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100432 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100433 }
434
Damien Miller1cbc2922007-10-26 14:27:45 +1000435 return optind;
Damien Miller20e1fab2004-02-18 14:30:55 +1100436}
437
438static int
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100439parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
440{
441 extern int opterr, optind, optopt, optreset;
442 int ch;
443
444 optind = optreset = 1;
445 opterr = 0;
446
447 *sflag = 0;
448 while ((ch = getopt(argc, argv, "s")) != -1) {
449 switch (ch) {
450 case 's':
451 *sflag = 1;
452 break;
453 default:
454 error("%s: Invalid flag -%c", cmd, optopt);
455 return -1;
456 }
457 }
458
459 return optind;
460}
461
462static int
Damien Millerc7dba122013-08-21 02:41:15 +1000463parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
464{
465 extern int opterr, optind, optopt, optreset;
466 int ch;
467
468 optind = optreset = 1;
469 opterr = 0;
470
471 *lflag = 0;
472 while ((ch = getopt(argc, argv, "l")) != -1) {
473 switch (ch) {
474 case 'l':
475 *lflag = 1;
476 break;
477 default:
478 error("%s: Invalid flag -%c", cmd, optopt);
479 return -1;
480 }
481 }
482
483 return optind;
484}
485
486static int
Damien Miller1cbc2922007-10-26 14:27:45 +1000487parse_ls_flags(char **argv, int argc, int *lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100488{
Damien Millerf184bcf2008-06-29 22:45:13 +1000489 extern int opterr, optind, optopt, optreset;
Damien Miller1cbc2922007-10-26 14:27:45 +1000490 int ch;
Damien Miller20e1fab2004-02-18 14:30:55 +1100491
Damien Miller1cbc2922007-10-26 14:27:45 +1000492 optind = optreset = 1;
493 opterr = 0;
494
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000495 *lflag = LS_NAME_SORT;
Darren Tucker2901e2d2010-01-13 22:44:06 +1100496 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
Damien Miller1cbc2922007-10-26 14:27:45 +1000497 switch (ch) {
498 case '1':
499 *lflag &= ~VIEW_FLAGS;
500 *lflag |= LS_SHORT_VIEW;
501 break;
502 case 'S':
503 *lflag &= ~SORT_FLAGS;
504 *lflag |= LS_SIZE_SORT;
505 break;
506 case 'a':
507 *lflag |= LS_SHOW_ALL;
508 break;
509 case 'f':
510 *lflag &= ~SORT_FLAGS;
511 break;
Darren Tucker2901e2d2010-01-13 22:44:06 +1100512 case 'h':
513 *lflag |= LS_SI_UNITS;
514 break;
Damien Miller1cbc2922007-10-26 14:27:45 +1000515 case 'l':
Darren Tucker2901e2d2010-01-13 22:44:06 +1100516 *lflag &= ~LS_SHORT_VIEW;
Damien Miller1cbc2922007-10-26 14:27:45 +1000517 *lflag |= LS_LONG_VIEW;
518 break;
519 case 'n':
Darren Tucker2901e2d2010-01-13 22:44:06 +1100520 *lflag &= ~LS_SHORT_VIEW;
Damien Miller1cbc2922007-10-26 14:27:45 +1000521 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
522 break;
523 case 'r':
524 *lflag |= LS_REVERSE_SORT;
525 break;
526 case 't':
527 *lflag &= ~SORT_FLAGS;
528 *lflag |= LS_TIME_SORT;
529 break;
530 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000531 error("ls: Invalid flag -%c", optopt);
Damien Miller1cbc2922007-10-26 14:27:45 +1000532 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100533 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100534 }
535
Damien Miller1cbc2922007-10-26 14:27:45 +1000536 return optind;
Damien Miller20e1fab2004-02-18 14:30:55 +1100537}
538
539static int
Damien Millerd671e5a2008-05-19 14:53:33 +1000540parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
541{
Damien Millerf184bcf2008-06-29 22:45:13 +1000542 extern int opterr, optind, optopt, optreset;
Damien Millerd671e5a2008-05-19 14:53:33 +1000543 int ch;
544
545 optind = optreset = 1;
546 opterr = 0;
547
548 *hflag = *iflag = 0;
549 while ((ch = getopt(argc, argv, "hi")) != -1) {
550 switch (ch) {
551 case 'h':
552 *hflag = 1;
553 break;
554 case 'i':
555 *iflag = 1;
556 break;
557 default:
Damien Millerf184bcf2008-06-29 22:45:13 +1000558 error("%s: Invalid flag -%c", cmd, optopt);
Damien Millerd671e5a2008-05-19 14:53:33 +1000559 return -1;
560 }
561 }
562
563 return optind;
564}
565
566static int
djm@openbsd.org60d8c842019-01-16 23:23:45 +0000567parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
568{
569 extern int opterr, optind, optopt, optreset;
570 int ch;
571
572 optind = optreset = 1;
573 opterr = 0;
574
575 *hflag = 0;
576 while ((ch = getopt(argc, argv, "h")) != -1) {
577 switch (ch) {
578 case 'h':
579 *hflag = 1;
580 break;
581 default:
582 error("%s: Invalid flag -%c", cmd, optopt);
583 return -1;
584 }
585 }
586
587 return optind;
588}
589
590static int
Damien Miller036d3072013-08-21 02:41:46 +1000591parse_no_flags(const char *cmd, char **argv, int argc)
592{
593 extern int opterr, optind, optopt, optreset;
594 int ch;
595
596 optind = optreset = 1;
597 opterr = 0;
598
599 while ((ch = getopt(argc, argv, "")) != -1) {
600 switch (ch) {
601 default:
602 error("%s: Invalid flag -%c", cmd, optopt);
603 return -1;
604 }
605 }
606
607 return optind;
608}
609
610static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000611is_dir(const char *path)
Damien Miller20e1fab2004-02-18 14:30:55 +1100612{
613 struct stat sb;
614
615 /* XXX: report errors? */
616 if (stat(path, &sb) == -1)
617 return(0);
618
Darren Tucker1e80e402006-09-21 12:59:33 +1000619 return(S_ISDIR(sb.st_mode));
Damien Miller20e1fab2004-02-18 14:30:55 +1100620}
621
622static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000623remote_is_dir(struct sftp_conn *conn, const char *path)
Damien Miller20e1fab2004-02-18 14:30:55 +1100624{
625 Attrib *a;
626
627 /* XXX: report errors? */
628 if ((a = do_stat(conn, path, 1)) == NULL)
629 return(0);
630 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
631 return(0);
Darren Tucker1e80e402006-09-21 12:59:33 +1000632 return(S_ISDIR(a->perm));
Damien Miller20e1fab2004-02-18 14:30:55 +1100633}
634
Darren Tucker1b0dd172009-10-07 08:37:48 +1100635/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
Damien Miller20e1fab2004-02-18 14:30:55 +1100636static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000637pathname_is_dir(const char *pathname)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100638{
639 size_t l = strlen(pathname);
640
641 return l > 0 && pathname[l - 1] == '/';
642}
643
644static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000645process_get(struct sftp_conn *conn, const char *src, const char *dst,
646 const char *pwd, int pflag, int rflag, int resume, int fflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100647{
648 char *abs_src = NULL;
649 char *abs_dst = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +1100650 glob_t g;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100651 char *filename, *tmp=NULL;
Damien Miller00707762014-07-09 13:07:06 +1000652 int i, r, err = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +1100653
654 abs_src = xstrdup(src);
655 abs_src = make_absolute(abs_src, pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +1100656 memset(&g, 0, sizeof(g));
Darren Tucker1b0dd172009-10-07 08:37:48 +1100657
Damien Miller20e1fab2004-02-18 14:30:55 +1100658 debug3("Looking up %s", abs_src);
Damien Miller00707762014-07-09 13:07:06 +1000659 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
660 if (r == GLOB_NOSPACE) {
661 error("Too many matches for \"%s\".", abs_src);
662 } else {
663 error("File \"%s\" not found.", abs_src);
664 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100665 err = -1;
666 goto out;
667 }
668
Darren Tucker1b0dd172009-10-07 08:37:48 +1100669 /*
670 * If multiple matches then dst must be a directory or
671 * unspecified.
672 */
673 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
674 error("Multiple source paths, but destination "
675 "\"%s\" is not a directory", dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100676 err = -1;
677 goto out;
678 }
679
Darren Tuckercdf547a2004-05-24 10:12:19 +1000680 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100681 tmp = xstrdup(g.gl_pathv[i]);
682 if ((filename = basename(tmp)) == NULL) {
683 error("basename %s: %s", tmp, strerror(errno));
Darren Tuckera627d422013-06-02 07:31:17 +1000684 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100685 err = -1;
686 goto out;
687 }
688
689 if (g.gl_matchc == 1 && dst) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100690 if (is_dir(dst)) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100691 abs_dst = path_append(dst, filename);
692 } else {
Damien Miller20e1fab2004-02-18 14:30:55 +1100693 abs_dst = xstrdup(dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100694 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100695 } else if (dst) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100696 abs_dst = path_append(dst, filename);
697 } else {
698 abs_dst = xstrdup(filename);
699 }
Darren Tuckera627d422013-06-02 07:31:17 +1000700 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100701
Damien Miller0d032412013-07-25 11:56:52 +1000702 resume |= global_aflag;
703 if (!quiet && resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000704 mprintf("Resuming %s to %s\n",
705 g.gl_pathv[i], abs_dst);
Damien Miller0d032412013-07-25 11:56:52 +1000706 else if (!quiet && !resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000707 mprintf("Fetching %s to %s\n",
708 g.gl_pathv[i], abs_dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100709 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
Damien Miller0d032412013-07-25 11:56:52 +1000710 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
Damien Millerf29238e2013-10-17 11:48:52 +1100711 pflag || global_pflag, 1, resume,
712 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100713 err = -1;
714 } else {
715 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
Damien Millerf29238e2013-10-17 11:48:52 +1100716 pflag || global_pflag, resume,
717 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100718 err = -1;
719 }
Darren Tuckera627d422013-06-02 07:31:17 +1000720 free(abs_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100721 abs_dst = NULL;
722 }
723
724out:
Darren Tuckera627d422013-06-02 07:31:17 +1000725 free(abs_src);
Damien Miller20e1fab2004-02-18 14:30:55 +1100726 globfree(&g);
727 return(err);
728}
729
730static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000731process_put(struct sftp_conn *conn, const char *src, const char *dst,
732 const char *pwd, int pflag, int rflag, int resume, int fflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100733{
734 char *tmp_dst = NULL;
735 char *abs_dst = NULL;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100736 char *tmp = NULL, *filename = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +1100737 glob_t g;
738 int err = 0;
Darren Tucker1b0dd172009-10-07 08:37:48 +1100739 int i, dst_is_dir = 1;
Damien Milleraec5cf82008-02-10 22:26:24 +1100740 struct stat sb;
Damien Miller20e1fab2004-02-18 14:30:55 +1100741
742 if (dst) {
743 tmp_dst = xstrdup(dst);
744 tmp_dst = make_absolute(tmp_dst, pwd);
745 }
746
747 memset(&g, 0, sizeof(g));
748 debug3("Looking up %s", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100749 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100750 error("File \"%s\" not found.", src);
751 err = -1;
752 goto out;
753 }
754
Darren Tucker1b0dd172009-10-07 08:37:48 +1100755 /* If we aren't fetching to pwd then stash this status for later */
756 if (tmp_dst != NULL)
757 dst_is_dir = remote_is_dir(conn, tmp_dst);
758
Damien Miller20e1fab2004-02-18 14:30:55 +1100759 /* If multiple matches, dst may be directory or unspecified */
Darren Tucker1b0dd172009-10-07 08:37:48 +1100760 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
761 error("Multiple paths match, but destination "
762 "\"%s\" is not a directory", tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100763 err = -1;
764 goto out;
765 }
766
Darren Tuckercdf547a2004-05-24 10:12:19 +1000767 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Milleraec5cf82008-02-10 22:26:24 +1100768 if (stat(g.gl_pathv[i], &sb) == -1) {
769 err = -1;
770 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
771 continue;
772 }
Damien Miller02e87802013-08-21 02:38:51 +1000773
Darren Tucker1b0dd172009-10-07 08:37:48 +1100774 tmp = xstrdup(g.gl_pathv[i]);
775 if ((filename = basename(tmp)) == NULL) {
776 error("basename %s: %s", tmp, strerror(errno));
Darren Tuckera627d422013-06-02 07:31:17 +1000777 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100778 err = -1;
779 goto out;
780 }
781
782 if (g.gl_matchc == 1 && tmp_dst) {
783 /* If directory specified, append filename */
Darren Tucker1b0dd172009-10-07 08:37:48 +1100784 if (dst_is_dir)
785 abs_dst = path_append(tmp_dst, filename);
786 else
Damien Miller20e1fab2004-02-18 14:30:55 +1100787 abs_dst = xstrdup(tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100788 } else if (tmp_dst) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100789 abs_dst = path_append(tmp_dst, filename);
790 } else {
791 abs_dst = make_absolute(xstrdup(filename), pwd);
792 }
Darren Tuckera627d422013-06-02 07:31:17 +1000793 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100794
Damien Millerd8accc02014-05-15 13:46:25 +1000795 resume |= global_aflag;
796 if (!quiet && resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000797 mprintf("Resuming upload of %s to %s\n",
798 g.gl_pathv[i], abs_dst);
Damien Millerd8accc02014-05-15 13:46:25 +1000799 else if (!quiet && !resume)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000800 mprintf("Uploading %s to %s\n",
801 g.gl_pathv[i], abs_dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +1100802 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
803 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
Damien Millerd8accc02014-05-15 13:46:25 +1000804 pflag || global_pflag, 1, resume,
Damien Millerf29238e2013-10-17 11:48:52 +1100805 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100806 err = -1;
807 } else {
808 if (do_upload(conn, g.gl_pathv[i], abs_dst,
Damien Millerd8accc02014-05-15 13:46:25 +1000809 pflag || global_pflag, resume,
Damien Millerf29238e2013-10-17 11:48:52 +1100810 fflag || global_fflag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +1100811 err = -1;
812 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100813 }
814
815out:
Darren Tuckera627d422013-06-02 07:31:17 +1000816 free(abs_dst);
817 free(tmp_dst);
Damien Miller20e1fab2004-02-18 14:30:55 +1100818 globfree(&g);
819 return(err);
820}
821
822static int
823sdirent_comp(const void *aa, const void *bb)
824{
825 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
826 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000827 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100828
Darren Tuckerb9123452004-06-22 13:06:45 +1000829#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000830 if (sort_flag & LS_NAME_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000831 return (rmul * strcmp(a->filename, b->filename));
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000832 else if (sort_flag & LS_TIME_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000833 return (rmul * NCMP(a->a.mtime, b->a.mtime));
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000834 else if (sort_flag & LS_SIZE_SORT)
Darren Tuckerb9123452004-06-22 13:06:45 +1000835 return (rmul * NCMP(a->a.size, b->a.size));
836
837 fatal("Unknown ls sort type");
Damien Miller20e1fab2004-02-18 14:30:55 +1100838}
839
840/* sftp ls.1 replacement for directories */
841static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000842do_ls_dir(struct sftp_conn *conn, const char *path,
843 const char *strip_path, int lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100844{
Damien Millereccb9de2005-06-17 12:59:34 +1000845 int n;
846 u_int c = 1, colspace = 0, columns = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100847 SFTP_DIRENT **d;
848
849 if ((n = do_readdir(conn, path, &d)) != 0)
850 return (n);
851
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000852 if (!(lflag & LS_SHORT_VIEW)) {
Damien Millereccb9de2005-06-17 12:59:34 +1000853 u_int m = 0, width = 80;
Damien Miller20e1fab2004-02-18 14:30:55 +1100854 struct winsize ws;
855 char *tmp;
856
857 /* Count entries for sort and find longest filename */
Darren Tucker9a526452004-06-22 13:09:55 +1000858 for (n = 0; d[n] != NULL; n++) {
859 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000860 m = MAXIMUM(m, strlen(d[n]->filename));
Darren Tucker9a526452004-06-22 13:09:55 +1000861 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100862
863 /* Add any subpath that also needs to be counted */
864 tmp = path_strip(path, strip_path);
865 m += strlen(tmp);
Darren Tuckera627d422013-06-02 07:31:17 +1000866 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100867
868 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
869 width = ws.ws_col;
870
871 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000872 columns = MAXIMUM(columns, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +1100873 colspace = width / columns;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000874 colspace = MINIMUM(colspace, width);
Damien Miller20e1fab2004-02-18 14:30:55 +1100875 }
876
Darren Tuckerb9123452004-06-22 13:06:45 +1000877 if (lflag & SORT_FLAGS) {
Damien Miller653b93b2005-11-05 15:15:23 +1100878 for (n = 0; d[n] != NULL; n++)
879 ; /* count entries */
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000880 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
Darren Tuckerb9123452004-06-22 13:06:45 +1000881 qsort(d, n, sizeof(*d), sdirent_comp);
882 }
Damien Miller20e1fab2004-02-18 14:30:55 +1100883
Darren Tuckercdf547a2004-05-24 10:12:19 +1000884 for (n = 0; d[n] != NULL && !interrupted; n++) {
Damien Miller20e1fab2004-02-18 14:30:55 +1100885 char *tmp, *fname;
886
Darren Tucker9a526452004-06-22 13:09:55 +1000887 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
888 continue;
889
Damien Miller20e1fab2004-02-18 14:30:55 +1100890 tmp = path_append(path, d[n]->filename);
891 fname = path_strip(tmp, strip_path);
Darren Tuckera627d422013-06-02 07:31:17 +1000892 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +1100893
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000894 if (lflag & LS_LONG_VIEW) {
Darren Tucker2901e2d2010-01-13 22:44:06 +1100895 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000896 char *lname;
897 struct stat sb;
Damien Miller20e1fab2004-02-18 14:30:55 +1100898
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000899 memset(&sb, 0, sizeof(sb));
900 attrib_to_stat(&d[n]->a, &sb);
Darren Tucker2901e2d2010-01-13 22:44:06 +1100901 lname = ls_file(fname, &sb, 1,
902 (lflag & LS_SI_UNITS));
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000903 mprintf("%s\n", lname);
Darren Tuckera627d422013-06-02 07:31:17 +1000904 free(lname);
Darren Tuckerb215c5d2004-06-22 12:30:53 +1000905 } else
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000906 mprintf("%s\n", d[n]->longname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100907 } else {
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000908 mprintf("%-*s", colspace, fname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100909 if (c >= columns) {
910 printf("\n");
911 c = 1;
912 } else
913 c++;
914 }
915
Darren Tuckera627d422013-06-02 07:31:17 +1000916 free(fname);
Damien Miller20e1fab2004-02-18 14:30:55 +1100917 }
918
Darren Tuckera4e9ffa2004-06-22 13:07:58 +1000919 if (!(lflag & LS_LONG_VIEW) && (c != 1))
Damien Miller20e1fab2004-02-18 14:30:55 +1100920 printf("\n");
921
922 free_sftp_dirents(d);
923 return (0);
924}
925
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000926static int
927sglob_comp(const void *aa, const void *bb)
928{
929 u_int a = *(const u_int *)aa;
930 u_int b = *(const u_int *)bb;
931 const char *ap = sort_glob->gl_pathv[a];
932 const char *bp = sort_glob->gl_pathv[b];
933 const struct stat *as = sort_glob->gl_statv[a];
934 const struct stat *bs = sort_glob->gl_statv[b];
935 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
936
937#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
938 if (sort_flag & LS_NAME_SORT)
939 return (rmul * strcmp(ap, bp));
Damien Millerbcd14852017-06-10 23:41:25 +1000940 else if (sort_flag & LS_TIME_SORT) {
941#if defined(HAVE_STRUCT_STAT_ST_MTIM)
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000942 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
Damien Millerbcd14852017-06-10 23:41:25 +1000943#elif defined(HAVE_STRUCT_STAT_ST_MTIME)
944 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
945#else
946 return rmul * 1;
947#endif
948 } else if (sort_flag & LS_SIZE_SORT)
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000949 return (rmul * NCMP(as->st_size, bs->st_size));
950
951 fatal("Unknown ls sort type");
952}
953
Damien Miller20e1fab2004-02-18 14:30:55 +1100954/* sftp ls.1 replacement which handles path globs */
955static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +0000956do_globbed_ls(struct sftp_conn *conn, const char *path,
957 const char *strip_path, int lflag)
Damien Miller20e1fab2004-02-18 14:30:55 +1100958{
Damien Millera6e121a2010-10-07 21:39:17 +1100959 char *fname, *lname;
Damien Miller68e2e562010-10-07 21:39:55 +1100960 glob_t g;
Damien Miller00707762014-07-09 13:07:06 +1000961 int err, r;
Damien Miller68e2e562010-10-07 21:39:55 +1100962 struct winsize ws;
djm@openbsd.org72be5b22017-06-10 06:33:34 +0000963 u_int i, j, nentries, *indices = NULL, c = 1;
964 u_int colspace = 0, columns = 1, m = 0, width = 80;
Damien Miller20e1fab2004-02-18 14:30:55 +1100965
966 memset(&g, 0, sizeof(g));
967
Damien Miller00707762014-07-09 13:07:06 +1000968 if ((r = remote_glob(conn, path,
Damien Millerd7be70d2011-09-22 21:43:06 +1000969 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
Damien Miller00707762014-07-09 13:07:06 +1000970 NULL, &g)) != 0 ||
Damien Millera6e121a2010-10-07 21:39:17 +1100971 (g.gl_pathc && !g.gl_matchc)) {
Darren Tucker596dcfa2004-12-11 13:37:22 +1100972 if (g.gl_pathc)
973 globfree(&g);
Damien Miller00707762014-07-09 13:07:06 +1000974 if (r == GLOB_NOSPACE) {
975 error("Can't ls: Too many matches for \"%s\"", path);
976 } else {
977 error("Can't ls: \"%s\" not found", path);
978 }
Damien Millera6e121a2010-10-07 21:39:17 +1100979 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +1100980 }
981
Darren Tuckercdf547a2004-05-24 10:12:19 +1000982 if (interrupted)
983 goto out;
984
Damien Miller20e1fab2004-02-18 14:30:55 +1100985 /*
Darren Tucker596dcfa2004-12-11 13:37:22 +1100986 * If the glob returns a single match and it is a directory,
987 * then just list its contents.
Damien Miller20e1fab2004-02-18 14:30:55 +1100988 */
Damien Millera6e121a2010-10-07 21:39:17 +1100989 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
990 S_ISDIR(g.gl_statv[0]->st_mode)) {
991 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
992 globfree(&g);
993 return err;
Damien Miller20e1fab2004-02-18 14:30:55 +1100994 }
995
Damien Miller68e2e562010-10-07 21:39:55 +1100996 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
997 width = ws.ws_col;
Damien Miller20e1fab2004-02-18 14:30:55 +1100998
Damien Miller68e2e562010-10-07 21:39:55 +1100999 if (!(lflag & LS_SHORT_VIEW)) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001000 /* Count entries for sort and find longest filename */
1001 for (i = 0; g.gl_pathv[i]; i++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001002 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
Damien Miller20e1fab2004-02-18 14:30:55 +11001003
Damien Miller20e1fab2004-02-18 14:30:55 +11001004 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001005 columns = MAXIMUM(columns, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001006 colspace = width / columns;
1007 }
1008
djm@openbsd.org72be5b22017-06-10 06:33:34 +00001009 /*
1010 * Sorting: rather than mess with the contents of glob_t, prepare
1011 * an array of indices into it and sort that. For the usual
1012 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1013 */
1014 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1015 ; /* count entries */
1016 indices = calloc(nentries, sizeof(*indices));
1017 for (i = 0; i < nentries; i++)
1018 indices[i] = i;
1019
1020 if (lflag & SORT_FLAGS) {
1021 sort_glob = &g;
1022 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1023 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1024 sort_glob = NULL;
1025 }
1026
1027 for (j = 0; j < nentries && !interrupted; j++) {
1028 i = indices[j];
Damien Miller20e1fab2004-02-18 14:30:55 +11001029 fname = path_strip(g.gl_pathv[i], strip_path);
Darren Tuckera4e9ffa2004-06-22 13:07:58 +10001030 if (lflag & LS_LONG_VIEW) {
Damien Millera6e121a2010-10-07 21:39:17 +11001031 if (g.gl_statv[i] == NULL) {
1032 error("no stat information for %s", fname);
1033 continue;
1034 }
1035 lname = ls_file(fname, g.gl_statv[i], 1,
1036 (lflag & LS_SI_UNITS));
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001037 mprintf("%s\n", lname);
Darren Tuckera627d422013-06-02 07:31:17 +10001038 free(lname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001039 } else {
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001040 mprintf("%-*s", colspace, fname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001041 if (c >= columns) {
1042 printf("\n");
1043 c = 1;
1044 } else
1045 c++;
1046 }
Darren Tuckera627d422013-06-02 07:31:17 +10001047 free(fname);
Damien Miller20e1fab2004-02-18 14:30:55 +11001048 }
1049
Darren Tuckera4e9ffa2004-06-22 13:07:58 +10001050 if (!(lflag & LS_LONG_VIEW) && (c != 1))
Damien Miller20e1fab2004-02-18 14:30:55 +11001051 printf("\n");
1052
Darren Tuckercdf547a2004-05-24 10:12:19 +10001053 out:
Damien Miller20e1fab2004-02-18 14:30:55 +11001054 if (g.gl_pathc)
1055 globfree(&g);
djm@openbsd.org72be5b22017-06-10 06:33:34 +00001056 free(indices);
Damien Miller20e1fab2004-02-18 14:30:55 +11001057
Damien Millera6e121a2010-10-07 21:39:17 +11001058 return 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001059}
1060
Damien Millerd671e5a2008-05-19 14:53:33 +10001061static int
djm@openbsd.orgda88a702016-07-22 03:47:36 +00001062do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
Damien Millerd671e5a2008-05-19 14:53:33 +10001063{
Darren Tucker7b598892008-06-09 22:49:36 +10001064 struct sftp_statvfs st;
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001065 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1066 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1067 char s_icapacity[16], s_dcapacity[16];
Damien Millerd671e5a2008-05-19 14:53:33 +10001068
1069 if (do_statvfs(conn, path, &st, 1) == -1)
1070 return -1;
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001071 if (st.f_files == 0)
1072 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1073 else {
1074 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1075 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1076 st.f_files));
1077 }
1078 if (st.f_blocks == 0)
1079 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1080 else {
1081 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1082 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1083 st.f_blocks));
1084 }
Damien Millerd671e5a2008-05-19 14:53:33 +10001085 if (iflag) {
1086 printf(" Inodes Used Avail "
1087 "(root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001088 printf("%11llu %11llu %11llu %11llu %s\n",
Damien Millerd671e5a2008-05-19 14:53:33 +10001089 (unsigned long long)st.f_files,
1090 (unsigned long long)(st.f_files - st.f_ffree),
1091 (unsigned long long)st.f_favail,
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001092 (unsigned long long)st.f_ffree, s_icapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001093 } else if (hflag) {
1094 strlcpy(s_used, "error", sizeof(s_used));
1095 strlcpy(s_avail, "error", sizeof(s_avail));
1096 strlcpy(s_root, "error", sizeof(s_root));
1097 strlcpy(s_total, "error", sizeof(s_total));
1098 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1099 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1100 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1101 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1102 printf(" Size Used Avail (root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001103 printf("%7sB %7sB %7sB %7sB %s\n",
1104 s_total, s_used, s_avail, s_root, s_dcapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001105 } else {
1106 printf(" Size Used Avail "
1107 "(root) %%Capacity\n");
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001108 printf("%12llu %12llu %12llu %12llu %s\n",
Damien Millerd671e5a2008-05-19 14:53:33 +10001109 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1110 (unsigned long long)(st.f_frsize *
1111 (st.f_blocks - st.f_bfree) / 1024),
1112 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1113 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
djm@openbsd.org6d5a41b2017-02-15 01:46:47 +00001114 s_dcapacity);
Damien Millerd671e5a2008-05-19 14:53:33 +10001115 }
1116 return 0;
1117}
1118
Damien Miller1cbc2922007-10-26 14:27:45 +10001119/*
1120 * Undo escaping of glob sequences in place. Used to undo extra escaping
1121 * applied in makeargv() when the string is destined for a function that
1122 * does not glob it.
1123 */
1124static void
1125undo_glob_escape(char *s)
1126{
1127 size_t i, j;
1128
1129 for (i = j = 0;;) {
1130 if (s[i] == '\0') {
1131 s[j] = '\0';
1132 return;
1133 }
1134 if (s[i] != '\\') {
1135 s[j++] = s[i++];
1136 continue;
1137 }
1138 /* s[i] == '\\' */
1139 ++i;
1140 switch (s[i]) {
1141 case '?':
1142 case '[':
1143 case '*':
1144 case '\\':
1145 s[j++] = s[i++];
1146 break;
1147 case '\0':
1148 s[j++] = '\\';
1149 s[j] = '\0';
1150 return;
1151 default:
1152 s[j++] = '\\';
1153 s[j++] = s[i++];
1154 break;
1155 }
1156 }
1157}
1158
1159/*
1160 * Split a string into an argument vector using sh(1)-style quoting,
1161 * comment and escaping rules, but with some tweaks to handle glob(3)
1162 * wildcards.
Darren Tucker909d8582010-01-08 19:02:40 +11001163 * The "sloppy" flag allows for recovery from missing terminating quote, for
1164 * use in parsing incomplete commandlines during tab autocompletion.
1165 *
Damien Miller1cbc2922007-10-26 14:27:45 +10001166 * Returns NULL on error or a NULL-terminated array of arguments.
Darren Tucker909d8582010-01-08 19:02:40 +11001167 *
1168 * If "lastquote" is not NULL, the quoting character used for the last
1169 * argument is placed in *lastquote ("\0", "'" or "\"").
Damien Miller02e87802013-08-21 02:38:51 +10001170 *
Darren Tucker909d8582010-01-08 19:02:40 +11001171 * If "terminated" is not NULL, *terminated will be set to 1 when the
1172 * last argument's quote has been properly terminated or 0 otherwise.
1173 * This parameter is only of use if "sloppy" is set.
Damien Miller1cbc2922007-10-26 14:27:45 +10001174 */
1175#define MAXARGS 128
1176#define MAXARGLEN 8192
1177static char **
Darren Tucker909d8582010-01-08 19:02:40 +11001178makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1179 u_int *terminated)
Damien Miller1cbc2922007-10-26 14:27:45 +10001180{
1181 int argc, quot;
1182 size_t i, j;
1183 static char argvs[MAXARGLEN];
1184 static char *argv[MAXARGS + 1];
1185 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1186
1187 *argcp = argc = 0;
1188 if (strlen(arg) > sizeof(argvs) - 1) {
1189 args_too_longs:
1190 error("string too long");
1191 return NULL;
1192 }
Darren Tucker909d8582010-01-08 19:02:40 +11001193 if (terminated != NULL)
1194 *terminated = 1;
1195 if (lastquote != NULL)
1196 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001197 state = MA_START;
1198 i = j = 0;
1199 for (;;) {
Damien Miller07daed52012-10-31 08:57:55 +11001200 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
Darren Tucker063018d2012-10-05 10:43:58 +10001201 error("Too many arguments.");
1202 return NULL;
1203 }
Damien Millerfdb23062013-11-21 13:57:15 +11001204 if (isspace((unsigned char)arg[i])) {
Damien Miller1cbc2922007-10-26 14:27:45 +10001205 if (state == MA_UNQUOTED) {
1206 /* Terminate current argument */
1207 argvs[j++] = '\0';
1208 argc++;
1209 state = MA_START;
1210 } else if (state != MA_START)
1211 argvs[j++] = arg[i];
1212 } else if (arg[i] == '"' || arg[i] == '\'') {
1213 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1214 if (state == MA_START) {
1215 argv[argc] = argvs + j;
1216 state = q;
Darren Tucker909d8582010-01-08 19:02:40 +11001217 if (lastquote != NULL)
1218 *lastquote = arg[i];
Damien Miller02e87802013-08-21 02:38:51 +10001219 } else if (state == MA_UNQUOTED)
Damien Miller1cbc2922007-10-26 14:27:45 +10001220 state = q;
1221 else if (state == q)
1222 state = MA_UNQUOTED;
1223 else
1224 argvs[j++] = arg[i];
1225 } else if (arg[i] == '\\') {
1226 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1227 quot = state == MA_SQUOTE ? '\'' : '"';
1228 /* Unescape quote we are in */
1229 /* XXX support \n and friends? */
1230 if (arg[i + 1] == quot) {
1231 i++;
1232 argvs[j++] = arg[i];
1233 } else if (arg[i + 1] == '?' ||
1234 arg[i + 1] == '[' || arg[i + 1] == '*') {
1235 /*
1236 * Special case for sftp: append
1237 * double-escaped glob sequence -
1238 * glob will undo one level of
1239 * escaping. NB. string can grow here.
1240 */
1241 if (j >= sizeof(argvs) - 5)
1242 goto args_too_longs;
1243 argvs[j++] = '\\';
1244 argvs[j++] = arg[i++];
1245 argvs[j++] = '\\';
1246 argvs[j++] = arg[i];
1247 } else {
1248 argvs[j++] = arg[i++];
1249 argvs[j++] = arg[i];
1250 }
1251 } else {
1252 if (state == MA_START) {
1253 argv[argc] = argvs + j;
1254 state = MA_UNQUOTED;
Darren Tucker909d8582010-01-08 19:02:40 +11001255 if (lastquote != NULL)
1256 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001257 }
1258 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1259 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1260 /*
1261 * Special case for sftp: append
1262 * escaped glob sequence -
1263 * glob will undo one level of
1264 * escaping.
1265 */
1266 argvs[j++] = arg[i++];
1267 argvs[j++] = arg[i];
1268 } else {
1269 /* Unescape everything */
1270 /* XXX support \n and friends? */
1271 i++;
1272 argvs[j++] = arg[i];
1273 }
1274 }
1275 } else if (arg[i] == '#') {
1276 if (state == MA_SQUOTE || state == MA_DQUOTE)
1277 argvs[j++] = arg[i];
1278 else
1279 goto string_done;
1280 } else if (arg[i] == '\0') {
1281 if (state == MA_SQUOTE || state == MA_DQUOTE) {
Darren Tucker909d8582010-01-08 19:02:40 +11001282 if (sloppy) {
1283 state = MA_UNQUOTED;
1284 if (terminated != NULL)
1285 *terminated = 0;
1286 goto string_done;
1287 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001288 error("Unterminated quoted argument");
1289 return NULL;
1290 }
1291 string_done:
1292 if (state == MA_UNQUOTED) {
1293 argvs[j++] = '\0';
1294 argc++;
1295 }
1296 break;
1297 } else {
1298 if (state == MA_START) {
1299 argv[argc] = argvs + j;
1300 state = MA_UNQUOTED;
Darren Tucker909d8582010-01-08 19:02:40 +11001301 if (lastquote != NULL)
1302 *lastquote = '\0';
Damien Miller1cbc2922007-10-26 14:27:45 +10001303 }
1304 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1305 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1306 /*
1307 * Special case for sftp: escape quoted
1308 * glob(3) wildcards. NB. string can grow
1309 * here.
1310 */
1311 if (j >= sizeof(argvs) - 3)
1312 goto args_too_longs;
1313 argvs[j++] = '\\';
1314 argvs[j++] = arg[i];
1315 } else
1316 argvs[j++] = arg[i];
1317 }
1318 i++;
1319 }
1320 *argcp = argc;
1321 return argv;
1322}
1323
Damien Miller20e1fab2004-02-18 14:30:55 +11001324static int
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001325parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
djm@openbsd.org34a01b22016-04-08 08:19:17 +00001326 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
Damien Millerd8accc02014-05-15 13:46:25 +10001327 int *rflag, int *sflag,
Damien Millerf29238e2013-10-17 11:48:52 +11001328 unsigned long *n_arg, char **path1, char **path2)
Damien Miller20e1fab2004-02-18 14:30:55 +11001329{
1330 const char *cmd, *cp = *cpp;
Damien Miller1cbc2922007-10-26 14:27:45 +10001331 char *cp2, **argv;
Damien Miller20e1fab2004-02-18 14:30:55 +11001332 int base = 0;
1333 long l;
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001334 int path1_mandatory = 0, i, cmdnum, optidx, argc;
Damien Miller20e1fab2004-02-18 14:30:55 +11001335
1336 /* Skip leading whitespace */
1337 cp = cp + strspn(cp, WHITESPACE);
1338
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001339 /*
1340 * Check for leading '-' (disable error processing) and '@' (suppress
1341 * command echo)
1342 */
Damien Millerf29238e2013-10-17 11:48:52 +11001343 *ignore_errors = 0;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001344 *disable_echo = 0;
1345 for (;*cp != '\0'; cp++) {
1346 if (*cp == '-') {
1347 *ignore_errors = 1;
1348 } else if (*cp == '@') {
1349 *disable_echo = 1;
1350 } else {
1351 /* all other characters terminate prefix processing */
1352 break;
1353 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001354 }
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001355 cp = cp + strspn(cp, WHITESPACE);
Damien Miller20e1fab2004-02-18 14:30:55 +11001356
Darren Tucker70cc0922010-01-09 22:28:03 +11001357 /* Ignore blank lines and lines which begin with comment '#' char */
1358 if (*cp == '\0' || *cp == '#')
1359 return (0);
1360
Darren Tucker909d8582010-01-08 19:02:40 +11001361 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
Damien Miller1cbc2922007-10-26 14:27:45 +10001362 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001363
Damien Miller1cbc2922007-10-26 14:27:45 +10001364 /* Figure out which command we have */
1365 for (i = 0; cmds[i].c != NULL; i++) {
Damien Millerd6d9fa02013-02-12 11:02:46 +11001366 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
Damien Miller20e1fab2004-02-18 14:30:55 +11001367 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001368 }
1369 cmdnum = cmds[i].n;
1370 cmd = cmds[i].c;
1371
1372 /* Special case */
1373 if (*cp == '!') {
1374 cp++;
1375 cmdnum = I_SHELL;
1376 } else if (cmdnum == -1) {
1377 error("Invalid command.");
Damien Miller1cbc2922007-10-26 14:27:45 +10001378 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001379 }
1380
1381 /* Get arguments and parse flags */
Damien Millerf29238e2013-10-17 11:48:52 +11001382 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1383 *rflag = *sflag = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001384 *path1 = *path2 = NULL;
Damien Miller1cbc2922007-10-26 14:27:45 +10001385 optidx = 1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001386 switch (cmdnum) {
1387 case I_GET:
Damien Miller0d032412013-07-25 11:56:52 +10001388 case I_REGET:
Damien Millerd8accc02014-05-15 13:46:25 +10001389 case I_REPUT:
Damien Miller20e1fab2004-02-18 14:30:55 +11001390 case I_PUT:
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001391 if ((optidx = parse_getput_flags(cmd, argv, argc,
Damien Millerf29238e2013-10-17 11:48:52 +11001392 aflag, fflag, pflag, rflag)) == -1)
Damien Miller1cbc2922007-10-26 14:27:45 +10001393 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001394 /* Get first pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001395 if (argc - optidx < 1) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001396 error("You must specify at least one path after a "
1397 "%s command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001398 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001399 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001400 *path1 = xstrdup(argv[optidx]);
1401 /* Get second pathname (optional) */
1402 if (argc - optidx > 1) {
1403 *path2 = xstrdup(argv[optidx + 1]);
1404 /* Destination is not globbed */
1405 undo_glob_escape(*path2);
1406 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001407 break;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001408 case I_LINK:
1409 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1410 return -1;
Damien Millerc7dba122013-08-21 02:41:15 +10001411 goto parse_two_paths;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001412 case I_RENAME:
Damien Millerc7dba122013-08-21 02:41:15 +10001413 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1414 return -1;
1415 goto parse_two_paths;
1416 case I_SYMLINK:
Damien Miller036d3072013-08-21 02:41:46 +10001417 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1418 return -1;
Damien Millerc7dba122013-08-21 02:41:15 +10001419 parse_two_paths:
Damien Miller1cbc2922007-10-26 14:27:45 +10001420 if (argc - optidx < 2) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001421 error("You must specify two paths after a %s "
1422 "command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001423 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001424 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001425 *path1 = xstrdup(argv[optidx]);
1426 *path2 = xstrdup(argv[optidx + 1]);
1427 /* Paths are not globbed */
1428 undo_glob_escape(*path1);
1429 undo_glob_escape(*path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001430 break;
1431 case I_RM:
1432 case I_MKDIR:
1433 case I_RMDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001434 case I_LMKDIR:
1435 path1_mandatory = 1;
1436 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001437 case I_CHDIR:
1438 case I_LCHDIR:
Damien Miller036d3072013-08-21 02:41:46 +10001439 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1440 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001441 /* Get pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001442 if (argc - optidx < 1) {
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001443 if (!path1_mandatory)
1444 break; /* return a NULL path1 */
Damien Miller20e1fab2004-02-18 14:30:55 +11001445 error("You must specify a path after a %s command.",
1446 cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001447 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001448 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001449 *path1 = xstrdup(argv[optidx]);
1450 /* Only "rm" globs */
1451 if (cmdnum != I_RM)
1452 undo_glob_escape(*path1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001453 break;
Damien Millerd671e5a2008-05-19 14:53:33 +10001454 case I_DF:
1455 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1456 iflag)) == -1)
1457 return -1;
1458 /* Default to current directory if no path specified */
1459 if (argc - optidx < 1)
1460 *path1 = NULL;
1461 else {
1462 *path1 = xstrdup(argv[optidx]);
1463 undo_glob_escape(*path1);
1464 }
1465 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001466 case I_LS:
Damien Miller1cbc2922007-10-26 14:27:45 +10001467 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
Damien Miller20e1fab2004-02-18 14:30:55 +11001468 return(-1);
1469 /* Path is optional */
Damien Miller1cbc2922007-10-26 14:27:45 +10001470 if (argc - optidx > 0)
1471 *path1 = xstrdup(argv[optidx]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001472 break;
1473 case I_LLS:
Darren Tucker88b976f2007-12-29 02:40:43 +11001474 /* Skip ls command and following whitespace */
1475 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
Damien Miller20e1fab2004-02-18 14:30:55 +11001476 case I_SHELL:
1477 /* Uses the rest of the line */
1478 break;
1479 case I_LUMASK:
Damien Miller20e1fab2004-02-18 14:30:55 +11001480 case I_CHMOD:
1481 base = 8;
dtucker@openbsd.orgde37ca92018-09-07 04:26:56 +00001482 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001483 case I_CHOWN:
1484 case I_CHGRP:
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001485 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
Damien Miller036d3072013-08-21 02:41:46 +10001486 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001487 /* Get numeric arg (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001488 if (argc - optidx < 1)
1489 goto need_num_arg;
Damien Millere7658a52006-10-24 03:00:12 +10001490 errno = 0;
Damien Miller1cbc2922007-10-26 14:27:45 +10001491 l = strtol(argv[optidx], &cp2, base);
1492 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1493 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1494 l < 0) {
1495 need_num_arg:
Damien Miller20e1fab2004-02-18 14:30:55 +11001496 error("You must supply a numeric argument "
1497 "to the %s command.", cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001498 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001499 }
Damien Miller20e1fab2004-02-18 14:30:55 +11001500 *n_arg = l;
Damien Miller1cbc2922007-10-26 14:27:45 +10001501 if (cmdnum == I_LUMASK)
Damien Miller20e1fab2004-02-18 14:30:55 +11001502 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001503 /* Get pathname (mandatory) */
Damien Miller1cbc2922007-10-26 14:27:45 +10001504 if (argc - optidx < 2) {
Damien Miller20e1fab2004-02-18 14:30:55 +11001505 error("You must specify a path after a %s command.",
1506 cmd);
Damien Miller1cbc2922007-10-26 14:27:45 +10001507 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001508 }
Damien Miller1cbc2922007-10-26 14:27:45 +10001509 *path1 = xstrdup(argv[optidx + 1]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001510 break;
1511 case I_QUIT:
1512 case I_PWD:
1513 case I_LPWD:
1514 case I_HELP:
1515 case I_VERSION:
1516 case I_PROGRESS:
Damien Miller036d3072013-08-21 02:41:46 +10001517 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1518 return -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001519 break;
1520 default:
1521 fatal("Command not implemented");
1522 }
1523
1524 *cpp = cp;
1525 return(cmdnum);
1526}
1527
1528static int
1529parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001530 const char *startdir, int err_abort, int echo_command)
Damien Miller20e1fab2004-02-18 14:30:55 +11001531{
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001532 const char *ocmd = cmd;
Damien Miller20e1fab2004-02-18 14:30:55 +11001533 char *path1, *path2, *tmp;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001534 int ignore_errors = 0, disable_echo = 1;
1535 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
Damien Millerf29238e2013-10-17 11:48:52 +11001536 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001537 int cmdnum, i;
Damien Millerfdd66fc2009-02-14 16:26:19 +11001538 unsigned long n_arg = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11001539 Attrib a, *aa;
deraadt@openbsd.org087266e2015-01-20 23:14:00 +00001540 char path_buf[PATH_MAX];
Damien Miller20e1fab2004-02-18 14:30:55 +11001541 int err = 0;
1542 glob_t g;
1543
1544 path1 = path2 = NULL;
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001545 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1546 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1547 &path1, &path2);
Damien Millerf29238e2013-10-17 11:48:52 +11001548 if (ignore_errors != 0)
Damien Miller20e1fab2004-02-18 14:30:55 +11001549 err_abort = 0;
1550
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00001551 if (echo_command && !disable_echo)
1552 mprintf("sftp> %s\n", ocmd);
1553
Damien Miller20e1fab2004-02-18 14:30:55 +11001554 memset(&g, 0, sizeof(g));
1555
1556 /* Perform command */
1557 switch (cmdnum) {
1558 case 0:
1559 /* Blank line */
1560 break;
1561 case -1:
1562 /* Unrecognized command */
1563 err = -1;
1564 break;
Damien Miller0d032412013-07-25 11:56:52 +10001565 case I_REGET:
1566 aflag = 1;
1567 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001568 case I_GET:
Damien Miller0d032412013-07-25 11:56:52 +10001569 err = process_get(conn, path1, path2, *pwd, pflag,
Damien Millerf29238e2013-10-17 11:48:52 +11001570 rflag, aflag, fflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001571 break;
Damien Millerd8accc02014-05-15 13:46:25 +10001572 case I_REPUT:
1573 aflag = 1;
1574 /* FALLTHROUGH */
Damien Miller20e1fab2004-02-18 14:30:55 +11001575 case I_PUT:
Damien Millerf29238e2013-10-17 11:48:52 +11001576 err = process_put(conn, path1, path2, *pwd, pflag,
Damien Millerd8accc02014-05-15 13:46:25 +10001577 rflag, aflag, fflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001578 break;
1579 case I_RENAME:
1580 path1 = make_absolute(path1, *pwd);
1581 path2 = make_absolute(path2, *pwd);
Damien Millerc7dba122013-08-21 02:41:15 +10001582 err = do_rename(conn, path1, path2, lflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001583 break;
1584 case I_SYMLINK:
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001585 sflag = 1;
dtucker@openbsd.orgde37ca92018-09-07 04:26:56 +00001586 /* FALLTHROUGH */
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001587 case I_LINK:
Damien Miller034f27a2013-08-21 02:40:44 +10001588 if (!sflag)
1589 path1 = make_absolute(path1, *pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001590 path2 = make_absolute(path2, *pwd);
Darren Tuckeraf1f9092010-12-05 09:02:47 +11001591 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001592 break;
1593 case I_RM:
1594 path1 = make_absolute(path1, *pwd);
1595 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001596 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Miller9303e652013-04-23 15:22:40 +10001597 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001598 mprintf("Removing %s\n", g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001599 err = do_rm(conn, g.gl_pathv[i]);
1600 if (err != 0 && err_abort)
1601 break;
1602 }
1603 break;
1604 case I_MKDIR:
1605 path1 = make_absolute(path1, *pwd);
1606 attrib_clear(&a);
1607 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1608 a.perm = 0777;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001609 err = do_mkdir(conn, path1, &a, 1);
Damien Miller20e1fab2004-02-18 14:30:55 +11001610 break;
1611 case I_RMDIR:
1612 path1 = make_absolute(path1, *pwd);
1613 err = do_rmdir(conn, path1);
1614 break;
1615 case I_CHDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001616 if (path1 == NULL || *path1 == '\0')
1617 path1 = xstrdup(startdir);
Damien Miller20e1fab2004-02-18 14:30:55 +11001618 path1 = make_absolute(path1, *pwd);
1619 if ((tmp = do_realpath(conn, path1)) == NULL) {
1620 err = 1;
1621 break;
1622 }
1623 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
Darren Tuckera627d422013-06-02 07:31:17 +10001624 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001625 err = 1;
1626 break;
1627 }
1628 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1629 error("Can't change directory: Can't check target");
Darren Tuckera627d422013-06-02 07:31:17 +10001630 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001631 err = 1;
1632 break;
1633 }
1634 if (!S_ISDIR(aa->perm)) {
1635 error("Can't change directory: \"%s\" is not "
1636 "a directory", tmp);
Darren Tuckera627d422013-06-02 07:31:17 +10001637 free(tmp);
Damien Miller20e1fab2004-02-18 14:30:55 +11001638 err = 1;
1639 break;
1640 }
Darren Tuckera627d422013-06-02 07:31:17 +10001641 free(*pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001642 *pwd = tmp;
1643 break;
1644 case I_LS:
1645 if (!path1) {
Damien Miller99ac4e92010-06-26 09:36:58 +10001646 do_ls_dir(conn, *pwd, *pwd, lflag);
Damien Miller20e1fab2004-02-18 14:30:55 +11001647 break;
1648 }
1649
1650 /* Strip pwd off beginning of non-absolute paths */
1651 tmp = NULL;
djm@openbsd.org2a358622018-11-16 03:26:01 +00001652 if (!path_absolute(path1))
Damien Miller20e1fab2004-02-18 14:30:55 +11001653 tmp = *pwd;
1654
1655 path1 = make_absolute(path1, *pwd);
1656 err = do_globbed_ls(conn, path1, tmp, lflag);
1657 break;
Damien Millerd671e5a2008-05-19 14:53:33 +10001658 case I_DF:
1659 /* Default to current directory if no path specified */
1660 if (path1 == NULL)
1661 path1 = xstrdup(*pwd);
1662 path1 = make_absolute(path1, *pwd);
1663 err = do_df(conn, path1, hflag, iflag);
1664 break;
Damien Miller20e1fab2004-02-18 14:30:55 +11001665 case I_LCHDIR:
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00001666 if (path1 == NULL || *path1 == '\0')
1667 path1 = xstrdup("~");
deraadt@openbsd.org40ba4c92014-08-20 01:28:55 +00001668 tmp = tilde_expand_filename(path1, getuid());
djm@openbsd.org7ff880e2014-08-19 23:57:18 +00001669 free(path1);
1670 path1 = tmp;
Damien Miller20e1fab2004-02-18 14:30:55 +11001671 if (chdir(path1) == -1) {
1672 error("Couldn't change local directory to "
1673 "\"%s\": %s", path1, strerror(errno));
1674 err = 1;
1675 }
1676 break;
1677 case I_LMKDIR:
1678 if (mkdir(path1, 0777) == -1) {
1679 error("Couldn't create local directory "
1680 "\"%s\": %s", path1, strerror(errno));
1681 err = 1;
1682 }
1683 break;
1684 case I_LLS:
1685 local_do_ls(cmd);
1686 break;
1687 case I_SHELL:
1688 local_do_shell(cmd);
1689 break;
1690 case I_LUMASK:
1691 umask(n_arg);
1692 printf("Local umask: %03lo\n", n_arg);
1693 break;
1694 case I_CHMOD:
1695 path1 = make_absolute(path1, *pwd);
1696 attrib_clear(&a);
1697 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1698 a.perm = n_arg;
1699 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001700 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
Damien Miller9303e652013-04-23 15:22:40 +10001701 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001702 mprintf("Changing mode on %s\n",
1703 g.gl_pathv[i]);
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001704 err = (hflag ? do_lsetstat : do_setstat)(conn,
1705 g.gl_pathv[i], &a);
Damien Miller20e1fab2004-02-18 14:30:55 +11001706 if (err != 0 && err_abort)
1707 break;
1708 }
1709 break;
1710 case I_CHOWN:
1711 case I_CHGRP:
1712 path1 = make_absolute(path1, *pwd);
1713 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Darren Tuckercdf547a2004-05-24 10:12:19 +10001714 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001715 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1716 g.gl_pathv[i], 0))) {
Damien Miller1be2cc42008-12-09 14:11:49 +11001717 if (err_abort) {
1718 err = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001719 break;
Damien Miller1be2cc42008-12-09 14:11:49 +11001720 } else
Damien Miller20e1fab2004-02-18 14:30:55 +11001721 continue;
1722 }
1723 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1724 error("Can't get current ownership of "
1725 "remote file \"%s\"", g.gl_pathv[i]);
Damien Miller1be2cc42008-12-09 14:11:49 +11001726 if (err_abort) {
1727 err = -1;
Damien Miller20e1fab2004-02-18 14:30:55 +11001728 break;
Damien Miller1be2cc42008-12-09 14:11:49 +11001729 } else
Damien Miller20e1fab2004-02-18 14:30:55 +11001730 continue;
1731 }
1732 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1733 if (cmdnum == I_CHOWN) {
Damien Miller9303e652013-04-23 15:22:40 +10001734 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001735 mprintf("Changing owner on %s\n",
Damien Miller9303e652013-04-23 15:22:40 +10001736 g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001737 aa->uid = n_arg;
1738 } else {
Damien Miller9303e652013-04-23 15:22:40 +10001739 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001740 mprintf("Changing group on %s\n",
Damien Miller9303e652013-04-23 15:22:40 +10001741 g.gl_pathv[i]);
Damien Miller20e1fab2004-02-18 14:30:55 +11001742 aa->gid = n_arg;
1743 }
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001744 err = (hflag ? do_lsetstat : do_setstat)(conn,
1745 g.gl_pathv[i], aa);
Damien Miller20e1fab2004-02-18 14:30:55 +11001746 if (err != 0 && err_abort)
1747 break;
1748 }
1749 break;
1750 case I_PWD:
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001751 mprintf("Remote working directory: %s\n", *pwd);
Damien Miller20e1fab2004-02-18 14:30:55 +11001752 break;
1753 case I_LPWD:
1754 if (!getcwd(path_buf, sizeof(path_buf))) {
1755 error("Couldn't get local cwd: %s", strerror(errno));
1756 err = -1;
1757 break;
1758 }
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001759 mprintf("Local working directory: %s\n", path_buf);
Damien Miller20e1fab2004-02-18 14:30:55 +11001760 break;
1761 case I_QUIT:
1762 /* Processed below */
1763 break;
1764 case I_HELP:
1765 help();
1766 break;
1767 case I_VERSION:
1768 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1769 break;
1770 case I_PROGRESS:
1771 showprogress = !showprogress;
1772 if (showprogress)
1773 printf("Progress meter enabled\n");
1774 else
1775 printf("Progress meter disabled\n");
1776 break;
1777 default:
1778 fatal("%d is not implemented", cmdnum);
1779 }
1780
1781 if (g.gl_pathc)
1782 globfree(&g);
Darren Tuckera627d422013-06-02 07:31:17 +10001783 free(path1);
1784 free(path2);
Damien Miller20e1fab2004-02-18 14:30:55 +11001785
1786 /* If an unignored error occurs in batch mode we should abort. */
1787 if (err_abort && err != 0)
1788 return (-1);
1789 else if (cmdnum == I_QUIT)
1790 return (1);
1791
1792 return (0);
1793}
1794
Darren Tucker2d963d82004-11-07 20:04:10 +11001795#ifdef USE_LIBEDIT
1796static char *
1797prompt(EditLine *el)
1798{
1799 return ("sftp> ");
1800}
Darren Tucker2d963d82004-11-07 20:04:10 +11001801
Darren Tucker909d8582010-01-08 19:02:40 +11001802/* Display entries in 'list' after skipping the first 'len' chars */
1803static void
1804complete_display(char **list, u_int len)
1805{
1806 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1807 struct winsize ws;
1808 char *tmp;
1809
1810 /* Count entries for sort and find longest */
Damien Miller02e87802013-08-21 02:38:51 +10001811 for (y = 0; list[y]; y++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001812 m = MAXIMUM(m, strlen(list[y]));
Darren Tucker909d8582010-01-08 19:02:40 +11001813
1814 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1815 width = ws.ws_col;
1816
1817 m = m > len ? m - len : 0;
1818 columns = width / (m + 2);
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001819 columns = MAXIMUM(columns, 1);
Darren Tucker909d8582010-01-08 19:02:40 +11001820 colspace = width / columns;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001821 colspace = MINIMUM(colspace, width);
Darren Tucker909d8582010-01-08 19:02:40 +11001822
1823 printf("\n");
1824 m = 1;
1825 for (y = 0; list[y]; y++) {
1826 llen = strlen(list[y]);
1827 tmp = llen > len ? list[y] + len : "";
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001828 mprintf("%-*s", colspace, tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11001829 if (m >= columns) {
1830 printf("\n");
1831 m = 1;
1832 } else
1833 m++;
1834 }
1835 printf("\n");
1836}
1837
1838/*
1839 * Given a "list" of words that begin with a common prefix of "word",
1840 * attempt to find an autocompletion to extends "word" by the next
1841 * characters common to all entries in "list".
1842 */
1843static char *
1844complete_ambiguous(const char *word, char **list, size_t count)
1845{
1846 if (word == NULL)
1847 return NULL;
1848
1849 if (count > 0) {
1850 u_int y, matchlen = strlen(list[0]);
1851
1852 /* Find length of common stem */
1853 for (y = 1; list[y]; y++) {
1854 u_int x;
1855
Damien Miller02e87802013-08-21 02:38:51 +10001856 for (x = 0; x < matchlen; x++)
1857 if (list[0][x] != list[y][x])
Darren Tucker909d8582010-01-08 19:02:40 +11001858 break;
1859
1860 matchlen = x;
1861 }
1862
1863 if (matchlen > strlen(word)) {
1864 char *tmp = xstrdup(list[0]);
1865
Darren Tucker340d1682010-01-09 08:54:31 +11001866 tmp[matchlen] = '\0';
Darren Tucker909d8582010-01-08 19:02:40 +11001867 return tmp;
1868 }
Damien Miller02e87802013-08-21 02:38:51 +10001869 }
Darren Tucker909d8582010-01-08 19:02:40 +11001870
1871 return xstrdup(word);
1872}
1873
1874/* Autocomplete a sftp command */
1875static int
1876complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1877 int terminated)
1878{
1879 u_int y, count = 0, cmdlen, tmplen;
1880 char *tmp, **list, argterm[3];
1881 const LineInfo *lf;
1882
1883 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1884
1885 /* No command specified: display all available commands */
1886 if (cmd == NULL) {
1887 for (y = 0; cmds[y].c; y++)
1888 list[count++] = xstrdup(cmds[y].c);
Damien Miller02e87802013-08-21 02:38:51 +10001889
Darren Tucker909d8582010-01-08 19:02:40 +11001890 list[count] = NULL;
1891 complete_display(list, 0);
1892
Damien Miller02e87802013-08-21 02:38:51 +10001893 for (y = 0; list[y] != NULL; y++)
1894 free(list[y]);
Darren Tuckera627d422013-06-02 07:31:17 +10001895 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001896 return count;
1897 }
1898
1899 /* Prepare subset of commands that start with "cmd" */
1900 cmdlen = strlen(cmd);
1901 for (y = 0; cmds[y].c; y++) {
Damien Miller02e87802013-08-21 02:38:51 +10001902 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
Darren Tucker909d8582010-01-08 19:02:40 +11001903 list[count++] = xstrdup(cmds[y].c);
1904 }
1905 list[count] = NULL;
1906
Damien Miller47d81152011-11-25 13:53:48 +11001907 if (count == 0) {
Darren Tuckera627d422013-06-02 07:31:17 +10001908 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001909 return 0;
Damien Miller47d81152011-11-25 13:53:48 +11001910 }
Darren Tucker909d8582010-01-08 19:02:40 +11001911
djm@openbsd.org001aa552018-04-10 00:10:49 +00001912 /* Complete ambiguous command */
Darren Tucker909d8582010-01-08 19:02:40 +11001913 tmp = complete_ambiguous(cmd, list, count);
1914 if (count > 1)
1915 complete_display(list, 0);
1916
Damien Miller02e87802013-08-21 02:38:51 +10001917 for (y = 0; list[y]; y++)
1918 free(list[y]);
Darren Tuckera627d422013-06-02 07:31:17 +10001919 free(list);
Darren Tucker909d8582010-01-08 19:02:40 +11001920
1921 if (tmp != NULL) {
1922 tmplen = strlen(tmp);
1923 cmdlen = strlen(cmd);
1924 /* If cmd may be extended then do so */
1925 if (tmplen > cmdlen)
1926 if (el_insertstr(el, tmp + cmdlen) == -1)
1927 fatal("el_insertstr failed.");
1928 lf = el_line(el);
1929 /* Terminate argument cleanly */
1930 if (count == 1) {
1931 y = 0;
1932 if (!terminated)
1933 argterm[y++] = quote;
1934 if (lastarg || *(lf->cursor) != ' ')
1935 argterm[y++] = ' ';
1936 argterm[y] = '\0';
1937 if (y > 0 && el_insertstr(el, argterm) == -1)
1938 fatal("el_insertstr failed.");
1939 }
Darren Tuckera627d422013-06-02 07:31:17 +10001940 free(tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11001941 }
1942
1943 return count;
1944}
1945
1946/*
1947 * Determine whether a particular sftp command's arguments (if any)
1948 * represent local or remote files.
1949 */
1950static int
1951complete_is_remote(char *cmd) {
1952 int i;
1953
1954 if (cmd == NULL)
1955 return -1;
1956
1957 for (i = 0; cmds[i].c; i++) {
Damien Miller02e87802013-08-21 02:38:51 +10001958 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
Darren Tucker909d8582010-01-08 19:02:40 +11001959 return cmds[i].t;
1960 }
1961
1962 return -1;
1963}
1964
1965/* Autocomplete a filename "file" */
1966static int
1967complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1968 char *file, int remote, int lastarg, char quote, int terminated)
1969{
1970 glob_t g;
Darren Tuckerea647212013-06-06 08:19:09 +10001971 char *tmp, *tmp2, ins[8];
Darren Tucker17146d32012-10-05 10:46:16 +10001972 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
Darren Tuckerea647212013-06-06 08:19:09 +10001973 int clen;
Darren Tucker909d8582010-01-08 19:02:40 +11001974 const LineInfo *lf;
Damien Miller02e87802013-08-21 02:38:51 +10001975
Darren Tucker909d8582010-01-08 19:02:40 +11001976 /* Glob from "file" location */
1977 if (file == NULL)
1978 tmp = xstrdup("*");
1979 else
1980 xasprintf(&tmp, "%s*", file);
1981
Darren Tucker191fcc62012-10-05 10:45:01 +10001982 /* Check if the path is absolute. */
djm@openbsd.org2a358622018-11-16 03:26:01 +00001983 isabs = path_absolute(tmp);
Darren Tucker191fcc62012-10-05 10:45:01 +10001984
Darren Tucker909d8582010-01-08 19:02:40 +11001985 memset(&g, 0, sizeof(g));
1986 if (remote != LOCAL) {
1987 tmp = make_absolute(tmp, remote_path);
1988 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
Damien Miller02e87802013-08-21 02:38:51 +10001989 } else
Darren Tucker909d8582010-01-08 19:02:40 +11001990 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
Damien Miller02e87802013-08-21 02:38:51 +10001991
Darren Tucker909d8582010-01-08 19:02:40 +11001992 /* Determine length of pwd so we can trim completion display */
1993 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1994 /* Terminate counting on first unescaped glob metacharacter */
1995 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1996 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1997 hadglob = 1;
1998 break;
1999 }
2000 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2001 tmplen++;
2002 if (tmp[tmplen] == '/')
2003 pwdlen = tmplen + 1; /* track last seen '/' */
2004 }
Darren Tuckera627d422013-06-02 07:31:17 +10002005 free(tmp);
Damien Millerd7fd8be2014-05-15 14:24:59 +10002006 tmp = NULL;
Darren Tucker909d8582010-01-08 19:02:40 +11002007
Damien Miller02e87802013-08-21 02:38:51 +10002008 if (g.gl_matchc == 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002009 goto out;
2010
2011 if (g.gl_matchc > 1)
2012 complete_display(g.gl_pathv, pwdlen);
2013
Darren Tucker909d8582010-01-08 19:02:40 +11002014 /* Don't try to extend globs */
2015 if (file == NULL || hadglob)
2016 goto out;
2017
2018 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
Darren Tucker191fcc62012-10-05 10:45:01 +10002019 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
Darren Tuckera627d422013-06-02 07:31:17 +10002020 free(tmp2);
Darren Tucker909d8582010-01-08 19:02:40 +11002021
2022 if (tmp == NULL)
2023 goto out;
2024
2025 tmplen = strlen(tmp);
2026 filelen = strlen(file);
2027
Darren Tucker17146d32012-10-05 10:46:16 +10002028 /* Count the number of escaped characters in the input string. */
2029 cesc = isesc = 0;
2030 for (i = 0; i < filelen; i++) {
2031 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2032 isesc = 1;
2033 cesc++;
2034 } else
2035 isesc = 0;
2036 }
2037
2038 if (tmplen > (filelen - cesc)) {
2039 tmp2 = tmp + filelen - cesc;
Damien Miller02e87802013-08-21 02:38:51 +10002040 len = strlen(tmp2);
Darren Tucker909d8582010-01-08 19:02:40 +11002041 /* quote argument on way out */
Darren Tuckerea647212013-06-06 08:19:09 +10002042 for (i = 0; i < len; i += clen) {
2043 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2044 (size_t)clen > sizeof(ins) - 2)
2045 fatal("invalid multibyte character");
Darren Tucker909d8582010-01-08 19:02:40 +11002046 ins[0] = '\\';
Darren Tuckerea647212013-06-06 08:19:09 +10002047 memcpy(ins + 1, tmp2 + i, clen);
2048 ins[clen + 1] = '\0';
Darren Tucker909d8582010-01-08 19:02:40 +11002049 switch (tmp2[i]) {
2050 case '\'':
2051 case '"':
2052 case '\\':
2053 case '\t':
Darren Tuckerd78739a2010-10-24 10:56:32 +11002054 case '[':
Darren Tucker909d8582010-01-08 19:02:40 +11002055 case ' ':
Darren Tucker17146d32012-10-05 10:46:16 +10002056 case '#':
2057 case '*':
Darren Tucker909d8582010-01-08 19:02:40 +11002058 if (quote == '\0' || tmp2[i] == quote) {
2059 if (el_insertstr(el, ins) == -1)
2060 fatal("el_insertstr "
2061 "failed.");
2062 break;
2063 }
2064 /* FALLTHROUGH */
2065 default:
2066 if (el_insertstr(el, ins + 1) == -1)
2067 fatal("el_insertstr failed.");
2068 break;
2069 }
2070 }
2071 }
2072
2073 lf = el_line(el);
Darren Tucker909d8582010-01-08 19:02:40 +11002074 if (g.gl_matchc == 1) {
2075 i = 0;
Damien Miller38094812014-05-15 14:25:18 +10002076 if (!terminated && quote != '\0')
Darren Tucker909d8582010-01-08 19:02:40 +11002077 ins[i++] = quote;
Darren Tucker9c3ba072010-01-13 22:45:03 +11002078 if (*(lf->cursor - 1) != '/' &&
2079 (lastarg || *(lf->cursor) != ' '))
Darren Tucker909d8582010-01-08 19:02:40 +11002080 ins[i++] = ' ';
2081 ins[i] = '\0';
2082 if (i > 0 && el_insertstr(el, ins) == -1)
2083 fatal("el_insertstr failed.");
2084 }
Darren Tuckera627d422013-06-02 07:31:17 +10002085 free(tmp);
Darren Tucker909d8582010-01-08 19:02:40 +11002086
2087 out:
2088 globfree(&g);
2089 return g.gl_matchc;
2090}
2091
2092/* tab-completion hook function, called via libedit */
2093static unsigned char
2094complete(EditLine *el, int ch)
2095{
Damien Miller02e87802013-08-21 02:38:51 +10002096 char **argv, *line, quote;
Damien Miller746d1a62013-07-18 16:13:02 +10002097 int argc, carg;
2098 u_int cursor, len, terminated, ret = CC_ERROR;
Darren Tucker909d8582010-01-08 19:02:40 +11002099 const LineInfo *lf;
2100 struct complete_ctx *complete_ctx;
2101
2102 lf = el_line(el);
2103 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2104 fatal("%s: el_get failed", __func__);
2105
2106 /* Figure out which argument the cursor points to */
2107 cursor = lf->cursor - lf->buffer;
deraadt@openbsd.orgce445b02015-08-20 22:32:42 +00002108 line = xmalloc(cursor + 1);
Darren Tucker909d8582010-01-08 19:02:40 +11002109 memcpy(line, lf->buffer, cursor);
2110 line[cursor] = '\0';
2111 argv = makeargv(line, &carg, 1, &quote, &terminated);
Darren Tuckera627d422013-06-02 07:31:17 +10002112 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002113
2114 /* Get all the arguments on the line */
2115 len = lf->lastchar - lf->buffer;
deraadt@openbsd.orgce445b02015-08-20 22:32:42 +00002116 line = xmalloc(len + 1);
Darren Tucker909d8582010-01-08 19:02:40 +11002117 memcpy(line, lf->buffer, len);
2118 line[len] = '\0';
2119 argv = makeargv(line, &argc, 1, NULL, NULL);
2120
2121 /* Ensure cursor is at EOL or a argument boundary */
2122 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2123 line[cursor] != '\n') {
Darren Tuckera627d422013-06-02 07:31:17 +10002124 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002125 return ret;
2126 }
2127
2128 if (carg == 0) {
2129 /* Show all available commands */
2130 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2131 ret = CC_REDISPLAY;
2132 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2133 /* Handle the command parsing */
2134 if (complete_cmd_parse(el, argv[0], argc == carg,
Damien Miller02e87802013-08-21 02:38:51 +10002135 quote, terminated) != 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002136 ret = CC_REDISPLAY;
2137 } else if (carg >= 1) {
2138 /* Handle file parsing */
2139 int remote = complete_is_remote(argv[0]);
2140 char *filematch = NULL;
2141
2142 if (carg > 1 && line[cursor-1] != ' ')
2143 filematch = argv[carg - 1];
2144
2145 if (remote != 0 &&
2146 complete_match(el, complete_ctx->conn,
2147 *complete_ctx->remote_pathp, filematch,
Damien Miller02e87802013-08-21 02:38:51 +10002148 remote, carg == argc, quote, terminated) != 0)
Darren Tucker909d8582010-01-08 19:02:40 +11002149 ret = CC_REDISPLAY;
2150 }
2151
Damien Miller02e87802013-08-21 02:38:51 +10002152 free(line);
Darren Tucker909d8582010-01-08 19:02:40 +11002153 return ret;
2154}
Darren Tuckere67f7db2010-01-08 19:50:02 +11002155#endif /* USE_LIBEDIT */
Darren Tucker909d8582010-01-08 19:02:40 +11002156
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002157static int
Darren Tucker21063192010-01-08 17:10:36 +11002158interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
Damien Miller20e1fab2004-02-18 14:30:55 +11002159{
Darren Tucker909d8582010-01-08 19:02:40 +11002160 char *remote_path;
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002161 char *dir = NULL, *startdir = NULL;
Damien Miller20e1fab2004-02-18 14:30:55 +11002162 char cmd[2048];
Damien Miller0e2c1022005-08-12 22:16:22 +10002163 int err, interactive;
Darren Tucker2d963d82004-11-07 20:04:10 +11002164 EditLine *el = NULL;
2165#ifdef USE_LIBEDIT
2166 History *hl = NULL;
2167 HistEvent hev;
2168 extern char *__progname;
Darren Tucker909d8582010-01-08 19:02:40 +11002169 struct complete_ctx complete_ctx;
Darren Tucker2d963d82004-11-07 20:04:10 +11002170
2171 if (!batchmode && isatty(STDIN_FILENO)) {
2172 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2173 fatal("Couldn't initialise editline");
2174 if ((hl = history_init()) == NULL)
2175 fatal("Couldn't initialise editline history");
2176 history(hl, &hev, H_SETSIZE, 100);
2177 el_set(el, EL_HIST, history, hl);
2178
2179 el_set(el, EL_PROMPT, prompt);
2180 el_set(el, EL_EDITOR, "emacs");
2181 el_set(el, EL_TERMINAL, NULL);
2182 el_set(el, EL_SIGNAL, 1);
2183 el_source(el, NULL);
Darren Tucker909d8582010-01-08 19:02:40 +11002184
2185 /* Tab Completion */
Damien Miller02e87802013-08-21 02:38:51 +10002186 el_set(el, EL_ADDFN, "ftp-complete",
Darren Tuckerd78739a2010-10-24 10:56:32 +11002187 "Context sensitive argument completion", complete);
Darren Tucker909d8582010-01-08 19:02:40 +11002188 complete_ctx.conn = conn;
2189 complete_ctx.remote_pathp = &remote_path;
2190 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2191 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
Damien Millere0ee7272013-08-21 02:42:35 +10002192 /* enable ctrl-left-arrow and ctrl-right-arrow */
2193 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
tb@openbsd.orgb9b0f2a2019-07-10 07:04:27 +00002194 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
Damien Millere0ee7272013-08-21 02:42:35 +10002195 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2196 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
Damien Miller61353b32013-09-14 09:45:32 +10002197 /* make ^w match ksh behaviour */
2198 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
Darren Tucker2d963d82004-11-07 20:04:10 +11002199 }
2200#endif /* USE_LIBEDIT */
Damien Miller20e1fab2004-02-18 14:30:55 +11002201
Darren Tucker909d8582010-01-08 19:02:40 +11002202 remote_path = do_realpath(conn, ".");
2203 if (remote_path == NULL)
Damien Miller20e1fab2004-02-18 14:30:55 +11002204 fatal("Need cwd");
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002205 startdir = xstrdup(remote_path);
Damien Miller20e1fab2004-02-18 14:30:55 +11002206
2207 if (file1 != NULL) {
2208 dir = xstrdup(file1);
Darren Tucker909d8582010-01-08 19:02:40 +11002209 dir = make_absolute(dir, remote_path);
Damien Miller20e1fab2004-02-18 14:30:55 +11002210
2211 if (remote_is_dir(conn, dir) && file2 == NULL) {
Damien Miller9303e652013-04-23 15:22:40 +10002212 if (!quiet)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00002213 mprintf("Changing to: %s\n", dir);
Damien Miller20e1fab2004-02-18 14:30:55 +11002214 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
Darren Tucker909d8582010-01-08 19:02:40 +11002215 if (parse_dispatch_command(conn, cmd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002216 &remote_path, startdir, 1, 0) != 0) {
Darren Tuckera627d422013-06-02 07:31:17 +10002217 free(dir);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002218 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002219 free(remote_path);
2220 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002221 return (-1);
Darren Tuckercd516ef2004-12-06 22:43:43 +11002222 }
Damien Miller20e1fab2004-02-18 14:30:55 +11002223 } else {
Darren Tucker0af24052012-10-05 10:41:25 +10002224 /* XXX this is wrong wrt quoting */
Damien Miller0d032412013-07-25 11:56:52 +10002225 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2226 global_aflag ? " -a" : "", dir,
2227 file2 == NULL ? "" : " ",
2228 file2 == NULL ? "" : file2);
Darren Tucker909d8582010-01-08 19:02:40 +11002229 err = parse_dispatch_command(conn, cmd,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002230 &remote_path, startdir, 1, 0);
Darren Tuckera627d422013-06-02 07:31:17 +10002231 free(dir);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002232 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002233 free(remote_path);
2234 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002235 return (err);
2236 }
Darren Tuckera627d422013-06-02 07:31:17 +10002237 free(dir);
Damien Miller20e1fab2004-02-18 14:30:55 +11002238 }
2239
millert@openbsd.orgdb995f22014-11-26 18:34:51 +00002240 setvbuf(stdout, NULL, _IOLBF, 0);
2241 setvbuf(infile, NULL, _IOLBF, 0);
Damien Miller20e1fab2004-02-18 14:30:55 +11002242
Damien Miller0e2c1022005-08-12 22:16:22 +10002243 interactive = !batchmode && isatty(STDIN_FILENO);
Damien Miller20e1fab2004-02-18 14:30:55 +11002244 err = 0;
2245 for (;;) {
Darren Tuckercdf547a2004-05-24 10:12:19 +10002246 signal(SIGINT, SIG_IGN);
2247
Darren Tucker2d963d82004-11-07 20:04:10 +11002248 if (el == NULL) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002249 if (interactive)
2250 printf("sftp> ");
Darren Tucker2d963d82004-11-07 20:04:10 +11002251 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002252 if (interactive)
2253 printf("\n");
Darren Tucker2d963d82004-11-07 20:04:10 +11002254 break;
2255 }
Darren Tucker2d963d82004-11-07 20:04:10 +11002256 } else {
2257#ifdef USE_LIBEDIT
2258 const char *line;
2259 int count = 0;
Damien Miller20e1fab2004-02-18 14:30:55 +11002260
Darren Tucker909d8582010-01-08 19:02:40 +11002261 if ((line = el_gets(el, &count)) == NULL ||
2262 count <= 0) {
Damien Miller0e2c1022005-08-12 22:16:22 +10002263 printf("\n");
2264 break;
2265 }
Darren Tucker2d963d82004-11-07 20:04:10 +11002266 history(hl, &hev, H_ENTER, line);
2267 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2268 fprintf(stderr, "Error: input line too long\n");
2269 continue;
2270 }
2271#endif /* USE_LIBEDIT */
Damien Miller20e1fab2004-02-18 14:30:55 +11002272 }
2273
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002274 cmd[strcspn(cmd, "\n")] = '\0';
Damien Miller20e1fab2004-02-18 14:30:55 +11002275
Darren Tuckercdf547a2004-05-24 10:12:19 +10002276 /* Handle user interrupts gracefully during commands */
2277 interrupted = 0;
2278 signal(SIGINT, cmd_interrupt);
2279
Darren Tucker909d8582010-01-08 19:02:40 +11002280 err = parse_dispatch_command(conn, cmd, &remote_path,
djm@openbsd.org5c1a6352018-11-16 02:30:20 +00002281 startdir, batchmode, !interactive && el == NULL);
Damien Miller20e1fab2004-02-18 14:30:55 +11002282 if (err != 0)
2283 break;
2284 }
djm@openbsd.org3455f1e2018-04-13 05:04:12 +00002285 signal(SIGCHLD, SIG_DFL);
Darren Tuckera627d422013-06-02 07:31:17 +10002286 free(remote_path);
djm@openbsd.org@openbsd.orgfbe8e7a2017-11-03 03:46:52 +00002287 free(startdir);
Darren Tuckera627d422013-06-02 07:31:17 +10002288 free(conn);
Damien Miller20e1fab2004-02-18 14:30:55 +11002289
Tim Rice027e8b12005-08-15 14:52:50 -07002290#ifdef USE_LIBEDIT
Damien Miller0e2c1022005-08-12 22:16:22 +10002291 if (el != NULL)
2292 el_end(el);
Tim Rice027e8b12005-08-15 14:52:50 -07002293#endif /* USE_LIBEDIT */
Damien Miller0e2c1022005-08-12 22:16:22 +10002294
Damien Miller20e1fab2004-02-18 14:30:55 +11002295 /* err == 1 signifies normal "quit" exit */
2296 return (err >= 0 ? 0 : -1);
2297}
Damien Miller62d57f62003-01-10 21:43:24 +11002298
Ben Lindstrombba81212001-06-25 05:01:22 +00002299static void
Damien Millercc685c12003-06-04 22:51:38 +10002300connect_to_server(char *path, char **args, int *in, int *out)
Damien Miller33804262001-02-04 23:20:18 +11002301{
2302 int c_in, c_out;
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002303
Damien Miller33804262001-02-04 23:20:18 +11002304#ifdef USE_PIPES
2305 int pin[2], pout[2];
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002306
Damien Miller33804262001-02-04 23:20:18 +11002307 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2308 fatal("pipe: %s", strerror(errno));
2309 *in = pin[0];
2310 *out = pout[1];
2311 c_in = pout[0];
2312 c_out = pin[1];
2313#else /* USE_PIPES */
2314 int inout[2];
Ben Lindstromb1f483f2002-06-23 21:27:18 +00002315
Damien Miller33804262001-02-04 23:20:18 +11002316 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2317 fatal("socketpair: %s", strerror(errno));
2318 *in = *out = inout[0];
2319 c_in = c_out = inout[1];
2320#endif /* USE_PIPES */
2321
Damien Millercc685c12003-06-04 22:51:38 +10002322 if ((sshpid = fork()) == -1)
Damien Miller33804262001-02-04 23:20:18 +11002323 fatal("fork: %s", strerror(errno));
Damien Millercc685c12003-06-04 22:51:38 +10002324 else if (sshpid == 0) {
Damien Miller33804262001-02-04 23:20:18 +11002325 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2326 (dup2(c_out, STDOUT_FILENO) == -1)) {
2327 fprintf(stderr, "dup2: %s\n", strerror(errno));
Damien Miller350327c2004-06-15 10:24:13 +10002328 _exit(1);
Damien Miller33804262001-02-04 23:20:18 +11002329 }
2330 close(*in);
2331 close(*out);
2332 close(c_in);
2333 close(c_out);
Darren Tuckercdf547a2004-05-24 10:12:19 +10002334
2335 /*
2336 * The underlying ssh is in the same process group, so we must
Darren Tuckerfc959702004-07-17 16:12:08 +10002337 * ignore SIGINT if we want to gracefully abort commands,
2338 * otherwise the signal will make it to the ssh process and
Darren Tuckerb8b17e92010-01-15 11:46:03 +11002339 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2340 * underlying ssh, it must *not* ignore that signal.
Darren Tuckercdf547a2004-05-24 10:12:19 +10002341 */
2342 signal(SIGINT, SIG_IGN);
Darren Tuckerb8b17e92010-01-15 11:46:03 +11002343 signal(SIGTERM, SIG_DFL);
Darren Tuckerbd12f172004-06-18 16:23:43 +10002344 execvp(path, args);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002345 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
Damien Miller350327c2004-06-15 10:24:13 +10002346 _exit(1);
Damien Miller33804262001-02-04 23:20:18 +11002347 }
2348
Damien Millercc685c12003-06-04 22:51:38 +10002349 signal(SIGTERM, killchild);
2350 signal(SIGINT, killchild);
2351 signal(SIGHUP, killchild);
millert@openbsd.org2c6697c2016-10-18 12:41:22 +00002352 signal(SIGTSTP, suspchild);
2353 signal(SIGTTIN, suspchild);
2354 signal(SIGTTOU, suspchild);
djm@openbsd.org3455f1e2018-04-13 05:04:12 +00002355 signal(SIGCHLD, sigchld_handler);
Damien Miller33804262001-02-04 23:20:18 +11002356 close(c_in);
2357 close(c_out);
2358}
2359
Ben Lindstrombba81212001-06-25 05:01:22 +00002360static void
Damien Miller33804262001-02-04 23:20:18 +11002361usage(void)
2362{
Damien Miller025e01c2002-02-08 22:06:29 +11002363 extern char *__progname;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002364
Ben Lindstrom1e243242001-09-18 05:38:44 +00002365 fprintf(stderr,
djm@openbsd.org3575f0b2017-05-02 08:54:19 +00002366 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
tb@openbsd.org622dedf2019-01-21 22:50:42 +00002367 " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n"
2368 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2369 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2370 " destination\n",
millert@openbsd.org887669e2017-10-21 23:06:24 +00002371 __progname);
Damien Miller33804262001-02-04 23:20:18 +11002372 exit(1);
2373}
2374
Kevin Stevesef4eea92001-02-05 12:42:17 +00002375int
Damien Miller33804262001-02-04 23:20:18 +11002376main(int argc, char **argv)
2377{
millert@openbsd.org887669e2017-10-21 23:06:24 +00002378 int in, out, ch, err, tmp, port = -1;
2379 char *host = NULL, *user, *cp, *file2 = NULL;
Ben Lindstrom387c4722001-05-08 20:27:25 +00002380 int debug_level = 0, sshver = 2;
2381 char *file1 = NULL, *sftp_server = NULL;
Damien Millerd14ee1e2002-02-05 12:27:31 +11002382 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
Damien Miller65e42f82010-09-24 22:15:11 +10002383 const char *errstr;
Ben Lindstrom387c4722001-05-08 20:27:25 +00002384 LogLevel ll = SYSLOG_LEVEL_INFO;
2385 arglist args;
Damien Millerd7686fd2001-02-10 00:40:03 +11002386 extern int optind;
2387 extern char *optarg;
Darren Tucker21063192010-01-08 17:10:36 +11002388 struct sftp_conn *conn;
2389 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2390 size_t num_requests = DEFAULT_NUM_REQUESTS;
Damien Miller65e42f82010-09-24 22:15:11 +10002391 long long limit_kbps = 0;
Damien Miller33804262001-02-04 23:20:18 +11002392
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,
tb@openbsd.org622dedf2019-01-21 22:50:42 +00002412 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:J: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':
tb@openbsd.org622dedf2019-01-21 22:50:42 +00002422 case 'J':
Darren Tucker46bbbe32009-10-07 08:21:48 +11002423 case 'c':
2424 case 'i':
2425 case 'o':
Darren Tuckerc4dc4f52010-01-08 18:50:04 +11002426 addargs(&args, "-%c", ch);
2427 addargs(&args, "%s", optarg);
Darren Tucker46bbbe32009-10-07 08:21:48 +11002428 break;
2429 case 'q':
Damien Miller9303e652013-04-23 15:22:40 +10002430 ll = SYSLOG_LEVEL_ERROR;
2431 quiet = 1;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002432 showprogress = 0;
2433 addargs(&args, "-%c", ch);
Damien Millerd7686fd2001-02-10 00:40:03 +11002434 break;
Darren Tucker282b4022009-10-07 08:23:06 +11002435 case 'P':
millert@openbsd.org887669e2017-10-21 23:06:24 +00002436 port = a2port(optarg);
2437 if (port <= 0)
2438 fatal("Bad port \"%s\"\n", optarg);
Darren Tucker282b4022009-10-07 08:23:06 +11002439 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002440 case 'v':
Ben Lindstrom387c4722001-05-08 20:27:25 +00002441 if (debug_level < 3) {
2442 addargs(&args, "-v");
2443 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2444 }
2445 debug_level++;
Damien Millerd7686fd2001-02-10 00:40:03 +11002446 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002447 case '1':
Ben Lindstrom387c4722001-05-08 20:27:25 +00002448 sshver = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +11002449 if (sftp_server == NULL)
2450 sftp_server = _PATH_SFTP_SERVER;
2451 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002452 case '2':
2453 sshver = 2;
Damien Millerd7686fd2001-02-10 00:40:03 +11002454 break;
Damien Miller0d032412013-07-25 11:56:52 +10002455 case 'a':
2456 global_aflag = 1;
2457 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002458 case 'B':
2459 copy_buffer_len = strtol(optarg, &cp, 10);
2460 if (copy_buffer_len == 0 || *cp != '\0')
2461 fatal("Invalid buffer size \"%s\"", optarg);
Damien Millerd7686fd2001-02-10 00:40:03 +11002462 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002463 case 'b':
Damien Miller44f75c12004-01-21 10:58:47 +11002464 if (batchmode)
2465 fatal("Batch file already specified.");
2466
2467 /* Allow "-" as stdin */
Darren Tuckerfc959702004-07-17 16:12:08 +10002468 if (strcmp(optarg, "-") != 0 &&
Damien Miller0dc1bef2005-07-17 17:22:45 +10002469 (infile = fopen(optarg, "r")) == NULL)
Damien Miller44f75c12004-01-21 10:58:47 +11002470 fatal("%s (%s).", strerror(errno), optarg);
Damien Miller62d57f62003-01-10 21:43:24 +11002471 showprogress = 0;
Damien Miller9303e652013-04-23 15:22:40 +10002472 quiet = batchmode = 1;
Damien Miller64e8d442005-03-01 21:16:47 +11002473 addargs(&args, "-obatchmode yes");
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002474 break;
Damien Millerf29238e2013-10-17 11:48:52 +11002475 case 'f':
2476 global_fflag = 1;
2477 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +11002478 case 'p':
2479 global_pflag = 1;
2480 break;
Darren Tucker282b4022009-10-07 08:23:06 +11002481 case 'D':
Damien Millerd14ee1e2002-02-05 12:27:31 +11002482 sftp_direct = optarg;
2483 break;
Damien Miller65e42f82010-09-24 22:15:11 +10002484 case 'l':
2485 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2486 &errstr);
2487 if (errstr != NULL)
2488 usage();
2489 limit_kbps *= 1024; /* kbps */
2490 break;
Darren Tucker1b0dd172009-10-07 08:37:48 +11002491 case 'r':
2492 global_rflag = 1;
2493 break;
Damien Miller16a13332002-02-13 14:03:56 +11002494 case 'R':
2495 num_requests = strtol(optarg, &cp, 10);
2496 if (num_requests == 0 || *cp != '\0')
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002497 fatal("Invalid number of requests \"%s\"",
Damien Miller16a13332002-02-13 14:03:56 +11002498 optarg);
2499 break;
Darren Tucker46bbbe32009-10-07 08:21:48 +11002500 case 's':
2501 sftp_server = optarg;
2502 break;
2503 case 'S':
2504 ssh_program = optarg;
2505 replacearg(&args, 0, "%s", ssh_program);
2506 break;
Damien Millerd7686fd2001-02-10 00:40:03 +11002507 case 'h':
2508 default:
Damien Miller33804262001-02-04 23:20:18 +11002509 usage();
2510 }
2511 }
2512
Damien Millerc0f27d82004-03-08 23:12:19 +11002513 if (!isatty(STDERR_FILENO))
2514 showprogress = 0;
2515
Ben Lindstrom2f3d52a2002-04-02 21:06:18 +00002516 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2517
Damien Millerd14ee1e2002-02-05 12:27:31 +11002518 if (sftp_direct == NULL) {
2519 if (optind == argc || argc > (optind + 2))
2520 usage();
millert@openbsd.org887669e2017-10-21 23:06:24 +00002521 argv += optind;
Damien Miller33804262001-02-04 23:20:18 +11002522
millert@openbsd.org887669e2017-10-21 23:06:24 +00002523 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2524 case -1:
2525 usage();
2526 break;
2527 case 0:
2528 if (tmp != -1)
2529 port = tmp;
2530 break;
2531 default:
dtucker@openbsd.org25e3bcc2019-06-07 03:47:12 +00002532 /* Try with user, host and path. */
millert@openbsd.org887669e2017-10-21 23:06:24 +00002533 if (parse_user_host_path(*argv, &user, &host,
dtucker@openbsd.org25e3bcc2019-06-07 03:47:12 +00002534 &file1) == 0)
2535 break;
2536 /* Try with user and host. */
2537 if (parse_user_host_port(*argv, &user, &host, NULL)
2538 == 0)
2539 break;
2540 /* Treat as a plain hostname. */
2541 host = xstrdup(*argv);
2542 host = cleanhostname(host);
millert@openbsd.org887669e2017-10-21 23:06:24 +00002543 break;
Damien Millerd14ee1e2002-02-05 12:27:31 +11002544 }
millert@openbsd.org887669e2017-10-21 23:06:24 +00002545 file2 = *(argv + 1);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002546
Damien Millerd14ee1e2002-02-05 12:27:31 +11002547 if (!*host) {
2548 fprintf(stderr, "Missing hostname\n");
Damien Miller33804262001-02-04 23:20:18 +11002549 usage();
2550 }
Damien Millerd14ee1e2002-02-05 12:27:31 +11002551
millert@openbsd.org887669e2017-10-21 23:06:24 +00002552 if (port != -1)
2553 addargs(&args, "-oPort %d", port);
2554 if (user != NULL) {
2555 addargs(&args, "-l");
2556 addargs(&args, "%s", user);
2557 }
Damien Millerd14ee1e2002-02-05 12:27:31 +11002558 addargs(&args, "-oProtocol %d", sshver);
2559
2560 /* no subsystem if the server-spec contains a '/' */
2561 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2562 addargs(&args, "-s");
2563
Darren Tuckerb8c884a2010-01-08 18:53:43 +11002564 addargs(&args, "--");
Damien Millerd14ee1e2002-02-05 12:27:31 +11002565 addargs(&args, "%s", host);
Ben Lindstrom6328ab32002-03-22 02:54:23 +00002566 addargs(&args, "%s", (sftp_server != NULL ?
Damien Millerd14ee1e2002-02-05 12:27:31 +11002567 sftp_server : "sftp"));
Damien Millerd14ee1e2002-02-05 12:27:31 +11002568
Damien Millercc685c12003-06-04 22:51:38 +10002569 connect_to_server(ssh_program, args.list, &in, &out);
Damien Millerd14ee1e2002-02-05 12:27:31 +11002570 } else {
2571 args.list = NULL;
2572 addargs(&args, "sftp-server");
2573
Damien Millercc685c12003-06-04 22:51:38 +10002574 connect_to_server(sftp_direct, args.list, &in, &out);
Damien Miller33804262001-02-04 23:20:18 +11002575 }
Damien Miller3eec6b72006-01-31 21:49:27 +11002576 freeargs(&args);
Damien Miller33804262001-02-04 23:20:18 +11002577
Damien Miller65e42f82010-09-24 22:15:11 +10002578 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
Darren Tucker21063192010-01-08 17:10:36 +11002579 if (conn == NULL)
2580 fatal("Couldn't initialise connection to server");
2581
Damien Miller9303e652013-04-23 15:22:40 +10002582 if (!quiet) {
Darren Tucker21063192010-01-08 17:10:36 +11002583 if (sftp_direct == NULL)
2584 fprintf(stderr, "Connected to %s.\n", host);
2585 else
2586 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2587 }
2588
2589 err = interactive_loop(conn, file1, file2);
Damien Miller33804262001-02-04 23:20:18 +11002590
Ben Lindstrom10b9bf92001-02-26 20:04:45 +00002591#if !defined(USE_PIPES)
Damien Miller37294fb2005-07-17 17:18:49 +10002592 shutdown(in, SHUT_RDWR);
2593 shutdown(out, SHUT_RDWR);
Ben Lindstrom10b9bf92001-02-26 20:04:45 +00002594#endif
2595
Damien Miller33804262001-02-04 23:20:18 +11002596 close(in);
2597 close(out);
Damien Miller44f75c12004-01-21 10:58:47 +11002598 if (batchmode)
Ben Lindstrom562c26b2001-03-07 01:26:48 +00002599 fclose(infile);
Damien Miller33804262001-02-04 23:20:18 +11002600
bluhm@openbsd.orge7751aa2018-04-26 14:47:03 +00002601 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
Ben Lindstrom47fd8112002-04-02 20:48:19 +00002602 if (errno != EINTR)
2603 fatal("Couldn't wait for ssh process: %s",
2604 strerror(errno));
Damien Miller33804262001-02-04 23:20:18 +11002605
Damien Miller956f3fb2003-01-10 21:40:00 +11002606 exit(err == 0 ? 0 : 1);
Damien Miller33804262001-02-04 23:20:18 +11002607}