xref: /freebsd/crypto/openssh/sftp.c (revision d74d50a84bda49cca847afc2f65bf790d6af7361)
11e8db6e2SBrian Feldman /*
2efcad6b7SDag-Erling Smørgrav  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
31e8db6e2SBrian Feldman  *
4efcad6b7SDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
5efcad6b7SDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
6efcad6b7SDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
71e8db6e2SBrian Feldman  *
8efcad6b7SDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9efcad6b7SDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10efcad6b7SDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11efcad6b7SDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12efcad6b7SDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13efcad6b7SDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14efcad6b7SDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
151e8db6e2SBrian Feldman  */
161e8db6e2SBrian Feldman 
171e8db6e2SBrian Feldman #include "includes.h"
181e8db6e2SBrian Feldman 
19d74d50a8SDag-Erling Smørgrav RCSID("$OpenBSD: sftp.c,v 1.56 2004/07/11 17:48:47 deraadt Exp $");
201e8db6e2SBrian Feldman 
211e8db6e2SBrian Feldman #include "buffer.h"
221e8db6e2SBrian Feldman #include "xmalloc.h"
231e8db6e2SBrian Feldman #include "log.h"
241e8db6e2SBrian Feldman #include "pathnames.h"
25ae1f160dSDag-Erling Smørgrav #include "misc.h"
261e8db6e2SBrian Feldman 
271e8db6e2SBrian Feldman #include "sftp.h"
281e8db6e2SBrian Feldman #include "sftp-common.h"
291e8db6e2SBrian Feldman #include "sftp-client.h"
30efcad6b7SDag-Erling Smørgrav 
31efcad6b7SDag-Erling Smørgrav /* File to read commands from */
32efcad6b7SDag-Erling Smørgrav FILE* infile;
33efcad6b7SDag-Erling Smørgrav 
34efcad6b7SDag-Erling Smørgrav /* Are we in batchfile mode? */
35efcad6b7SDag-Erling Smørgrav int batchmode = 0;
36efcad6b7SDag-Erling Smørgrav 
37efcad6b7SDag-Erling Smørgrav /* Size of buffer used when copying files */
38efcad6b7SDag-Erling Smørgrav size_t copy_buffer_len = 32768;
39efcad6b7SDag-Erling Smørgrav 
40efcad6b7SDag-Erling Smørgrav /* Number of concurrent outstanding requests */
41efcad6b7SDag-Erling Smørgrav size_t num_requests = 16;
42efcad6b7SDag-Erling Smørgrav 
43efcad6b7SDag-Erling Smørgrav /* PID of ssh transport process */
44efcad6b7SDag-Erling Smørgrav static pid_t sshpid = -1;
45efcad6b7SDag-Erling Smørgrav 
46efcad6b7SDag-Erling Smørgrav /* This is set to 0 if the progressmeter is not desired. */
4752028650SDag-Erling Smørgrav int showprogress = 1;
48efcad6b7SDag-Erling Smørgrav 
49d74d50a8SDag-Erling Smørgrav /* SIGINT received during command processing */
50d74d50a8SDag-Erling Smørgrav volatile sig_atomic_t interrupted = 0;
51d74d50a8SDag-Erling Smørgrav 
52d74d50a8SDag-Erling Smørgrav /* I wish qsort() took a separate ctx for the comparison function...*/
53d74d50a8SDag-Erling Smørgrav int sort_flag;
54d74d50a8SDag-Erling Smørgrav 
55efcad6b7SDag-Erling Smørgrav int remote_glob(struct sftp_conn *, const char *, int,
56efcad6b7SDag-Erling Smørgrav     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
571e8db6e2SBrian Feldman 
5883d2307dSDag-Erling Smørgrav extern char *__progname;
5983d2307dSDag-Erling Smørgrav 
60efcad6b7SDag-Erling Smørgrav /* Separators for interactive commands */
61efcad6b7SDag-Erling Smørgrav #define WHITESPACE " \t\r\n"
621e8db6e2SBrian Feldman 
63d74d50a8SDag-Erling Smørgrav /* ls flags */
64d74d50a8SDag-Erling Smørgrav #define LS_LONG_VIEW	0x01	/* Full view ala ls -l */
65d74d50a8SDag-Erling Smørgrav #define LS_SHORT_VIEW	0x02	/* Single row view ala ls -1 */
66d74d50a8SDag-Erling Smørgrav #define LS_NUMERIC_VIEW	0x04	/* Long view with numeric uid/gid */
67d74d50a8SDag-Erling Smørgrav #define LS_NAME_SORT	0x08	/* Sort by name (default) */
68d74d50a8SDag-Erling Smørgrav #define LS_TIME_SORT	0x10	/* Sort by mtime */
69d74d50a8SDag-Erling Smørgrav #define LS_SIZE_SORT	0x20	/* Sort by file size */
70d74d50a8SDag-Erling Smørgrav #define LS_REVERSE_SORT	0x40	/* Reverse sort order */
71d74d50a8SDag-Erling Smørgrav #define LS_SHOW_ALL	0x80	/* Don't skip filenames starting with '.' */
72d74d50a8SDag-Erling Smørgrav 
73d74d50a8SDag-Erling Smørgrav #define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
74d74d50a8SDag-Erling Smørgrav #define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
75efcad6b7SDag-Erling Smørgrav 
76efcad6b7SDag-Erling Smørgrav /* Commands for interactive mode */
77efcad6b7SDag-Erling Smørgrav #define I_CHDIR		1
78efcad6b7SDag-Erling Smørgrav #define I_CHGRP		2
79efcad6b7SDag-Erling Smørgrav #define I_CHMOD		3
80efcad6b7SDag-Erling Smørgrav #define I_CHOWN		4
81efcad6b7SDag-Erling Smørgrav #define I_GET		5
82efcad6b7SDag-Erling Smørgrav #define I_HELP		6
83efcad6b7SDag-Erling Smørgrav #define I_LCHDIR	7
84efcad6b7SDag-Erling Smørgrav #define I_LLS		8
85efcad6b7SDag-Erling Smørgrav #define I_LMKDIR	9
86efcad6b7SDag-Erling Smørgrav #define I_LPWD		10
87efcad6b7SDag-Erling Smørgrav #define I_LS		11
88efcad6b7SDag-Erling Smørgrav #define I_LUMASK	12
89efcad6b7SDag-Erling Smørgrav #define I_MKDIR		13
90efcad6b7SDag-Erling Smørgrav #define I_PUT		14
91efcad6b7SDag-Erling Smørgrav #define I_PWD		15
92efcad6b7SDag-Erling Smørgrav #define I_QUIT		16
93efcad6b7SDag-Erling Smørgrav #define I_RENAME	17
94efcad6b7SDag-Erling Smørgrav #define I_RM		18
95efcad6b7SDag-Erling Smørgrav #define I_RMDIR		19
96efcad6b7SDag-Erling Smørgrav #define I_SHELL		20
97efcad6b7SDag-Erling Smørgrav #define I_SYMLINK	21
98efcad6b7SDag-Erling Smørgrav #define I_VERSION	22
99efcad6b7SDag-Erling Smørgrav #define I_PROGRESS	23
100efcad6b7SDag-Erling Smørgrav 
101efcad6b7SDag-Erling Smørgrav struct CMD {
102efcad6b7SDag-Erling Smørgrav 	const char *c;
103efcad6b7SDag-Erling Smørgrav 	const int n;
104efcad6b7SDag-Erling Smørgrav };
105efcad6b7SDag-Erling Smørgrav 
106efcad6b7SDag-Erling Smørgrav static const struct CMD cmds[] = {
107efcad6b7SDag-Erling Smørgrav 	{ "bye",	I_QUIT },
108efcad6b7SDag-Erling Smørgrav 	{ "cd",		I_CHDIR },
109efcad6b7SDag-Erling Smørgrav 	{ "chdir",	I_CHDIR },
110efcad6b7SDag-Erling Smørgrav 	{ "chgrp",	I_CHGRP },
111efcad6b7SDag-Erling Smørgrav 	{ "chmod",	I_CHMOD },
112efcad6b7SDag-Erling Smørgrav 	{ "chown",	I_CHOWN },
113efcad6b7SDag-Erling Smørgrav 	{ "dir",	I_LS },
114efcad6b7SDag-Erling Smørgrav 	{ "exit",	I_QUIT },
115efcad6b7SDag-Erling Smørgrav 	{ "get",	I_GET },
116efcad6b7SDag-Erling Smørgrav 	{ "mget",	I_GET },
117efcad6b7SDag-Erling Smørgrav 	{ "help",	I_HELP },
118efcad6b7SDag-Erling Smørgrav 	{ "lcd",	I_LCHDIR },
119efcad6b7SDag-Erling Smørgrav 	{ "lchdir",	I_LCHDIR },
120efcad6b7SDag-Erling Smørgrav 	{ "lls",	I_LLS },
121efcad6b7SDag-Erling Smørgrav 	{ "lmkdir",	I_LMKDIR },
122efcad6b7SDag-Erling Smørgrav 	{ "ln",		I_SYMLINK },
123efcad6b7SDag-Erling Smørgrav 	{ "lpwd",	I_LPWD },
124efcad6b7SDag-Erling Smørgrav 	{ "ls",		I_LS },
125efcad6b7SDag-Erling Smørgrav 	{ "lumask",	I_LUMASK },
126efcad6b7SDag-Erling Smørgrav 	{ "mkdir",	I_MKDIR },
127efcad6b7SDag-Erling Smørgrav 	{ "progress",	I_PROGRESS },
128efcad6b7SDag-Erling Smørgrav 	{ "put",	I_PUT },
129efcad6b7SDag-Erling Smørgrav 	{ "mput",	I_PUT },
130efcad6b7SDag-Erling Smørgrav 	{ "pwd",	I_PWD },
131efcad6b7SDag-Erling Smørgrav 	{ "quit",	I_QUIT },
132efcad6b7SDag-Erling Smørgrav 	{ "rename",	I_RENAME },
133efcad6b7SDag-Erling Smørgrav 	{ "rm",		I_RM },
134efcad6b7SDag-Erling Smørgrav 	{ "rmdir",	I_RMDIR },
135efcad6b7SDag-Erling Smørgrav 	{ "symlink",	I_SYMLINK },
136efcad6b7SDag-Erling Smørgrav 	{ "version",	I_VERSION },
137efcad6b7SDag-Erling Smørgrav 	{ "!",		I_SHELL },
138efcad6b7SDag-Erling Smørgrav 	{ "?",		I_HELP },
139efcad6b7SDag-Erling Smørgrav 	{ NULL,			-1}
140efcad6b7SDag-Erling Smørgrav };
141efcad6b7SDag-Erling Smørgrav 
142efcad6b7SDag-Erling Smørgrav int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
143efcad6b7SDag-Erling Smørgrav 
144efcad6b7SDag-Erling Smørgrav static void
145d74d50a8SDag-Erling Smørgrav killchild(int signo)
146d74d50a8SDag-Erling Smørgrav {
147d74d50a8SDag-Erling Smørgrav 	if (sshpid > 1)
148d74d50a8SDag-Erling Smørgrav 		kill(sshpid, SIGTERM);
149d74d50a8SDag-Erling Smørgrav 
150d74d50a8SDag-Erling Smørgrav 	_exit(1);
151d74d50a8SDag-Erling Smørgrav }
152d74d50a8SDag-Erling Smørgrav 
153d74d50a8SDag-Erling Smørgrav static void
154d74d50a8SDag-Erling Smørgrav cmd_interrupt(int signo)
155d74d50a8SDag-Erling Smørgrav {
156d74d50a8SDag-Erling Smørgrav 	const char msg[] = "\rInterrupt  \n";
157d74d50a8SDag-Erling Smørgrav 
158d74d50a8SDag-Erling Smørgrav 	write(STDERR_FILENO, msg, sizeof(msg) - 1);
159d74d50a8SDag-Erling Smørgrav 	interrupted = 1;
160d74d50a8SDag-Erling Smørgrav }
161d74d50a8SDag-Erling Smørgrav 
162d74d50a8SDag-Erling Smørgrav static void
163efcad6b7SDag-Erling Smørgrav help(void)
164efcad6b7SDag-Erling Smørgrav {
165efcad6b7SDag-Erling Smørgrav 	printf("Available commands:\n");
166efcad6b7SDag-Erling Smørgrav 	printf("cd path                       Change remote directory to 'path'\n");
167efcad6b7SDag-Erling Smørgrav 	printf("lcd path                      Change local directory to 'path'\n");
168efcad6b7SDag-Erling Smørgrav 	printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
169efcad6b7SDag-Erling Smørgrav 	printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
170efcad6b7SDag-Erling Smørgrav 	printf("chown own path                Change owner of file 'path' to 'own'\n");
171efcad6b7SDag-Erling Smørgrav 	printf("help                          Display this help text\n");
172efcad6b7SDag-Erling Smørgrav 	printf("get remote-path [local-path]  Download file\n");
173efcad6b7SDag-Erling Smørgrav 	printf("lls [ls-options [path]]       Display local directory listing\n");
174efcad6b7SDag-Erling Smørgrav 	printf("ln oldpath newpath            Symlink remote file\n");
175efcad6b7SDag-Erling Smørgrav 	printf("lmkdir path                   Create local directory\n");
176efcad6b7SDag-Erling Smørgrav 	printf("lpwd                          Print local working directory\n");
177efcad6b7SDag-Erling Smørgrav 	printf("ls [path]                     Display remote directory listing\n");
178efcad6b7SDag-Erling Smørgrav 	printf("lumask umask                  Set local umask to 'umask'\n");
179efcad6b7SDag-Erling Smørgrav 	printf("mkdir path                    Create remote directory\n");
180efcad6b7SDag-Erling Smørgrav 	printf("progress                      Toggle display of progress meter\n");
181efcad6b7SDag-Erling Smørgrav 	printf("put local-path [remote-path]  Upload file\n");
182efcad6b7SDag-Erling Smørgrav 	printf("pwd                           Display remote working directory\n");
183efcad6b7SDag-Erling Smørgrav 	printf("exit                          Quit sftp\n");
184efcad6b7SDag-Erling Smørgrav 	printf("quit                          Quit sftp\n");
185efcad6b7SDag-Erling Smørgrav 	printf("rename oldpath newpath        Rename remote file\n");
186efcad6b7SDag-Erling Smørgrav 	printf("rmdir path                    Remove remote directory\n");
187efcad6b7SDag-Erling Smørgrav 	printf("rm path                       Delete remote file\n");
188efcad6b7SDag-Erling Smørgrav 	printf("symlink oldpath newpath       Symlink remote file\n");
189efcad6b7SDag-Erling Smørgrav 	printf("version                       Show SFTP version\n");
190efcad6b7SDag-Erling Smørgrav 	printf("!command                      Execute 'command' in local shell\n");
191efcad6b7SDag-Erling Smørgrav 	printf("!                             Escape to local shell\n");
192efcad6b7SDag-Erling Smørgrav 	printf("?                             Synonym for help\n");
193efcad6b7SDag-Erling Smørgrav }
194efcad6b7SDag-Erling Smørgrav 
195efcad6b7SDag-Erling Smørgrav static void
196efcad6b7SDag-Erling Smørgrav local_do_shell(const char *args)
197efcad6b7SDag-Erling Smørgrav {
198efcad6b7SDag-Erling Smørgrav 	int status;
199efcad6b7SDag-Erling Smørgrav 	char *shell;
200efcad6b7SDag-Erling Smørgrav 	pid_t pid;
201efcad6b7SDag-Erling Smørgrav 
202efcad6b7SDag-Erling Smørgrav 	if (!*args)
203efcad6b7SDag-Erling Smørgrav 		args = NULL;
204efcad6b7SDag-Erling Smørgrav 
205efcad6b7SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL)
206efcad6b7SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
207efcad6b7SDag-Erling Smørgrav 
208efcad6b7SDag-Erling Smørgrav 	if ((pid = fork()) == -1)
209efcad6b7SDag-Erling Smørgrav 		fatal("Couldn't fork: %s", strerror(errno));
210efcad6b7SDag-Erling Smørgrav 
211efcad6b7SDag-Erling Smørgrav 	if (pid == 0) {
212efcad6b7SDag-Erling Smørgrav 		/* XXX: child has pipe fds to ssh subproc open - issue? */
213efcad6b7SDag-Erling Smørgrav 		if (args) {
214efcad6b7SDag-Erling Smørgrav 			debug3("Executing %s -c \"%s\"", shell, args);
215efcad6b7SDag-Erling Smørgrav 			execl(shell, shell, "-c", args, (char *)NULL);
216efcad6b7SDag-Erling Smørgrav 		} else {
217efcad6b7SDag-Erling Smørgrav 			debug3("Executing %s", shell);
218efcad6b7SDag-Erling Smørgrav 			execl(shell, shell, (char *)NULL);
219efcad6b7SDag-Erling Smørgrav 		}
220efcad6b7SDag-Erling Smørgrav 		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
221efcad6b7SDag-Erling Smørgrav 		    strerror(errno));
222efcad6b7SDag-Erling Smørgrav 		_exit(1);
223efcad6b7SDag-Erling Smørgrav 	}
224efcad6b7SDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1)
225efcad6b7SDag-Erling Smørgrav 		if (errno != EINTR)
226efcad6b7SDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
227efcad6b7SDag-Erling Smørgrav 	if (!WIFEXITED(status))
228efcad6b7SDag-Erling Smørgrav 		error("Shell exited abormally");
229efcad6b7SDag-Erling Smørgrav 	else if (WEXITSTATUS(status))
230efcad6b7SDag-Erling Smørgrav 		error("Shell exited with status %d", WEXITSTATUS(status));
231efcad6b7SDag-Erling Smørgrav }
232efcad6b7SDag-Erling Smørgrav 
233efcad6b7SDag-Erling Smørgrav static void
234efcad6b7SDag-Erling Smørgrav local_do_ls(const char *args)
235efcad6b7SDag-Erling Smørgrav {
236efcad6b7SDag-Erling Smørgrav 	if (!args || !*args)
237efcad6b7SDag-Erling Smørgrav 		local_do_shell(_PATH_LS);
238efcad6b7SDag-Erling Smørgrav 	else {
239efcad6b7SDag-Erling Smørgrav 		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
240efcad6b7SDag-Erling Smørgrav 		char *buf = xmalloc(len);
241efcad6b7SDag-Erling Smørgrav 
242efcad6b7SDag-Erling Smørgrav 		/* XXX: quoting - rip quoting code from ftp? */
243efcad6b7SDag-Erling Smørgrav 		snprintf(buf, len, _PATH_LS " %s", args);
244efcad6b7SDag-Erling Smørgrav 		local_do_shell(buf);
245efcad6b7SDag-Erling Smørgrav 		xfree(buf);
246efcad6b7SDag-Erling Smørgrav 	}
247efcad6b7SDag-Erling Smørgrav }
248efcad6b7SDag-Erling Smørgrav 
249efcad6b7SDag-Erling Smørgrav /* Strip one path (usually the pwd) from the start of another */
250efcad6b7SDag-Erling Smørgrav static char *
251efcad6b7SDag-Erling Smørgrav path_strip(char *path, char *strip)
252efcad6b7SDag-Erling Smørgrav {
253efcad6b7SDag-Erling Smørgrav 	size_t len;
254efcad6b7SDag-Erling Smørgrav 
255efcad6b7SDag-Erling Smørgrav 	if (strip == NULL)
256efcad6b7SDag-Erling Smørgrav 		return (xstrdup(path));
257efcad6b7SDag-Erling Smørgrav 
258efcad6b7SDag-Erling Smørgrav 	len = strlen(strip);
259efcad6b7SDag-Erling Smørgrav 	if (strip != NULL && strncmp(path, strip, len) == 0) {
260efcad6b7SDag-Erling Smørgrav 		if (strip[len - 1] != '/' && path[len] == '/')
261efcad6b7SDag-Erling Smørgrav 			len++;
262efcad6b7SDag-Erling Smørgrav 		return (xstrdup(path + len));
263efcad6b7SDag-Erling Smørgrav 	}
264efcad6b7SDag-Erling Smørgrav 
265efcad6b7SDag-Erling Smørgrav 	return (xstrdup(path));
266efcad6b7SDag-Erling Smørgrav }
267efcad6b7SDag-Erling Smørgrav 
268efcad6b7SDag-Erling Smørgrav static char *
269efcad6b7SDag-Erling Smørgrav path_append(char *p1, char *p2)
270efcad6b7SDag-Erling Smørgrav {
271efcad6b7SDag-Erling Smørgrav 	char *ret;
272efcad6b7SDag-Erling Smørgrav 	int len = strlen(p1) + strlen(p2) + 2;
273efcad6b7SDag-Erling Smørgrav 
274efcad6b7SDag-Erling Smørgrav 	ret = xmalloc(len);
275efcad6b7SDag-Erling Smørgrav 	strlcpy(ret, p1, len);
276efcad6b7SDag-Erling Smørgrav 	if (p1[strlen(p1) - 1] != '/')
277efcad6b7SDag-Erling Smørgrav 		strlcat(ret, "/", len);
278efcad6b7SDag-Erling Smørgrav 	strlcat(ret, p2, len);
279efcad6b7SDag-Erling Smørgrav 
280efcad6b7SDag-Erling Smørgrav 	return(ret);
281efcad6b7SDag-Erling Smørgrav }
282efcad6b7SDag-Erling Smørgrav 
283efcad6b7SDag-Erling Smørgrav static char *
284efcad6b7SDag-Erling Smørgrav make_absolute(char *p, char *pwd)
285efcad6b7SDag-Erling Smørgrav {
286d74d50a8SDag-Erling Smørgrav 	char *abs_str;
287efcad6b7SDag-Erling Smørgrav 
288efcad6b7SDag-Erling Smørgrav 	/* Derelativise */
289efcad6b7SDag-Erling Smørgrav 	if (p && p[0] != '/') {
290d74d50a8SDag-Erling Smørgrav 		abs_str = path_append(pwd, p);
291efcad6b7SDag-Erling Smørgrav 		xfree(p);
292d74d50a8SDag-Erling Smørgrav 		return(abs_str);
293efcad6b7SDag-Erling Smørgrav 	} else
294efcad6b7SDag-Erling Smørgrav 		return(p);
295efcad6b7SDag-Erling Smørgrav }
296efcad6b7SDag-Erling Smørgrav 
297efcad6b7SDag-Erling Smørgrav static int
298efcad6b7SDag-Erling Smørgrav infer_path(const char *p, char **ifp)
299efcad6b7SDag-Erling Smørgrav {
300efcad6b7SDag-Erling Smørgrav 	char *cp;
301efcad6b7SDag-Erling Smørgrav 
302efcad6b7SDag-Erling Smørgrav 	cp = strrchr(p, '/');
303efcad6b7SDag-Erling Smørgrav 	if (cp == NULL) {
304efcad6b7SDag-Erling Smørgrav 		*ifp = xstrdup(p);
305efcad6b7SDag-Erling Smørgrav 		return(0);
306efcad6b7SDag-Erling Smørgrav 	}
307efcad6b7SDag-Erling Smørgrav 
308efcad6b7SDag-Erling Smørgrav 	if (!cp[1]) {
309efcad6b7SDag-Erling Smørgrav 		error("Invalid path");
310efcad6b7SDag-Erling Smørgrav 		return(-1);
311efcad6b7SDag-Erling Smørgrav 	}
312efcad6b7SDag-Erling Smørgrav 
313efcad6b7SDag-Erling Smørgrav 	*ifp = xstrdup(cp + 1);
314efcad6b7SDag-Erling Smørgrav 	return(0);
315efcad6b7SDag-Erling Smørgrav }
316efcad6b7SDag-Erling Smørgrav 
317efcad6b7SDag-Erling Smørgrav static int
318efcad6b7SDag-Erling Smørgrav parse_getput_flags(const char **cpp, int *pflag)
319efcad6b7SDag-Erling Smørgrav {
320efcad6b7SDag-Erling Smørgrav 	const char *cp = *cpp;
321efcad6b7SDag-Erling Smørgrav 
322efcad6b7SDag-Erling Smørgrav 	/* Check for flags */
323efcad6b7SDag-Erling Smørgrav 	if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
324efcad6b7SDag-Erling Smørgrav 		switch (cp[1]) {
325efcad6b7SDag-Erling Smørgrav 		case 'p':
326efcad6b7SDag-Erling Smørgrav 		case 'P':
327efcad6b7SDag-Erling Smørgrav 			*pflag = 1;
328efcad6b7SDag-Erling Smørgrav 			break;
329efcad6b7SDag-Erling Smørgrav 		default:
330efcad6b7SDag-Erling Smørgrav 			error("Invalid flag -%c", cp[1]);
331efcad6b7SDag-Erling Smørgrav 			return(-1);
332efcad6b7SDag-Erling Smørgrav 		}
333efcad6b7SDag-Erling Smørgrav 		cp += 2;
334efcad6b7SDag-Erling Smørgrav 		*cpp = cp + strspn(cp, WHITESPACE);
335efcad6b7SDag-Erling Smørgrav 	}
336efcad6b7SDag-Erling Smørgrav 
337efcad6b7SDag-Erling Smørgrav 	return(0);
338efcad6b7SDag-Erling Smørgrav }
339efcad6b7SDag-Erling Smørgrav 
340efcad6b7SDag-Erling Smørgrav static int
341efcad6b7SDag-Erling Smørgrav parse_ls_flags(const char **cpp, int *lflag)
342efcad6b7SDag-Erling Smørgrav {
343efcad6b7SDag-Erling Smørgrav 	const char *cp = *cpp;
344efcad6b7SDag-Erling Smørgrav 
345d74d50a8SDag-Erling Smørgrav 	/* Defaults */
346d74d50a8SDag-Erling Smørgrav 	*lflag = LS_NAME_SORT;
347d74d50a8SDag-Erling Smørgrav 
348efcad6b7SDag-Erling Smørgrav 	/* Check for flags */
349efcad6b7SDag-Erling Smørgrav 	if (cp++[0] == '-') {
350efcad6b7SDag-Erling Smørgrav 		for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
351efcad6b7SDag-Erling Smørgrav 			switch (*cp) {
352efcad6b7SDag-Erling Smørgrav 			case 'l':
353d74d50a8SDag-Erling Smørgrav 				*lflag &= ~VIEW_FLAGS;
354d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_LONG_VIEW;
355efcad6b7SDag-Erling Smørgrav 				break;
356efcad6b7SDag-Erling Smørgrav 			case '1':
357d74d50a8SDag-Erling Smørgrav 				*lflag &= ~VIEW_FLAGS;
358d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_SHORT_VIEW;
359d74d50a8SDag-Erling Smørgrav 				break;
360d74d50a8SDag-Erling Smørgrav 			case 'n':
361d74d50a8SDag-Erling Smørgrav 				*lflag &= ~VIEW_FLAGS;
362d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
363d74d50a8SDag-Erling Smørgrav 				break;
364d74d50a8SDag-Erling Smørgrav 			case 'S':
365d74d50a8SDag-Erling Smørgrav 				*lflag &= ~SORT_FLAGS;
366d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_SIZE_SORT;
367d74d50a8SDag-Erling Smørgrav 				break;
368d74d50a8SDag-Erling Smørgrav 			case 't':
369d74d50a8SDag-Erling Smørgrav 				*lflag &= ~SORT_FLAGS;
370d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_TIME_SORT;
371d74d50a8SDag-Erling Smørgrav 				break;
372d74d50a8SDag-Erling Smørgrav 			case 'r':
373d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_REVERSE_SORT;
374d74d50a8SDag-Erling Smørgrav 				break;
375d74d50a8SDag-Erling Smørgrav 			case 'f':
376d74d50a8SDag-Erling Smørgrav 				*lflag &= ~SORT_FLAGS;
377d74d50a8SDag-Erling Smørgrav 				break;
378d74d50a8SDag-Erling Smørgrav 			case 'a':
379d74d50a8SDag-Erling Smørgrav 				*lflag |= LS_SHOW_ALL;
380efcad6b7SDag-Erling Smørgrav 				break;
381efcad6b7SDag-Erling Smørgrav 			default:
382efcad6b7SDag-Erling Smørgrav 				error("Invalid flag -%c", *cp);
383efcad6b7SDag-Erling Smørgrav 				return(-1);
384efcad6b7SDag-Erling Smørgrav 			}
385efcad6b7SDag-Erling Smørgrav 		}
386efcad6b7SDag-Erling Smørgrav 		*cpp = cp + strspn(cp, WHITESPACE);
387efcad6b7SDag-Erling Smørgrav 	}
388efcad6b7SDag-Erling Smørgrav 
389efcad6b7SDag-Erling Smørgrav 	return(0);
390efcad6b7SDag-Erling Smørgrav }
391efcad6b7SDag-Erling Smørgrav 
392efcad6b7SDag-Erling Smørgrav static int
393efcad6b7SDag-Erling Smørgrav get_pathname(const char **cpp, char **path)
394efcad6b7SDag-Erling Smørgrav {
395efcad6b7SDag-Erling Smørgrav 	const char *cp = *cpp, *end;
396efcad6b7SDag-Erling Smørgrav 	char quot;
397efcad6b7SDag-Erling Smørgrav 	int i, j;
398efcad6b7SDag-Erling Smørgrav 
399efcad6b7SDag-Erling Smørgrav 	cp += strspn(cp, WHITESPACE);
400efcad6b7SDag-Erling Smørgrav 	if (!*cp) {
401efcad6b7SDag-Erling Smørgrav 		*cpp = cp;
402efcad6b7SDag-Erling Smørgrav 		*path = NULL;
403efcad6b7SDag-Erling Smørgrav 		return (0);
404efcad6b7SDag-Erling Smørgrav 	}
405efcad6b7SDag-Erling Smørgrav 
406efcad6b7SDag-Erling Smørgrav 	*path = xmalloc(strlen(cp) + 1);
407efcad6b7SDag-Erling Smørgrav 
408efcad6b7SDag-Erling Smørgrav 	/* Check for quoted filenames */
409efcad6b7SDag-Erling Smørgrav 	if (*cp == '\"' || *cp == '\'') {
410efcad6b7SDag-Erling Smørgrav 		quot = *cp++;
411efcad6b7SDag-Erling Smørgrav 
412efcad6b7SDag-Erling Smørgrav 		/* Search for terminating quote, unescape some chars */
413efcad6b7SDag-Erling Smørgrav 		for (i = j = 0; i <= strlen(cp); i++) {
414efcad6b7SDag-Erling Smørgrav 			if (cp[i] == quot) {	/* Found quote */
415efcad6b7SDag-Erling Smørgrav 				i++;
416efcad6b7SDag-Erling Smørgrav 				(*path)[j] = '\0';
417efcad6b7SDag-Erling Smørgrav 				break;
418efcad6b7SDag-Erling Smørgrav 			}
419efcad6b7SDag-Erling Smørgrav 			if (cp[i] == '\0') {	/* End of string */
420efcad6b7SDag-Erling Smørgrav 				error("Unterminated quote");
421efcad6b7SDag-Erling Smørgrav 				goto fail;
422efcad6b7SDag-Erling Smørgrav 			}
423efcad6b7SDag-Erling Smørgrav 			if (cp[i] == '\\') {	/* Escaped characters */
424efcad6b7SDag-Erling Smørgrav 				i++;
425efcad6b7SDag-Erling Smørgrav 				if (cp[i] != '\'' && cp[i] != '\"' &&
426efcad6b7SDag-Erling Smørgrav 				    cp[i] != '\\') {
427d74d50a8SDag-Erling Smørgrav 					error("Bad escaped character '\\%c'",
428efcad6b7SDag-Erling Smørgrav 					    cp[i]);
429efcad6b7SDag-Erling Smørgrav 					goto fail;
430efcad6b7SDag-Erling Smørgrav 				}
431efcad6b7SDag-Erling Smørgrav 			}
432efcad6b7SDag-Erling Smørgrav 			(*path)[j++] = cp[i];
433efcad6b7SDag-Erling Smørgrav 		}
434efcad6b7SDag-Erling Smørgrav 
435efcad6b7SDag-Erling Smørgrav 		if (j == 0) {
436efcad6b7SDag-Erling Smørgrav 			error("Empty quotes");
437efcad6b7SDag-Erling Smørgrav 			goto fail;
438efcad6b7SDag-Erling Smørgrav 		}
439efcad6b7SDag-Erling Smørgrav 		*cpp = cp + i + strspn(cp + i, WHITESPACE);
440efcad6b7SDag-Erling Smørgrav 	} else {
441efcad6b7SDag-Erling Smørgrav 		/* Read to end of filename */
442efcad6b7SDag-Erling Smørgrav 		end = strpbrk(cp, WHITESPACE);
443efcad6b7SDag-Erling Smørgrav 		if (end == NULL)
444efcad6b7SDag-Erling Smørgrav 			end = strchr(cp, '\0');
445efcad6b7SDag-Erling Smørgrav 		*cpp = end + strspn(end, WHITESPACE);
446efcad6b7SDag-Erling Smørgrav 
447efcad6b7SDag-Erling Smørgrav 		memcpy(*path, cp, end - cp);
448efcad6b7SDag-Erling Smørgrav 		(*path)[end - cp] = '\0';
449efcad6b7SDag-Erling Smørgrav 	}
450efcad6b7SDag-Erling Smørgrav 	return (0);
451efcad6b7SDag-Erling Smørgrav 
452efcad6b7SDag-Erling Smørgrav  fail:
453efcad6b7SDag-Erling Smørgrav 	xfree(*path);
454efcad6b7SDag-Erling Smørgrav 	*path = NULL;
455efcad6b7SDag-Erling Smørgrav 	return (-1);
456efcad6b7SDag-Erling Smørgrav }
457efcad6b7SDag-Erling Smørgrav 
458efcad6b7SDag-Erling Smørgrav static int
459efcad6b7SDag-Erling Smørgrav is_dir(char *path)
460efcad6b7SDag-Erling Smørgrav {
461efcad6b7SDag-Erling Smørgrav 	struct stat sb;
462efcad6b7SDag-Erling Smørgrav 
463efcad6b7SDag-Erling Smørgrav 	/* XXX: report errors? */
464efcad6b7SDag-Erling Smørgrav 	if (stat(path, &sb) == -1)
465efcad6b7SDag-Erling Smørgrav 		return(0);
466efcad6b7SDag-Erling Smørgrav 
467efcad6b7SDag-Erling Smørgrav 	return(sb.st_mode & S_IFDIR);
468efcad6b7SDag-Erling Smørgrav }
469efcad6b7SDag-Erling Smørgrav 
470efcad6b7SDag-Erling Smørgrav static int
471efcad6b7SDag-Erling Smørgrav is_reg(char *path)
472efcad6b7SDag-Erling Smørgrav {
473efcad6b7SDag-Erling Smørgrav 	struct stat sb;
474efcad6b7SDag-Erling Smørgrav 
475efcad6b7SDag-Erling Smørgrav 	if (stat(path, &sb) == -1)
476efcad6b7SDag-Erling Smørgrav 		fatal("stat %s: %s", path, strerror(errno));
477efcad6b7SDag-Erling Smørgrav 
478efcad6b7SDag-Erling Smørgrav 	return(S_ISREG(sb.st_mode));
479efcad6b7SDag-Erling Smørgrav }
480efcad6b7SDag-Erling Smørgrav 
481efcad6b7SDag-Erling Smørgrav static int
482efcad6b7SDag-Erling Smørgrav remote_is_dir(struct sftp_conn *conn, char *path)
483efcad6b7SDag-Erling Smørgrav {
484efcad6b7SDag-Erling Smørgrav 	Attrib *a;
485efcad6b7SDag-Erling Smørgrav 
486efcad6b7SDag-Erling Smørgrav 	/* XXX: report errors? */
487efcad6b7SDag-Erling Smørgrav 	if ((a = do_stat(conn, path, 1)) == NULL)
488efcad6b7SDag-Erling Smørgrav 		return(0);
489efcad6b7SDag-Erling Smørgrav 	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
490efcad6b7SDag-Erling Smørgrav 		return(0);
491efcad6b7SDag-Erling Smørgrav 	return(a->perm & S_IFDIR);
492efcad6b7SDag-Erling Smørgrav }
493efcad6b7SDag-Erling Smørgrav 
494efcad6b7SDag-Erling Smørgrav static int
495efcad6b7SDag-Erling Smørgrav process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
496efcad6b7SDag-Erling Smørgrav {
497efcad6b7SDag-Erling Smørgrav 	char *abs_src = NULL;
498efcad6b7SDag-Erling Smørgrav 	char *abs_dst = NULL;
499efcad6b7SDag-Erling Smørgrav 	char *tmp;
500efcad6b7SDag-Erling Smørgrav 	glob_t g;
501efcad6b7SDag-Erling Smørgrav 	int err = 0;
502efcad6b7SDag-Erling Smørgrav 	int i;
503efcad6b7SDag-Erling Smørgrav 
504efcad6b7SDag-Erling Smørgrav 	abs_src = xstrdup(src);
505efcad6b7SDag-Erling Smørgrav 	abs_src = make_absolute(abs_src, pwd);
506efcad6b7SDag-Erling Smørgrav 
507efcad6b7SDag-Erling Smørgrav 	memset(&g, 0, sizeof(g));
508efcad6b7SDag-Erling Smørgrav 	debug3("Looking up %s", abs_src);
509efcad6b7SDag-Erling Smørgrav 	if (remote_glob(conn, abs_src, 0, NULL, &g)) {
510efcad6b7SDag-Erling Smørgrav 		error("File \"%s\" not found.", abs_src);
511efcad6b7SDag-Erling Smørgrav 		err = -1;
512efcad6b7SDag-Erling Smørgrav 		goto out;
513efcad6b7SDag-Erling Smørgrav 	}
514efcad6b7SDag-Erling Smørgrav 
515efcad6b7SDag-Erling Smørgrav 	/* If multiple matches, dst must be a directory or unspecified */
516efcad6b7SDag-Erling Smørgrav 	if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
517efcad6b7SDag-Erling Smørgrav 		error("Multiple files match, but \"%s\" is not a directory",
518efcad6b7SDag-Erling Smørgrav 		    dst);
519efcad6b7SDag-Erling Smørgrav 		err = -1;
520efcad6b7SDag-Erling Smørgrav 		goto out;
521efcad6b7SDag-Erling Smørgrav 	}
522efcad6b7SDag-Erling Smørgrav 
523d74d50a8SDag-Erling Smørgrav 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
524efcad6b7SDag-Erling Smørgrav 		if (infer_path(g.gl_pathv[i], &tmp)) {
525efcad6b7SDag-Erling Smørgrav 			err = -1;
526efcad6b7SDag-Erling Smørgrav 			goto out;
527efcad6b7SDag-Erling Smørgrav 		}
528efcad6b7SDag-Erling Smørgrav 
529efcad6b7SDag-Erling Smørgrav 		if (g.gl_matchc == 1 && dst) {
530efcad6b7SDag-Erling Smørgrav 			/* If directory specified, append filename */
531efcad6b7SDag-Erling Smørgrav 			if (is_dir(dst)) {
532efcad6b7SDag-Erling Smørgrav 				if (infer_path(g.gl_pathv[0], &tmp)) {
533efcad6b7SDag-Erling Smørgrav 					err = 1;
534efcad6b7SDag-Erling Smørgrav 					goto out;
535efcad6b7SDag-Erling Smørgrav 				}
536efcad6b7SDag-Erling Smørgrav 				abs_dst = path_append(dst, tmp);
537efcad6b7SDag-Erling Smørgrav 				xfree(tmp);
538efcad6b7SDag-Erling Smørgrav 			} else
539efcad6b7SDag-Erling Smørgrav 				abs_dst = xstrdup(dst);
540efcad6b7SDag-Erling Smørgrav 		} else if (dst) {
541efcad6b7SDag-Erling Smørgrav 			abs_dst = path_append(dst, tmp);
542efcad6b7SDag-Erling Smørgrav 			xfree(tmp);
543efcad6b7SDag-Erling Smørgrav 		} else
544efcad6b7SDag-Erling Smørgrav 			abs_dst = tmp;
545efcad6b7SDag-Erling Smørgrav 
546efcad6b7SDag-Erling Smørgrav 		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
547efcad6b7SDag-Erling Smørgrav 		if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
548efcad6b7SDag-Erling Smørgrav 			err = -1;
549efcad6b7SDag-Erling Smørgrav 		xfree(abs_dst);
550efcad6b7SDag-Erling Smørgrav 		abs_dst = NULL;
551efcad6b7SDag-Erling Smørgrav 	}
552efcad6b7SDag-Erling Smørgrav 
553efcad6b7SDag-Erling Smørgrav out:
554efcad6b7SDag-Erling Smørgrav 	xfree(abs_src);
555efcad6b7SDag-Erling Smørgrav 	if (abs_dst)
556efcad6b7SDag-Erling Smørgrav 		xfree(abs_dst);
557efcad6b7SDag-Erling Smørgrav 	globfree(&g);
558efcad6b7SDag-Erling Smørgrav 	return(err);
559efcad6b7SDag-Erling Smørgrav }
560efcad6b7SDag-Erling Smørgrav 
561efcad6b7SDag-Erling Smørgrav static int
562efcad6b7SDag-Erling Smørgrav process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
563efcad6b7SDag-Erling Smørgrav {
564efcad6b7SDag-Erling Smørgrav 	char *tmp_dst = NULL;
565efcad6b7SDag-Erling Smørgrav 	char *abs_dst = NULL;
566efcad6b7SDag-Erling Smørgrav 	char *tmp;
567efcad6b7SDag-Erling Smørgrav 	glob_t g;
568efcad6b7SDag-Erling Smørgrav 	int err = 0;
569efcad6b7SDag-Erling Smørgrav 	int i;
570efcad6b7SDag-Erling Smørgrav 
571efcad6b7SDag-Erling Smørgrav 	if (dst) {
572efcad6b7SDag-Erling Smørgrav 		tmp_dst = xstrdup(dst);
573efcad6b7SDag-Erling Smørgrav 		tmp_dst = make_absolute(tmp_dst, pwd);
574efcad6b7SDag-Erling Smørgrav 	}
575efcad6b7SDag-Erling Smørgrav 
576efcad6b7SDag-Erling Smørgrav 	memset(&g, 0, sizeof(g));
577efcad6b7SDag-Erling Smørgrav 	debug3("Looking up %s", src);
578efcad6b7SDag-Erling Smørgrav 	if (glob(src, 0, NULL, &g)) {
579efcad6b7SDag-Erling Smørgrav 		error("File \"%s\" not found.", src);
580efcad6b7SDag-Erling Smørgrav 		err = -1;
581efcad6b7SDag-Erling Smørgrav 		goto out;
582efcad6b7SDag-Erling Smørgrav 	}
583efcad6b7SDag-Erling Smørgrav 
584efcad6b7SDag-Erling Smørgrav 	/* If multiple matches, dst may be directory or unspecified */
585efcad6b7SDag-Erling Smørgrav 	if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
586efcad6b7SDag-Erling Smørgrav 		error("Multiple files match, but \"%s\" is not a directory",
587efcad6b7SDag-Erling Smørgrav 		    tmp_dst);
588efcad6b7SDag-Erling Smørgrav 		err = -1;
589efcad6b7SDag-Erling Smørgrav 		goto out;
590efcad6b7SDag-Erling Smørgrav 	}
591efcad6b7SDag-Erling Smørgrav 
592d74d50a8SDag-Erling Smørgrav 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
593efcad6b7SDag-Erling Smørgrav 		if (!is_reg(g.gl_pathv[i])) {
594efcad6b7SDag-Erling Smørgrav 			error("skipping non-regular file %s",
595efcad6b7SDag-Erling Smørgrav 			    g.gl_pathv[i]);
596efcad6b7SDag-Erling Smørgrav 			continue;
597efcad6b7SDag-Erling Smørgrav 		}
598efcad6b7SDag-Erling Smørgrav 		if (infer_path(g.gl_pathv[i], &tmp)) {
599efcad6b7SDag-Erling Smørgrav 			err = -1;
600efcad6b7SDag-Erling Smørgrav 			goto out;
601efcad6b7SDag-Erling Smørgrav 		}
602efcad6b7SDag-Erling Smørgrav 
603efcad6b7SDag-Erling Smørgrav 		if (g.gl_matchc == 1 && tmp_dst) {
604efcad6b7SDag-Erling Smørgrav 			/* If directory specified, append filename */
605efcad6b7SDag-Erling Smørgrav 			if (remote_is_dir(conn, tmp_dst)) {
606efcad6b7SDag-Erling Smørgrav 				if (infer_path(g.gl_pathv[0], &tmp)) {
607efcad6b7SDag-Erling Smørgrav 					err = 1;
608efcad6b7SDag-Erling Smørgrav 					goto out;
609efcad6b7SDag-Erling Smørgrav 				}
610efcad6b7SDag-Erling Smørgrav 				abs_dst = path_append(tmp_dst, tmp);
611efcad6b7SDag-Erling Smørgrav 				xfree(tmp);
612efcad6b7SDag-Erling Smørgrav 			} else
613efcad6b7SDag-Erling Smørgrav 				abs_dst = xstrdup(tmp_dst);
614efcad6b7SDag-Erling Smørgrav 
615efcad6b7SDag-Erling Smørgrav 		} else if (tmp_dst) {
616efcad6b7SDag-Erling Smørgrav 			abs_dst = path_append(tmp_dst, tmp);
617efcad6b7SDag-Erling Smørgrav 			xfree(tmp);
618efcad6b7SDag-Erling Smørgrav 		} else
619efcad6b7SDag-Erling Smørgrav 			abs_dst = make_absolute(tmp, pwd);
620efcad6b7SDag-Erling Smørgrav 
621efcad6b7SDag-Erling Smørgrav 		printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
622efcad6b7SDag-Erling Smørgrav 		if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
623efcad6b7SDag-Erling Smørgrav 			err = -1;
624efcad6b7SDag-Erling Smørgrav 	}
625efcad6b7SDag-Erling Smørgrav 
626efcad6b7SDag-Erling Smørgrav out:
627efcad6b7SDag-Erling Smørgrav 	if (abs_dst)
628efcad6b7SDag-Erling Smørgrav 		xfree(abs_dst);
629efcad6b7SDag-Erling Smørgrav 	if (tmp_dst)
630efcad6b7SDag-Erling Smørgrav 		xfree(tmp_dst);
631efcad6b7SDag-Erling Smørgrav 	globfree(&g);
632efcad6b7SDag-Erling Smørgrav 	return(err);
633efcad6b7SDag-Erling Smørgrav }
634efcad6b7SDag-Erling Smørgrav 
635efcad6b7SDag-Erling Smørgrav static int
636efcad6b7SDag-Erling Smørgrav sdirent_comp(const void *aa, const void *bb)
637efcad6b7SDag-Erling Smørgrav {
638efcad6b7SDag-Erling Smørgrav 	SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
639efcad6b7SDag-Erling Smørgrav 	SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
640d74d50a8SDag-Erling Smørgrav 	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
641efcad6b7SDag-Erling Smørgrav 
642d74d50a8SDag-Erling Smørgrav #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
643d74d50a8SDag-Erling Smørgrav 	if (sort_flag & LS_NAME_SORT)
644d74d50a8SDag-Erling Smørgrav 		return (rmul * strcmp(a->filename, b->filename));
645d74d50a8SDag-Erling Smørgrav 	else if (sort_flag & LS_TIME_SORT)
646d74d50a8SDag-Erling Smørgrav 		return (rmul * NCMP(a->a.mtime, b->a.mtime));
647d74d50a8SDag-Erling Smørgrav 	else if (sort_flag & LS_SIZE_SORT)
648d74d50a8SDag-Erling Smørgrav 		return (rmul * NCMP(a->a.size, b->a.size));
649d74d50a8SDag-Erling Smørgrav 
650d74d50a8SDag-Erling Smørgrav 	fatal("Unknown ls sort type");
651efcad6b7SDag-Erling Smørgrav }
652efcad6b7SDag-Erling Smørgrav 
653efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement for directories */
654efcad6b7SDag-Erling Smørgrav static int
655efcad6b7SDag-Erling Smørgrav do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
656efcad6b7SDag-Erling Smørgrav {
657efcad6b7SDag-Erling Smørgrav 	int n, c = 1, colspace = 0, columns = 1;
658efcad6b7SDag-Erling Smørgrav 	SFTP_DIRENT **d;
659efcad6b7SDag-Erling Smørgrav 
660efcad6b7SDag-Erling Smørgrav 	if ((n = do_readdir(conn, path, &d)) != 0)
661efcad6b7SDag-Erling Smørgrav 		return (n);
662efcad6b7SDag-Erling Smørgrav 
663d74d50a8SDag-Erling Smørgrav 	if (!(lflag & LS_SHORT_VIEW)) {
664efcad6b7SDag-Erling Smørgrav 		int m = 0, width = 80;
665efcad6b7SDag-Erling Smørgrav 		struct winsize ws;
666efcad6b7SDag-Erling Smørgrav 		char *tmp;
667efcad6b7SDag-Erling Smørgrav 
668efcad6b7SDag-Erling Smørgrav 		/* Count entries for sort and find longest filename */
669d74d50a8SDag-Erling Smørgrav 		for (n = 0; d[n] != NULL; n++) {
670d74d50a8SDag-Erling Smørgrav 			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
671efcad6b7SDag-Erling Smørgrav 				m = MAX(m, strlen(d[n]->filename));
672d74d50a8SDag-Erling Smørgrav 		}
673efcad6b7SDag-Erling Smørgrav 
674efcad6b7SDag-Erling Smørgrav 		/* Add any subpath that also needs to be counted */
675efcad6b7SDag-Erling Smørgrav 		tmp = path_strip(path, strip_path);
676efcad6b7SDag-Erling Smørgrav 		m += strlen(tmp);
677efcad6b7SDag-Erling Smørgrav 		xfree(tmp);
678efcad6b7SDag-Erling Smørgrav 
679efcad6b7SDag-Erling Smørgrav 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
680efcad6b7SDag-Erling Smørgrav 			width = ws.ws_col;
681efcad6b7SDag-Erling Smørgrav 
682efcad6b7SDag-Erling Smørgrav 		columns = width / (m + 2);
683efcad6b7SDag-Erling Smørgrav 		columns = MAX(columns, 1);
684efcad6b7SDag-Erling Smørgrav 		colspace = width / columns;
685efcad6b7SDag-Erling Smørgrav 		colspace = MIN(colspace, width);
686efcad6b7SDag-Erling Smørgrav 	}
687efcad6b7SDag-Erling Smørgrav 
688d74d50a8SDag-Erling Smørgrav 	if (lflag & SORT_FLAGS) {
689d74d50a8SDag-Erling Smørgrav 		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
690efcad6b7SDag-Erling Smørgrav 		qsort(d, n, sizeof(*d), sdirent_comp);
691d74d50a8SDag-Erling Smørgrav 	}
692efcad6b7SDag-Erling Smørgrav 
693d74d50a8SDag-Erling Smørgrav 	for (n = 0; d[n] != NULL && !interrupted; n++) {
694efcad6b7SDag-Erling Smørgrav 		char *tmp, *fname;
695efcad6b7SDag-Erling Smørgrav 
696d74d50a8SDag-Erling Smørgrav 		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
697d74d50a8SDag-Erling Smørgrav 			continue;
698d74d50a8SDag-Erling Smørgrav 
699efcad6b7SDag-Erling Smørgrav 		tmp = path_append(path, d[n]->filename);
700efcad6b7SDag-Erling Smørgrav 		fname = path_strip(tmp, strip_path);
701efcad6b7SDag-Erling Smørgrav 		xfree(tmp);
702efcad6b7SDag-Erling Smørgrav 
703d74d50a8SDag-Erling Smørgrav 		if (lflag & LS_LONG_VIEW) {
704d74d50a8SDag-Erling Smørgrav 			if (lflag & LS_NUMERIC_VIEW) {
705efcad6b7SDag-Erling Smørgrav 				char *lname;
706efcad6b7SDag-Erling Smørgrav 				struct stat sb;
707efcad6b7SDag-Erling Smørgrav 
708efcad6b7SDag-Erling Smørgrav 				memset(&sb, 0, sizeof(sb));
709efcad6b7SDag-Erling Smørgrav 				attrib_to_stat(&d[n]->a, &sb);
710efcad6b7SDag-Erling Smørgrav 				lname = ls_file(fname, &sb, 1);
711efcad6b7SDag-Erling Smørgrav 				printf("%s\n", lname);
712efcad6b7SDag-Erling Smørgrav 				xfree(lname);
713d74d50a8SDag-Erling Smørgrav 			} else
714d74d50a8SDag-Erling Smørgrav 				printf("%s\n", d[n]->longname);
715efcad6b7SDag-Erling Smørgrav 		} else {
716efcad6b7SDag-Erling Smørgrav 			printf("%-*s", colspace, fname);
717efcad6b7SDag-Erling Smørgrav 			if (c >= columns) {
718efcad6b7SDag-Erling Smørgrav 				printf("\n");
719efcad6b7SDag-Erling Smørgrav 				c = 1;
720efcad6b7SDag-Erling Smørgrav 			} else
721efcad6b7SDag-Erling Smørgrav 				c++;
722efcad6b7SDag-Erling Smørgrav 		}
723efcad6b7SDag-Erling Smørgrav 
724efcad6b7SDag-Erling Smørgrav 		xfree(fname);
725efcad6b7SDag-Erling Smørgrav 	}
726efcad6b7SDag-Erling Smørgrav 
727d74d50a8SDag-Erling Smørgrav 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
728efcad6b7SDag-Erling Smørgrav 		printf("\n");
729efcad6b7SDag-Erling Smørgrav 
730efcad6b7SDag-Erling Smørgrav 	free_sftp_dirents(d);
731efcad6b7SDag-Erling Smørgrav 	return (0);
732efcad6b7SDag-Erling Smørgrav }
733efcad6b7SDag-Erling Smørgrav 
734efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement which handles path globs */
735efcad6b7SDag-Erling Smørgrav static int
736efcad6b7SDag-Erling Smørgrav do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
737efcad6b7SDag-Erling Smørgrav     int lflag)
738efcad6b7SDag-Erling Smørgrav {
739efcad6b7SDag-Erling Smørgrav 	glob_t g;
740efcad6b7SDag-Erling Smørgrav 	int i, c = 1, colspace = 0, columns = 1;
741efcad6b7SDag-Erling Smørgrav 	Attrib *a;
742efcad6b7SDag-Erling Smørgrav 
743efcad6b7SDag-Erling Smørgrav 	memset(&g, 0, sizeof(g));
744efcad6b7SDag-Erling Smørgrav 
745efcad6b7SDag-Erling Smørgrav 	if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
746efcad6b7SDag-Erling Smørgrav 	    NULL, &g)) {
747efcad6b7SDag-Erling Smørgrav 		error("Can't ls: \"%s\" not found", path);
748efcad6b7SDag-Erling Smørgrav 		return (-1);
749efcad6b7SDag-Erling Smørgrav 	}
750efcad6b7SDag-Erling Smørgrav 
751d74d50a8SDag-Erling Smørgrav 	if (interrupted)
752d74d50a8SDag-Erling Smørgrav 		goto out;
753d74d50a8SDag-Erling Smørgrav 
754efcad6b7SDag-Erling Smørgrav 	/*
755efcad6b7SDag-Erling Smørgrav 	 * If the glob returns a single match, which is the same as the
756efcad6b7SDag-Erling Smørgrav 	 * input glob, and it is a directory, then just list its contents
757efcad6b7SDag-Erling Smørgrav 	 */
758efcad6b7SDag-Erling Smørgrav 	if (g.gl_pathc == 1 &&
759efcad6b7SDag-Erling Smørgrav 	    strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
760efcad6b7SDag-Erling Smørgrav 		if ((a = do_lstat(conn, path, 1)) == NULL) {
761efcad6b7SDag-Erling Smørgrav 			globfree(&g);
762efcad6b7SDag-Erling Smørgrav 			return (-1);
763efcad6b7SDag-Erling Smørgrav 		}
764efcad6b7SDag-Erling Smørgrav 		if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
765efcad6b7SDag-Erling Smørgrav 		    S_ISDIR(a->perm)) {
766efcad6b7SDag-Erling Smørgrav 			globfree(&g);
767efcad6b7SDag-Erling Smørgrav 			return (do_ls_dir(conn, path, strip_path, lflag));
768efcad6b7SDag-Erling Smørgrav 		}
769efcad6b7SDag-Erling Smørgrav 	}
770efcad6b7SDag-Erling Smørgrav 
771d74d50a8SDag-Erling Smørgrav 	if (!(lflag & LS_SHORT_VIEW)) {
772efcad6b7SDag-Erling Smørgrav 		int m = 0, width = 80;
773efcad6b7SDag-Erling Smørgrav 		struct winsize ws;
774efcad6b7SDag-Erling Smørgrav 
775efcad6b7SDag-Erling Smørgrav 		/* Count entries for sort and find longest filename */
776efcad6b7SDag-Erling Smørgrav 		for (i = 0; g.gl_pathv[i]; i++)
777efcad6b7SDag-Erling Smørgrav 			m = MAX(m, strlen(g.gl_pathv[i]));
778efcad6b7SDag-Erling Smørgrav 
779efcad6b7SDag-Erling Smørgrav 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
780efcad6b7SDag-Erling Smørgrav 			width = ws.ws_col;
781efcad6b7SDag-Erling Smørgrav 
782efcad6b7SDag-Erling Smørgrav 		columns = width / (m + 2);
783efcad6b7SDag-Erling Smørgrav 		columns = MAX(columns, 1);
784efcad6b7SDag-Erling Smørgrav 		colspace = width / columns;
785efcad6b7SDag-Erling Smørgrav 	}
786efcad6b7SDag-Erling Smørgrav 
787d74d50a8SDag-Erling Smørgrav 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
788efcad6b7SDag-Erling Smørgrav 		char *fname;
789efcad6b7SDag-Erling Smørgrav 
790efcad6b7SDag-Erling Smørgrav 		fname = path_strip(g.gl_pathv[i], strip_path);
791efcad6b7SDag-Erling Smørgrav 
792d74d50a8SDag-Erling Smørgrav 		if (lflag & LS_LONG_VIEW) {
793efcad6b7SDag-Erling Smørgrav 			char *lname;
794efcad6b7SDag-Erling Smørgrav 			struct stat sb;
795efcad6b7SDag-Erling Smørgrav 
796efcad6b7SDag-Erling Smørgrav 			/*
797efcad6b7SDag-Erling Smørgrav 			 * XXX: this is slow - 1 roundtrip per path
798efcad6b7SDag-Erling Smørgrav 			 * A solution to this is to fork glob() and
799efcad6b7SDag-Erling Smørgrav 			 * build a sftp specific version which keeps the
800efcad6b7SDag-Erling Smørgrav 			 * attribs (which currently get thrown away)
801efcad6b7SDag-Erling Smørgrav 			 * that the server returns as well as the filenames.
802efcad6b7SDag-Erling Smørgrav 			 */
803efcad6b7SDag-Erling Smørgrav 			memset(&sb, 0, sizeof(sb));
804efcad6b7SDag-Erling Smørgrav 			a = do_lstat(conn, g.gl_pathv[i], 1);
805efcad6b7SDag-Erling Smørgrav 			if (a != NULL)
806efcad6b7SDag-Erling Smørgrav 				attrib_to_stat(a, &sb);
807efcad6b7SDag-Erling Smørgrav 			lname = ls_file(fname, &sb, 1);
808efcad6b7SDag-Erling Smørgrav 			printf("%s\n", lname);
809efcad6b7SDag-Erling Smørgrav 			xfree(lname);
810efcad6b7SDag-Erling Smørgrav 		} else {
811efcad6b7SDag-Erling Smørgrav 			printf("%-*s", colspace, fname);
812efcad6b7SDag-Erling Smørgrav 			if (c >= columns) {
813efcad6b7SDag-Erling Smørgrav 				printf("\n");
814efcad6b7SDag-Erling Smørgrav 				c = 1;
815efcad6b7SDag-Erling Smørgrav 			} else
816efcad6b7SDag-Erling Smørgrav 				c++;
817efcad6b7SDag-Erling Smørgrav 		}
818efcad6b7SDag-Erling Smørgrav 		xfree(fname);
819efcad6b7SDag-Erling Smørgrav 	}
820efcad6b7SDag-Erling Smørgrav 
821d74d50a8SDag-Erling Smørgrav 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
822efcad6b7SDag-Erling Smørgrav 		printf("\n");
823efcad6b7SDag-Erling Smørgrav 
824d74d50a8SDag-Erling Smørgrav  out:
825efcad6b7SDag-Erling Smørgrav 	if (g.gl_pathc)
826efcad6b7SDag-Erling Smørgrav 		globfree(&g);
827efcad6b7SDag-Erling Smørgrav 
828efcad6b7SDag-Erling Smørgrav 	return (0);
829efcad6b7SDag-Erling Smørgrav }
830efcad6b7SDag-Erling Smørgrav 
831efcad6b7SDag-Erling Smørgrav static int
832efcad6b7SDag-Erling Smørgrav parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
833efcad6b7SDag-Erling Smørgrav     unsigned long *n_arg, char **path1, char **path2)
834efcad6b7SDag-Erling Smørgrav {
835efcad6b7SDag-Erling Smørgrav 	const char *cmd, *cp = *cpp;
836efcad6b7SDag-Erling Smørgrav 	char *cp2;
837efcad6b7SDag-Erling Smørgrav 	int base = 0;
838efcad6b7SDag-Erling Smørgrav 	long l;
839efcad6b7SDag-Erling Smørgrav 	int i, cmdnum;
840efcad6b7SDag-Erling Smørgrav 
841efcad6b7SDag-Erling Smørgrav 	/* Skip leading whitespace */
842efcad6b7SDag-Erling Smørgrav 	cp = cp + strspn(cp, WHITESPACE);
843efcad6b7SDag-Erling Smørgrav 
844efcad6b7SDag-Erling Smørgrav 	/* Ignore blank lines and lines which begin with comment '#' char */
845efcad6b7SDag-Erling Smørgrav 	if (*cp == '\0' || *cp == '#')
846efcad6b7SDag-Erling Smørgrav 		return (0);
847efcad6b7SDag-Erling Smørgrav 
848efcad6b7SDag-Erling Smørgrav 	/* Check for leading '-' (disable error processing) */
849efcad6b7SDag-Erling Smørgrav 	*iflag = 0;
850efcad6b7SDag-Erling Smørgrav 	if (*cp == '-') {
851efcad6b7SDag-Erling Smørgrav 		*iflag = 1;
852efcad6b7SDag-Erling Smørgrav 		cp++;
853efcad6b7SDag-Erling Smørgrav 	}
854efcad6b7SDag-Erling Smørgrav 
855efcad6b7SDag-Erling Smørgrav 	/* Figure out which command we have */
856efcad6b7SDag-Erling Smørgrav 	for (i = 0; cmds[i].c; i++) {
857efcad6b7SDag-Erling Smørgrav 		int cmdlen = strlen(cmds[i].c);
858efcad6b7SDag-Erling Smørgrav 
859efcad6b7SDag-Erling Smørgrav 		/* Check for command followed by whitespace */
860efcad6b7SDag-Erling Smørgrav 		if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
861efcad6b7SDag-Erling Smørgrav 		    strchr(WHITESPACE, cp[cmdlen])) {
862efcad6b7SDag-Erling Smørgrav 			cp += cmdlen;
863efcad6b7SDag-Erling Smørgrav 			cp = cp + strspn(cp, WHITESPACE);
864efcad6b7SDag-Erling Smørgrav 			break;
865efcad6b7SDag-Erling Smørgrav 		}
866efcad6b7SDag-Erling Smørgrav 	}
867efcad6b7SDag-Erling Smørgrav 	cmdnum = cmds[i].n;
868efcad6b7SDag-Erling Smørgrav 	cmd = cmds[i].c;
869efcad6b7SDag-Erling Smørgrav 
870efcad6b7SDag-Erling Smørgrav 	/* Special case */
871efcad6b7SDag-Erling Smørgrav 	if (*cp == '!') {
872efcad6b7SDag-Erling Smørgrav 		cp++;
873efcad6b7SDag-Erling Smørgrav 		cmdnum = I_SHELL;
874efcad6b7SDag-Erling Smørgrav 	} else if (cmdnum == -1) {
875efcad6b7SDag-Erling Smørgrav 		error("Invalid command.");
876efcad6b7SDag-Erling Smørgrav 		return (-1);
877efcad6b7SDag-Erling Smørgrav 	}
878efcad6b7SDag-Erling Smørgrav 
879efcad6b7SDag-Erling Smørgrav 	/* Get arguments and parse flags */
880efcad6b7SDag-Erling Smørgrav 	*lflag = *pflag = *n_arg = 0;
881efcad6b7SDag-Erling Smørgrav 	*path1 = *path2 = NULL;
882efcad6b7SDag-Erling Smørgrav 	switch (cmdnum) {
883efcad6b7SDag-Erling Smørgrav 	case I_GET:
884efcad6b7SDag-Erling Smørgrav 	case I_PUT:
885efcad6b7SDag-Erling Smørgrav 		if (parse_getput_flags(&cp, pflag))
886efcad6b7SDag-Erling Smørgrav 			return(-1);
887efcad6b7SDag-Erling Smørgrav 		/* Get first pathname (mandatory) */
888efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path1))
889efcad6b7SDag-Erling Smørgrav 			return(-1);
890efcad6b7SDag-Erling Smørgrav 		if (*path1 == NULL) {
891efcad6b7SDag-Erling Smørgrav 			error("You must specify at least one path after a "
892efcad6b7SDag-Erling Smørgrav 			    "%s command.", cmd);
893efcad6b7SDag-Erling Smørgrav 			return(-1);
894efcad6b7SDag-Erling Smørgrav 		}
895efcad6b7SDag-Erling Smørgrav 		/* Try to get second pathname (optional) */
896efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path2))
897efcad6b7SDag-Erling Smørgrav 			return(-1);
898efcad6b7SDag-Erling Smørgrav 		break;
899efcad6b7SDag-Erling Smørgrav 	case I_RENAME:
900efcad6b7SDag-Erling Smørgrav 	case I_SYMLINK:
901efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path1))
902efcad6b7SDag-Erling Smørgrav 			return(-1);
903efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path2))
904efcad6b7SDag-Erling Smørgrav 			return(-1);
905efcad6b7SDag-Erling Smørgrav 		if (!*path1 || !*path2) {
906efcad6b7SDag-Erling Smørgrav 			error("You must specify two paths after a %s "
907efcad6b7SDag-Erling Smørgrav 			    "command.", cmd);
908efcad6b7SDag-Erling Smørgrav 			return(-1);
909efcad6b7SDag-Erling Smørgrav 		}
910efcad6b7SDag-Erling Smørgrav 		break;
911efcad6b7SDag-Erling Smørgrav 	case I_RM:
912efcad6b7SDag-Erling Smørgrav 	case I_MKDIR:
913efcad6b7SDag-Erling Smørgrav 	case I_RMDIR:
914efcad6b7SDag-Erling Smørgrav 	case I_CHDIR:
915efcad6b7SDag-Erling Smørgrav 	case I_LCHDIR:
916efcad6b7SDag-Erling Smørgrav 	case I_LMKDIR:
917efcad6b7SDag-Erling Smørgrav 		/* Get pathname (mandatory) */
918efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path1))
919efcad6b7SDag-Erling Smørgrav 			return(-1);
920efcad6b7SDag-Erling Smørgrav 		if (*path1 == NULL) {
921efcad6b7SDag-Erling Smørgrav 			error("You must specify a path after a %s command.",
922efcad6b7SDag-Erling Smørgrav 			    cmd);
923efcad6b7SDag-Erling Smørgrav 			return(-1);
924efcad6b7SDag-Erling Smørgrav 		}
925efcad6b7SDag-Erling Smørgrav 		break;
926efcad6b7SDag-Erling Smørgrav 	case I_LS:
927efcad6b7SDag-Erling Smørgrav 		if (parse_ls_flags(&cp, lflag))
928efcad6b7SDag-Erling Smørgrav 			return(-1);
929efcad6b7SDag-Erling Smørgrav 		/* Path is optional */
930efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path1))
931efcad6b7SDag-Erling Smørgrav 			return(-1);
932efcad6b7SDag-Erling Smørgrav 		break;
933efcad6b7SDag-Erling Smørgrav 	case I_LLS:
934efcad6b7SDag-Erling Smørgrav 	case I_SHELL:
935efcad6b7SDag-Erling Smørgrav 		/* Uses the rest of the line */
936efcad6b7SDag-Erling Smørgrav 		break;
937efcad6b7SDag-Erling Smørgrav 	case I_LUMASK:
938efcad6b7SDag-Erling Smørgrav 		base = 8;
939efcad6b7SDag-Erling Smørgrav 	case I_CHMOD:
940efcad6b7SDag-Erling Smørgrav 		base = 8;
941efcad6b7SDag-Erling Smørgrav 	case I_CHOWN:
942efcad6b7SDag-Erling Smørgrav 	case I_CHGRP:
943efcad6b7SDag-Erling Smørgrav 		/* Get numeric arg (mandatory) */
944efcad6b7SDag-Erling Smørgrav 		l = strtol(cp, &cp2, base);
945efcad6b7SDag-Erling Smørgrav 		if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
946efcad6b7SDag-Erling Smørgrav 		    errno == ERANGE) || l < 0) {
947efcad6b7SDag-Erling Smørgrav 			error("You must supply a numeric argument "
948efcad6b7SDag-Erling Smørgrav 			    "to the %s command.", cmd);
949efcad6b7SDag-Erling Smørgrav 			return(-1);
950efcad6b7SDag-Erling Smørgrav 		}
951efcad6b7SDag-Erling Smørgrav 		cp = cp2;
952efcad6b7SDag-Erling Smørgrav 		*n_arg = l;
953efcad6b7SDag-Erling Smørgrav 		if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
954efcad6b7SDag-Erling Smørgrav 			break;
955efcad6b7SDag-Erling Smørgrav 		if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
956efcad6b7SDag-Erling Smørgrav 			error("You must supply a numeric argument "
957efcad6b7SDag-Erling Smørgrav 			    "to the %s command.", cmd);
958efcad6b7SDag-Erling Smørgrav 			return(-1);
959efcad6b7SDag-Erling Smørgrav 		}
960efcad6b7SDag-Erling Smørgrav 		cp += strspn(cp, WHITESPACE);
961efcad6b7SDag-Erling Smørgrav 
962efcad6b7SDag-Erling Smørgrav 		/* Get pathname (mandatory) */
963efcad6b7SDag-Erling Smørgrav 		if (get_pathname(&cp, path1))
964efcad6b7SDag-Erling Smørgrav 			return(-1);
965efcad6b7SDag-Erling Smørgrav 		if (*path1 == NULL) {
966efcad6b7SDag-Erling Smørgrav 			error("You must specify a path after a %s command.",
967efcad6b7SDag-Erling Smørgrav 			    cmd);
968efcad6b7SDag-Erling Smørgrav 			return(-1);
969efcad6b7SDag-Erling Smørgrav 		}
970efcad6b7SDag-Erling Smørgrav 		break;
971efcad6b7SDag-Erling Smørgrav 	case I_QUIT:
972efcad6b7SDag-Erling Smørgrav 	case I_PWD:
973efcad6b7SDag-Erling Smørgrav 	case I_LPWD:
974efcad6b7SDag-Erling Smørgrav 	case I_HELP:
975efcad6b7SDag-Erling Smørgrav 	case I_VERSION:
976efcad6b7SDag-Erling Smørgrav 	case I_PROGRESS:
977efcad6b7SDag-Erling Smørgrav 		break;
978efcad6b7SDag-Erling Smørgrav 	default:
979efcad6b7SDag-Erling Smørgrav 		fatal("Command not implemented");
980efcad6b7SDag-Erling Smørgrav 	}
981efcad6b7SDag-Erling Smørgrav 
982efcad6b7SDag-Erling Smørgrav 	*cpp = cp;
983efcad6b7SDag-Erling Smørgrav 	return(cmdnum);
984efcad6b7SDag-Erling Smørgrav }
985efcad6b7SDag-Erling Smørgrav 
986efcad6b7SDag-Erling Smørgrav static int
987efcad6b7SDag-Erling Smørgrav parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
988efcad6b7SDag-Erling Smørgrav     int err_abort)
989efcad6b7SDag-Erling Smørgrav {
990efcad6b7SDag-Erling Smørgrav 	char *path1, *path2, *tmp;
991efcad6b7SDag-Erling Smørgrav 	int pflag, lflag, iflag, cmdnum, i;
992efcad6b7SDag-Erling Smørgrav 	unsigned long n_arg;
993efcad6b7SDag-Erling Smørgrav 	Attrib a, *aa;
994efcad6b7SDag-Erling Smørgrav 	char path_buf[MAXPATHLEN];
995efcad6b7SDag-Erling Smørgrav 	int err = 0;
996efcad6b7SDag-Erling Smørgrav 	glob_t g;
997efcad6b7SDag-Erling Smørgrav 
998efcad6b7SDag-Erling Smørgrav 	path1 = path2 = NULL;
999efcad6b7SDag-Erling Smørgrav 	cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
1000efcad6b7SDag-Erling Smørgrav 	    &path1, &path2);
1001efcad6b7SDag-Erling Smørgrav 
1002efcad6b7SDag-Erling Smørgrav 	if (iflag != 0)
1003efcad6b7SDag-Erling Smørgrav 		err_abort = 0;
1004efcad6b7SDag-Erling Smørgrav 
1005efcad6b7SDag-Erling Smørgrav 	memset(&g, 0, sizeof(g));
1006efcad6b7SDag-Erling Smørgrav 
1007efcad6b7SDag-Erling Smørgrav 	/* Perform command */
1008efcad6b7SDag-Erling Smørgrav 	switch (cmdnum) {
1009efcad6b7SDag-Erling Smørgrav 	case 0:
1010efcad6b7SDag-Erling Smørgrav 		/* Blank line */
1011efcad6b7SDag-Erling Smørgrav 		break;
1012efcad6b7SDag-Erling Smørgrav 	case -1:
1013efcad6b7SDag-Erling Smørgrav 		/* Unrecognized command */
1014efcad6b7SDag-Erling Smørgrav 		err = -1;
1015efcad6b7SDag-Erling Smørgrav 		break;
1016efcad6b7SDag-Erling Smørgrav 	case I_GET:
1017efcad6b7SDag-Erling Smørgrav 		err = process_get(conn, path1, path2, *pwd, pflag);
1018efcad6b7SDag-Erling Smørgrav 		break;
1019efcad6b7SDag-Erling Smørgrav 	case I_PUT:
1020efcad6b7SDag-Erling Smørgrav 		err = process_put(conn, path1, path2, *pwd, pflag);
1021efcad6b7SDag-Erling Smørgrav 		break;
1022efcad6b7SDag-Erling Smørgrav 	case I_RENAME:
1023efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1024efcad6b7SDag-Erling Smørgrav 		path2 = make_absolute(path2, *pwd);
1025efcad6b7SDag-Erling Smørgrav 		err = do_rename(conn, path1, path2);
1026efcad6b7SDag-Erling Smørgrav 		break;
1027efcad6b7SDag-Erling Smørgrav 	case I_SYMLINK:
1028efcad6b7SDag-Erling Smørgrav 		path2 = make_absolute(path2, *pwd);
1029efcad6b7SDag-Erling Smørgrav 		err = do_symlink(conn, path1, path2);
1030efcad6b7SDag-Erling Smørgrav 		break;
1031efcad6b7SDag-Erling Smørgrav 	case I_RM:
1032efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1033efcad6b7SDag-Erling Smørgrav 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1034d74d50a8SDag-Erling Smørgrav 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1035efcad6b7SDag-Erling Smørgrav 			printf("Removing %s\n", g.gl_pathv[i]);
1036efcad6b7SDag-Erling Smørgrav 			err = do_rm(conn, g.gl_pathv[i]);
1037efcad6b7SDag-Erling Smørgrav 			if (err != 0 && err_abort)
1038efcad6b7SDag-Erling Smørgrav 				break;
1039efcad6b7SDag-Erling Smørgrav 		}
1040efcad6b7SDag-Erling Smørgrav 		break;
1041efcad6b7SDag-Erling Smørgrav 	case I_MKDIR:
1042efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1043efcad6b7SDag-Erling Smørgrav 		attrib_clear(&a);
1044efcad6b7SDag-Erling Smørgrav 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1045efcad6b7SDag-Erling Smørgrav 		a.perm = 0777;
1046efcad6b7SDag-Erling Smørgrav 		err = do_mkdir(conn, path1, &a);
1047efcad6b7SDag-Erling Smørgrav 		break;
1048efcad6b7SDag-Erling Smørgrav 	case I_RMDIR:
1049efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1050efcad6b7SDag-Erling Smørgrav 		err = do_rmdir(conn, path1);
1051efcad6b7SDag-Erling Smørgrav 		break;
1052efcad6b7SDag-Erling Smørgrav 	case I_CHDIR:
1053efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1054efcad6b7SDag-Erling Smørgrav 		if ((tmp = do_realpath(conn, path1)) == NULL) {
1055efcad6b7SDag-Erling Smørgrav 			err = 1;
1056efcad6b7SDag-Erling Smørgrav 			break;
1057efcad6b7SDag-Erling Smørgrav 		}
1058efcad6b7SDag-Erling Smørgrav 		if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1059efcad6b7SDag-Erling Smørgrav 			xfree(tmp);
1060efcad6b7SDag-Erling Smørgrav 			err = 1;
1061efcad6b7SDag-Erling Smørgrav 			break;
1062efcad6b7SDag-Erling Smørgrav 		}
1063efcad6b7SDag-Erling Smørgrav 		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1064efcad6b7SDag-Erling Smørgrav 			error("Can't change directory: Can't check target");
1065efcad6b7SDag-Erling Smørgrav 			xfree(tmp);
1066efcad6b7SDag-Erling Smørgrav 			err = 1;
1067efcad6b7SDag-Erling Smørgrav 			break;
1068efcad6b7SDag-Erling Smørgrav 		}
1069efcad6b7SDag-Erling Smørgrav 		if (!S_ISDIR(aa->perm)) {
1070efcad6b7SDag-Erling Smørgrav 			error("Can't change directory: \"%s\" is not "
1071efcad6b7SDag-Erling Smørgrav 			    "a directory", tmp);
1072efcad6b7SDag-Erling Smørgrav 			xfree(tmp);
1073efcad6b7SDag-Erling Smørgrav 			err = 1;
1074efcad6b7SDag-Erling Smørgrav 			break;
1075efcad6b7SDag-Erling Smørgrav 		}
1076efcad6b7SDag-Erling Smørgrav 		xfree(*pwd);
1077efcad6b7SDag-Erling Smørgrav 		*pwd = tmp;
1078efcad6b7SDag-Erling Smørgrav 		break;
1079efcad6b7SDag-Erling Smørgrav 	case I_LS:
1080efcad6b7SDag-Erling Smørgrav 		if (!path1) {
1081efcad6b7SDag-Erling Smørgrav 			do_globbed_ls(conn, *pwd, *pwd, lflag);
1082efcad6b7SDag-Erling Smørgrav 			break;
1083efcad6b7SDag-Erling Smørgrav 		}
1084efcad6b7SDag-Erling Smørgrav 
1085efcad6b7SDag-Erling Smørgrav 		/* Strip pwd off beginning of non-absolute paths */
1086efcad6b7SDag-Erling Smørgrav 		tmp = NULL;
1087efcad6b7SDag-Erling Smørgrav 		if (*path1 != '/')
1088efcad6b7SDag-Erling Smørgrav 			tmp = *pwd;
1089efcad6b7SDag-Erling Smørgrav 
1090efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1091efcad6b7SDag-Erling Smørgrav 		err = do_globbed_ls(conn, path1, tmp, lflag);
1092efcad6b7SDag-Erling Smørgrav 		break;
1093efcad6b7SDag-Erling Smørgrav 	case I_LCHDIR:
1094efcad6b7SDag-Erling Smørgrav 		if (chdir(path1) == -1) {
1095efcad6b7SDag-Erling Smørgrav 			error("Couldn't change local directory to "
1096efcad6b7SDag-Erling Smørgrav 			    "\"%s\": %s", path1, strerror(errno));
1097efcad6b7SDag-Erling Smørgrav 			err = 1;
1098efcad6b7SDag-Erling Smørgrav 		}
1099efcad6b7SDag-Erling Smørgrav 		break;
1100efcad6b7SDag-Erling Smørgrav 	case I_LMKDIR:
1101efcad6b7SDag-Erling Smørgrav 		if (mkdir(path1, 0777) == -1) {
1102efcad6b7SDag-Erling Smørgrav 			error("Couldn't create local directory "
1103efcad6b7SDag-Erling Smørgrav 			    "\"%s\": %s", path1, strerror(errno));
1104efcad6b7SDag-Erling Smørgrav 			err = 1;
1105efcad6b7SDag-Erling Smørgrav 		}
1106efcad6b7SDag-Erling Smørgrav 		break;
1107efcad6b7SDag-Erling Smørgrav 	case I_LLS:
1108efcad6b7SDag-Erling Smørgrav 		local_do_ls(cmd);
1109efcad6b7SDag-Erling Smørgrav 		break;
1110efcad6b7SDag-Erling Smørgrav 	case I_SHELL:
1111efcad6b7SDag-Erling Smørgrav 		local_do_shell(cmd);
1112efcad6b7SDag-Erling Smørgrav 		break;
1113efcad6b7SDag-Erling Smørgrav 	case I_LUMASK:
1114efcad6b7SDag-Erling Smørgrav 		umask(n_arg);
1115efcad6b7SDag-Erling Smørgrav 		printf("Local umask: %03lo\n", n_arg);
1116efcad6b7SDag-Erling Smørgrav 		break;
1117efcad6b7SDag-Erling Smørgrav 	case I_CHMOD:
1118efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1119efcad6b7SDag-Erling Smørgrav 		attrib_clear(&a);
1120efcad6b7SDag-Erling Smørgrav 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1121efcad6b7SDag-Erling Smørgrav 		a.perm = n_arg;
1122efcad6b7SDag-Erling Smørgrav 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1123d74d50a8SDag-Erling Smørgrav 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1124efcad6b7SDag-Erling Smørgrav 			printf("Changing mode on %s\n", g.gl_pathv[i]);
1125efcad6b7SDag-Erling Smørgrav 			err = do_setstat(conn, g.gl_pathv[i], &a);
1126efcad6b7SDag-Erling Smørgrav 			if (err != 0 && err_abort)
1127efcad6b7SDag-Erling Smørgrav 				break;
1128efcad6b7SDag-Erling Smørgrav 		}
1129efcad6b7SDag-Erling Smørgrav 		break;
1130efcad6b7SDag-Erling Smørgrav 	case I_CHOWN:
1131efcad6b7SDag-Erling Smørgrav 	case I_CHGRP:
1132efcad6b7SDag-Erling Smørgrav 		path1 = make_absolute(path1, *pwd);
1133efcad6b7SDag-Erling Smørgrav 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1134d74d50a8SDag-Erling Smørgrav 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1135efcad6b7SDag-Erling Smørgrav 			if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1136efcad6b7SDag-Erling Smørgrav 				if (err != 0 && err_abort)
1137efcad6b7SDag-Erling Smørgrav 					break;
1138efcad6b7SDag-Erling Smørgrav 				else
1139efcad6b7SDag-Erling Smørgrav 					continue;
1140efcad6b7SDag-Erling Smørgrav 			}
1141efcad6b7SDag-Erling Smørgrav 			if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1142efcad6b7SDag-Erling Smørgrav 				error("Can't get current ownership of "
1143efcad6b7SDag-Erling Smørgrav 				    "remote file \"%s\"", g.gl_pathv[i]);
1144efcad6b7SDag-Erling Smørgrav 				if (err != 0 && err_abort)
1145efcad6b7SDag-Erling Smørgrav 					break;
1146efcad6b7SDag-Erling Smørgrav 				else
1147efcad6b7SDag-Erling Smørgrav 					continue;
1148efcad6b7SDag-Erling Smørgrav 			}
1149efcad6b7SDag-Erling Smørgrav 			aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1150efcad6b7SDag-Erling Smørgrav 			if (cmdnum == I_CHOWN) {
1151efcad6b7SDag-Erling Smørgrav 				printf("Changing owner on %s\n", g.gl_pathv[i]);
1152efcad6b7SDag-Erling Smørgrav 				aa->uid = n_arg;
1153efcad6b7SDag-Erling Smørgrav 			} else {
1154efcad6b7SDag-Erling Smørgrav 				printf("Changing group on %s\n", g.gl_pathv[i]);
1155efcad6b7SDag-Erling Smørgrav 				aa->gid = n_arg;
1156efcad6b7SDag-Erling Smørgrav 			}
1157efcad6b7SDag-Erling Smørgrav 			err = do_setstat(conn, g.gl_pathv[i], aa);
1158efcad6b7SDag-Erling Smørgrav 			if (err != 0 && err_abort)
1159efcad6b7SDag-Erling Smørgrav 				break;
1160efcad6b7SDag-Erling Smørgrav 		}
1161efcad6b7SDag-Erling Smørgrav 		break;
1162efcad6b7SDag-Erling Smørgrav 	case I_PWD:
1163efcad6b7SDag-Erling Smørgrav 		printf("Remote working directory: %s\n", *pwd);
1164efcad6b7SDag-Erling Smørgrav 		break;
1165efcad6b7SDag-Erling Smørgrav 	case I_LPWD:
1166efcad6b7SDag-Erling Smørgrav 		if (!getcwd(path_buf, sizeof(path_buf))) {
1167efcad6b7SDag-Erling Smørgrav 			error("Couldn't get local cwd: %s", strerror(errno));
1168efcad6b7SDag-Erling Smørgrav 			err = -1;
1169efcad6b7SDag-Erling Smørgrav 			break;
1170efcad6b7SDag-Erling Smørgrav 		}
1171efcad6b7SDag-Erling Smørgrav 		printf("Local working directory: %s\n", path_buf);
1172efcad6b7SDag-Erling Smørgrav 		break;
1173efcad6b7SDag-Erling Smørgrav 	case I_QUIT:
1174efcad6b7SDag-Erling Smørgrav 		/* Processed below */
1175efcad6b7SDag-Erling Smørgrav 		break;
1176efcad6b7SDag-Erling Smørgrav 	case I_HELP:
1177efcad6b7SDag-Erling Smørgrav 		help();
1178efcad6b7SDag-Erling Smørgrav 		break;
1179efcad6b7SDag-Erling Smørgrav 	case I_VERSION:
1180efcad6b7SDag-Erling Smørgrav 		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1181efcad6b7SDag-Erling Smørgrav 		break;
1182efcad6b7SDag-Erling Smørgrav 	case I_PROGRESS:
1183efcad6b7SDag-Erling Smørgrav 		showprogress = !showprogress;
1184efcad6b7SDag-Erling Smørgrav 		if (showprogress)
1185efcad6b7SDag-Erling Smørgrav 			printf("Progress meter enabled\n");
1186efcad6b7SDag-Erling Smørgrav 		else
1187efcad6b7SDag-Erling Smørgrav 			printf("Progress meter disabled\n");
1188efcad6b7SDag-Erling Smørgrav 		break;
1189efcad6b7SDag-Erling Smørgrav 	default:
1190efcad6b7SDag-Erling Smørgrav 		fatal("%d is not implemented", cmdnum);
1191efcad6b7SDag-Erling Smørgrav 	}
1192efcad6b7SDag-Erling Smørgrav 
1193efcad6b7SDag-Erling Smørgrav 	if (g.gl_pathc)
1194efcad6b7SDag-Erling Smørgrav 		globfree(&g);
1195efcad6b7SDag-Erling Smørgrav 	if (path1)
1196efcad6b7SDag-Erling Smørgrav 		xfree(path1);
1197efcad6b7SDag-Erling Smørgrav 	if (path2)
1198efcad6b7SDag-Erling Smørgrav 		xfree(path2);
1199efcad6b7SDag-Erling Smørgrav 
1200efcad6b7SDag-Erling Smørgrav 	/* If an unignored error occurs in batch mode we should abort. */
1201efcad6b7SDag-Erling Smørgrav 	if (err_abort && err != 0)
1202efcad6b7SDag-Erling Smørgrav 		return (-1);
1203efcad6b7SDag-Erling Smørgrav 	else if (cmdnum == I_QUIT)
1204efcad6b7SDag-Erling Smørgrav 		return (1);
1205efcad6b7SDag-Erling Smørgrav 
1206efcad6b7SDag-Erling Smørgrav 	return (0);
1207efcad6b7SDag-Erling Smørgrav }
1208efcad6b7SDag-Erling Smørgrav 
1209efcad6b7SDag-Erling Smørgrav int
1210efcad6b7SDag-Erling Smørgrav interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1211efcad6b7SDag-Erling Smørgrav {
1212efcad6b7SDag-Erling Smørgrav 	char *pwd;
1213efcad6b7SDag-Erling Smørgrav 	char *dir = NULL;
1214efcad6b7SDag-Erling Smørgrav 	char cmd[2048];
1215efcad6b7SDag-Erling Smørgrav 	struct sftp_conn *conn;
1216efcad6b7SDag-Erling Smørgrav 	int err;
1217efcad6b7SDag-Erling Smørgrav 
1218efcad6b7SDag-Erling Smørgrav 	conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1219efcad6b7SDag-Erling Smørgrav 	if (conn == NULL)
1220efcad6b7SDag-Erling Smørgrav 		fatal("Couldn't initialise connection to server");
1221efcad6b7SDag-Erling Smørgrav 
1222efcad6b7SDag-Erling Smørgrav 	pwd = do_realpath(conn, ".");
1223efcad6b7SDag-Erling Smørgrav 	if (pwd == NULL)
1224efcad6b7SDag-Erling Smørgrav 		fatal("Need cwd");
1225efcad6b7SDag-Erling Smørgrav 
1226efcad6b7SDag-Erling Smørgrav 	if (file1 != NULL) {
1227efcad6b7SDag-Erling Smørgrav 		dir = xstrdup(file1);
1228efcad6b7SDag-Erling Smørgrav 		dir = make_absolute(dir, pwd);
1229efcad6b7SDag-Erling Smørgrav 
1230efcad6b7SDag-Erling Smørgrav 		if (remote_is_dir(conn, dir) && file2 == NULL) {
1231efcad6b7SDag-Erling Smørgrav 			printf("Changing to: %s\n", dir);
1232efcad6b7SDag-Erling Smørgrav 			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1233efcad6b7SDag-Erling Smørgrav 			if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0)
1234efcad6b7SDag-Erling Smørgrav 				return (-1);
1235efcad6b7SDag-Erling Smørgrav 		} else {
1236efcad6b7SDag-Erling Smørgrav 			if (file2 == NULL)
1237efcad6b7SDag-Erling Smørgrav 				snprintf(cmd, sizeof cmd, "get %s", dir);
1238efcad6b7SDag-Erling Smørgrav 			else
1239efcad6b7SDag-Erling Smørgrav 				snprintf(cmd, sizeof cmd, "get %s %s", dir,
1240efcad6b7SDag-Erling Smørgrav 				    file2);
1241efcad6b7SDag-Erling Smørgrav 
1242efcad6b7SDag-Erling Smørgrav 			err = parse_dispatch_command(conn, cmd, &pwd, 1);
1243efcad6b7SDag-Erling Smørgrav 			xfree(dir);
1244efcad6b7SDag-Erling Smørgrav 			xfree(pwd);
1245efcad6b7SDag-Erling Smørgrav 			return (err);
1246efcad6b7SDag-Erling Smørgrav 		}
1247efcad6b7SDag-Erling Smørgrav 		xfree(dir);
1248efcad6b7SDag-Erling Smørgrav 	}
1249efcad6b7SDag-Erling Smørgrav 
1250efcad6b7SDag-Erling Smørgrav #if HAVE_SETVBUF
1251efcad6b7SDag-Erling Smørgrav 	setvbuf(stdout, NULL, _IOLBF, 0);
1252efcad6b7SDag-Erling Smørgrav 	setvbuf(infile, NULL, _IOLBF, 0);
1253efcad6b7SDag-Erling Smørgrav #else
1254efcad6b7SDag-Erling Smørgrav        setlinebuf(stdout);
1255efcad6b7SDag-Erling Smørgrav        setlinebuf(infile);
1256efcad6b7SDag-Erling Smørgrav #endif
1257efcad6b7SDag-Erling Smørgrav 
1258efcad6b7SDag-Erling Smørgrav 	err = 0;
1259efcad6b7SDag-Erling Smørgrav 	for (;;) {
1260efcad6b7SDag-Erling Smørgrav 		char *cp;
1261efcad6b7SDag-Erling Smørgrav 
1262d74d50a8SDag-Erling Smørgrav 		signal(SIGINT, SIG_IGN);
1263d74d50a8SDag-Erling Smørgrav 
1264efcad6b7SDag-Erling Smørgrav 		printf("sftp> ");
1265efcad6b7SDag-Erling Smørgrav 
1266efcad6b7SDag-Erling Smørgrav 		/* XXX: use libedit */
1267efcad6b7SDag-Erling Smørgrav 		if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1268efcad6b7SDag-Erling Smørgrav 			printf("\n");
1269efcad6b7SDag-Erling Smørgrav 			break;
1270efcad6b7SDag-Erling Smørgrav 		}
1271efcad6b7SDag-Erling Smørgrav 
1272efcad6b7SDag-Erling Smørgrav 		if (batchmode) /* Echo command */
1273efcad6b7SDag-Erling Smørgrav 			printf("%s", cmd);
1274efcad6b7SDag-Erling Smørgrav 
1275efcad6b7SDag-Erling Smørgrav 		cp = strrchr(cmd, '\n');
1276efcad6b7SDag-Erling Smørgrav 		if (cp)
1277efcad6b7SDag-Erling Smørgrav 			*cp = '\0';
1278efcad6b7SDag-Erling Smørgrav 
1279d74d50a8SDag-Erling Smørgrav 		/* Handle user interrupts gracefully during commands */
1280d74d50a8SDag-Erling Smørgrav 		interrupted = 0;
1281d74d50a8SDag-Erling Smørgrav 		signal(SIGINT, cmd_interrupt);
1282d74d50a8SDag-Erling Smørgrav 
1283efcad6b7SDag-Erling Smørgrav 		err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1284efcad6b7SDag-Erling Smørgrav 		if (err != 0)
1285efcad6b7SDag-Erling Smørgrav 			break;
1286efcad6b7SDag-Erling Smørgrav 	}
1287efcad6b7SDag-Erling Smørgrav 	xfree(pwd);
1288efcad6b7SDag-Erling Smørgrav 
1289efcad6b7SDag-Erling Smørgrav 	/* err == 1 signifies normal "quit" exit */
1290efcad6b7SDag-Erling Smørgrav 	return (err >= 0 ? 0 : -1);
1291efcad6b7SDag-Erling Smørgrav }
1292d0c8c0bcSDag-Erling Smørgrav 
1293ae1f160dSDag-Erling Smørgrav static void
1294d95e11bfSDag-Erling Smørgrav connect_to_server(char *path, char **args, int *in, int *out)
12951e8db6e2SBrian Feldman {
12961e8db6e2SBrian Feldman 	int c_in, c_out;
1297ee21a45fSDag-Erling Smørgrav 
12981e8db6e2SBrian Feldman #ifdef USE_PIPES
12991e8db6e2SBrian Feldman 	int pin[2], pout[2];
1300ee21a45fSDag-Erling Smørgrav 
13011e8db6e2SBrian Feldman 	if ((pipe(pin) == -1) || (pipe(pout) == -1))
13021e8db6e2SBrian Feldman 		fatal("pipe: %s", strerror(errno));
13031e8db6e2SBrian Feldman 	*in = pin[0];
13041e8db6e2SBrian Feldman 	*out = pout[1];
13051e8db6e2SBrian Feldman 	c_in = pout[0];
13061e8db6e2SBrian Feldman 	c_out = pin[1];
13071e8db6e2SBrian Feldman #else /* USE_PIPES */
13081e8db6e2SBrian Feldman 	int inout[2];
1309ee21a45fSDag-Erling Smørgrav 
13101e8db6e2SBrian Feldman 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
13111e8db6e2SBrian Feldman 		fatal("socketpair: %s", strerror(errno));
13121e8db6e2SBrian Feldman 	*in = *out = inout[0];
13131e8db6e2SBrian Feldman 	c_in = c_out = inout[1];
13141e8db6e2SBrian Feldman #endif /* USE_PIPES */
13151e8db6e2SBrian Feldman 
1316d95e11bfSDag-Erling Smørgrav 	if ((sshpid = fork()) == -1)
13171e8db6e2SBrian Feldman 		fatal("fork: %s", strerror(errno));
1318d95e11bfSDag-Erling Smørgrav 	else if (sshpid == 0) {
13191e8db6e2SBrian Feldman 		if ((dup2(c_in, STDIN_FILENO) == -1) ||
13201e8db6e2SBrian Feldman 		    (dup2(c_out, STDOUT_FILENO) == -1)) {
13211e8db6e2SBrian Feldman 			fprintf(stderr, "dup2: %s\n", strerror(errno));
1322d74d50a8SDag-Erling Smørgrav 			_exit(1);
13231e8db6e2SBrian Feldman 		}
13241e8db6e2SBrian Feldman 		close(*in);
13251e8db6e2SBrian Feldman 		close(*out);
13261e8db6e2SBrian Feldman 		close(c_in);
13271e8db6e2SBrian Feldman 		close(c_out);
1328d74d50a8SDag-Erling Smørgrav 
1329d74d50a8SDag-Erling Smørgrav 		/*
1330d74d50a8SDag-Erling Smørgrav 		 * The underlying ssh is in the same process group, so we must
1331d74d50a8SDag-Erling Smørgrav 		 * ignore SIGINT if we want to gracefully abort commands,
1332d74d50a8SDag-Erling Smørgrav 		 * otherwise the signal will make it to the ssh process and
1333d74d50a8SDag-Erling Smørgrav 		 * kill it too
1334d74d50a8SDag-Erling Smørgrav 		 */
1335d74d50a8SDag-Erling Smørgrav 		signal(SIGINT, SIG_IGN);
1336d74d50a8SDag-Erling Smørgrav 		execvp(path, args);
1337ae1f160dSDag-Erling Smørgrav 		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1338d74d50a8SDag-Erling Smørgrav 		_exit(1);
13391e8db6e2SBrian Feldman 	}
13401e8db6e2SBrian Feldman 
1341d95e11bfSDag-Erling Smørgrav 	signal(SIGTERM, killchild);
1342d95e11bfSDag-Erling Smørgrav 	signal(SIGINT, killchild);
1343d95e11bfSDag-Erling Smørgrav 	signal(SIGHUP, killchild);
13441e8db6e2SBrian Feldman 	close(c_in);
13451e8db6e2SBrian Feldman 	close(c_out);
13461e8db6e2SBrian Feldman }
13471e8db6e2SBrian Feldman 
1348ae1f160dSDag-Erling Smørgrav static void
13491e8db6e2SBrian Feldman usage(void)
13501e8db6e2SBrian Feldman {
1351ae1f160dSDag-Erling Smørgrav 	extern char *__progname;
1352ae1f160dSDag-Erling Smørgrav 
1353ae1f160dSDag-Erling Smørgrav 	fprintf(stderr,
1354efcad6b7SDag-Erling Smørgrav 	    "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1355efcad6b7SDag-Erling Smørgrav 	    "            [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1356efcad6b7SDag-Erling Smørgrav 	    "            [-S program] [-s subsystem | sftp_server] host\n"
1357efcad6b7SDag-Erling Smørgrav 	    "       %s [[user@]host[:file [file]]]\n"
1358efcad6b7SDag-Erling Smørgrav 	    "       %s [[user@]host[:dir[/]]]\n"
1359efcad6b7SDag-Erling Smørgrav 	    "       %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
13601e8db6e2SBrian Feldman 	exit(1);
13611e8db6e2SBrian Feldman }
13621e8db6e2SBrian Feldman 
13631e8db6e2SBrian Feldman int
13641e8db6e2SBrian Feldman main(int argc, char **argv)
13651e8db6e2SBrian Feldman {
1366d0c8c0bcSDag-Erling Smørgrav 	int in, out, ch, err;
1367d74d50a8SDag-Erling Smørgrav 	char *host, *userhost, *cp, *file2 = NULL;
1368ae1f160dSDag-Erling Smørgrav 	int debug_level = 0, sshver = 2;
1369ae1f160dSDag-Erling Smørgrav 	char *file1 = NULL, *sftp_server = NULL;
1370ae1f160dSDag-Erling Smørgrav 	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1371ae1f160dSDag-Erling Smørgrav 	LogLevel ll = SYSLOG_LEVEL_INFO;
1372ae1f160dSDag-Erling Smørgrav 	arglist args;
13731e8db6e2SBrian Feldman 	extern int optind;
13741e8db6e2SBrian Feldman 	extern char *optarg;
13751e8db6e2SBrian Feldman 
1376d95e11bfSDag-Erling Smørgrav 	__progname = ssh_get_progname(argv[0]);
1377ae1f160dSDag-Erling Smørgrav 	args.list = NULL;
1378ae1f160dSDag-Erling Smørgrav 	addargs(&args, "ssh");		/* overwritten with ssh_program */
1379ae1f160dSDag-Erling Smørgrav 	addargs(&args, "-oForwardX11 no");
1380ae1f160dSDag-Erling Smørgrav 	addargs(&args, "-oForwardAgent no");
1381ae1f160dSDag-Erling Smørgrav 	addargs(&args, "-oClearAllForwardings yes");
1382efcad6b7SDag-Erling Smørgrav 
1383ae1f160dSDag-Erling Smørgrav 	ll = SYSLOG_LEVEL_INFO;
1384efcad6b7SDag-Erling Smørgrav 	infile = stdin;
13851e8db6e2SBrian Feldman 
1386ae1f160dSDag-Erling Smørgrav 	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
13871e8db6e2SBrian Feldman 		switch (ch) {
13881e8db6e2SBrian Feldman 		case 'C':
1389ae1f160dSDag-Erling Smørgrav 			addargs(&args, "-C");
13901e8db6e2SBrian Feldman 			break;
13911e8db6e2SBrian Feldman 		case 'v':
1392ae1f160dSDag-Erling Smørgrav 			if (debug_level < 3) {
1393ae1f160dSDag-Erling Smørgrav 				addargs(&args, "-v");
1394ae1f160dSDag-Erling Smørgrav 				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1395ae1f160dSDag-Erling Smørgrav 			}
1396ae1f160dSDag-Erling Smørgrav 			debug_level++;
13971e8db6e2SBrian Feldman 			break;
1398ae1f160dSDag-Erling Smørgrav 		case 'F':
13991e8db6e2SBrian Feldman 		case 'o':
1400ae1f160dSDag-Erling Smørgrav 			addargs(&args, "-%c%s", ch, optarg);
14011e8db6e2SBrian Feldman 			break;
14021e8db6e2SBrian Feldman 		case '1':
1403ae1f160dSDag-Erling Smørgrav 			sshver = 1;
14041e8db6e2SBrian Feldman 			if (sftp_server == NULL)
14051e8db6e2SBrian Feldman 				sftp_server = _PATH_SFTP_SERVER;
14061e8db6e2SBrian Feldman 			break;
14071e8db6e2SBrian Feldman 		case 's':
14081e8db6e2SBrian Feldman 			sftp_server = optarg;
14091e8db6e2SBrian Feldman 			break;
14101e8db6e2SBrian Feldman 		case 'S':
14111e8db6e2SBrian Feldman 			ssh_program = optarg;
14121e8db6e2SBrian Feldman 			break;
14131e8db6e2SBrian Feldman 		case 'b':
1414efcad6b7SDag-Erling Smørgrav 			if (batchmode)
1415efcad6b7SDag-Erling Smørgrav 				fatal("Batch file already specified.");
1416efcad6b7SDag-Erling Smørgrav 
1417efcad6b7SDag-Erling Smørgrav 			/* Allow "-" as stdin */
1418efcad6b7SDag-Erling Smørgrav 			if (strcmp(optarg, "-") != 0 &&
1419efcad6b7SDag-Erling Smørgrav 			   (infile = fopen(optarg, "r")) == NULL)
14201e8db6e2SBrian Feldman 				fatal("%s (%s).", strerror(errno), optarg);
1421d0c8c0bcSDag-Erling Smørgrav 			showprogress = 0;
1422efcad6b7SDag-Erling Smørgrav 			batchmode = 1;
14231e8db6e2SBrian Feldman 			break;
1424ae1f160dSDag-Erling Smørgrav 		case 'P':
1425ae1f160dSDag-Erling Smørgrav 			sftp_direct = optarg;
1426ae1f160dSDag-Erling Smørgrav 			break;
1427ae1f160dSDag-Erling Smørgrav 		case 'B':
1428ae1f160dSDag-Erling Smørgrav 			copy_buffer_len = strtol(optarg, &cp, 10);
1429ae1f160dSDag-Erling Smørgrav 			if (copy_buffer_len == 0 || *cp != '\0')
1430ae1f160dSDag-Erling Smørgrav 				fatal("Invalid buffer size \"%s\"", optarg);
1431ae1f160dSDag-Erling Smørgrav 			break;
1432ae1f160dSDag-Erling Smørgrav 		case 'R':
1433ae1f160dSDag-Erling Smørgrav 			num_requests = strtol(optarg, &cp, 10);
1434ae1f160dSDag-Erling Smørgrav 			if (num_requests == 0 || *cp != '\0')
1435ae1f160dSDag-Erling Smørgrav 				fatal("Invalid number of requests \"%s\"",
1436ae1f160dSDag-Erling Smørgrav 				    optarg);
1437ae1f160dSDag-Erling Smørgrav 			break;
14381e8db6e2SBrian Feldman 		case 'h':
14391e8db6e2SBrian Feldman 		default:
14401e8db6e2SBrian Feldman 			usage();
14411e8db6e2SBrian Feldman 		}
14421e8db6e2SBrian Feldman 	}
14431e8db6e2SBrian Feldman 
144452028650SDag-Erling Smørgrav 	if (!isatty(STDERR_FILENO))
144552028650SDag-Erling Smørgrav 		showprogress = 0;
144652028650SDag-Erling Smørgrav 
1447545d5ecaSDag-Erling Smørgrav 	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1448545d5ecaSDag-Erling Smørgrav 
1449ae1f160dSDag-Erling Smørgrav 	if (sftp_direct == NULL) {
14501e8db6e2SBrian Feldman 		if (optind == argc || argc > (optind + 2))
14511e8db6e2SBrian Feldman 			usage();
14521e8db6e2SBrian Feldman 
14531e8db6e2SBrian Feldman 		userhost = xstrdup(argv[optind]);
14541e8db6e2SBrian Feldman 		file2 = argv[optind+1];
14551e8db6e2SBrian Feldman 
1456d0c8c0bcSDag-Erling Smørgrav 		if ((host = strrchr(userhost, '@')) == NULL)
14571e8db6e2SBrian Feldman 			host = userhost;
14581e8db6e2SBrian Feldman 		else {
14591e8db6e2SBrian Feldman 			*host++ = '\0';
14601e8db6e2SBrian Feldman 			if (!userhost[0]) {
14611e8db6e2SBrian Feldman 				fprintf(stderr, "Missing username\n");
14621e8db6e2SBrian Feldman 				usage();
14631e8db6e2SBrian Feldman 			}
1464ae1f160dSDag-Erling Smørgrav 			addargs(&args, "-l%s",userhost);
14651e8db6e2SBrian Feldman 		}
14661e8db6e2SBrian Feldman 
1467efcad6b7SDag-Erling Smørgrav 		if ((cp = colon(host)) != NULL) {
1468efcad6b7SDag-Erling Smørgrav 			*cp++ = '\0';
1469efcad6b7SDag-Erling Smørgrav 			file1 = cp;
1470efcad6b7SDag-Erling Smørgrav 		}
1471efcad6b7SDag-Erling Smørgrav 
14721e8db6e2SBrian Feldman 		host = cleanhostname(host);
14731e8db6e2SBrian Feldman 		if (!*host) {
14741e8db6e2SBrian Feldman 			fprintf(stderr, "Missing hostname\n");
14751e8db6e2SBrian Feldman 			usage();
14761e8db6e2SBrian Feldman 		}
14771e8db6e2SBrian Feldman 
1478ae1f160dSDag-Erling Smørgrav 		addargs(&args, "-oProtocol %d", sshver);
14791e8db6e2SBrian Feldman 
1480ae1f160dSDag-Erling Smørgrav 		/* no subsystem if the server-spec contains a '/' */
1481ae1f160dSDag-Erling Smørgrav 		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1482ae1f160dSDag-Erling Smørgrav 			addargs(&args, "-s");
1483ae1f160dSDag-Erling Smørgrav 
1484ae1f160dSDag-Erling Smørgrav 		addargs(&args, "%s", host);
1485ae1f160dSDag-Erling Smørgrav 		addargs(&args, "%s", (sftp_server != NULL ?
1486ae1f160dSDag-Erling Smørgrav 		    sftp_server : "sftp"));
1487ae1f160dSDag-Erling Smørgrav 		args.list[0] = ssh_program;
14881e8db6e2SBrian Feldman 
1489efcad6b7SDag-Erling Smørgrav 		if (!batchmode)
14901e8db6e2SBrian Feldman 			fprintf(stderr, "Connecting to %s...\n", host);
1491d95e11bfSDag-Erling Smørgrav 		connect_to_server(ssh_program, args.list, &in, &out);
1492ae1f160dSDag-Erling Smørgrav 	} else {
1493ae1f160dSDag-Erling Smørgrav 		args.list = NULL;
1494ae1f160dSDag-Erling Smørgrav 		addargs(&args, "sftp-server");
14951e8db6e2SBrian Feldman 
1496efcad6b7SDag-Erling Smørgrav 		if (!batchmode)
1497ae1f160dSDag-Erling Smørgrav 			fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1498d95e11bfSDag-Erling Smørgrav 		connect_to_server(sftp_direct, args.list, &in, &out);
1499ae1f160dSDag-Erling Smørgrav 	}
15001e8db6e2SBrian Feldman 
1501d0c8c0bcSDag-Erling Smørgrav 	err = interactive_loop(in, out, file1, file2);
15021e8db6e2SBrian Feldman 
150383d2307dSDag-Erling Smørgrav #if !defined(USE_PIPES)
150483d2307dSDag-Erling Smørgrav        shutdown(in, SHUT_RDWR);
150583d2307dSDag-Erling Smørgrav        shutdown(out, SHUT_RDWR);
150683d2307dSDag-Erling Smørgrav #endif
150783d2307dSDag-Erling Smørgrav 
15081e8db6e2SBrian Feldman 	close(in);
15091e8db6e2SBrian Feldman 	close(out);
1510efcad6b7SDag-Erling Smørgrav 	if (batchmode)
15111e8db6e2SBrian Feldman 		fclose(infile);
15121e8db6e2SBrian Feldman 
1513545d5ecaSDag-Erling Smørgrav 	while (waitpid(sshpid, NULL, 0) == -1)
1514545d5ecaSDag-Erling Smørgrav 		if (errno != EINTR)
1515545d5ecaSDag-Erling Smørgrav 			fatal("Couldn't wait for ssh process: %s",
1516545d5ecaSDag-Erling Smørgrav 			    strerror(errno));
15171e8db6e2SBrian Feldman 
1518d0c8c0bcSDag-Erling Smørgrav 	exit(err == 0 ? 0 : 1);
15191e8db6e2SBrian Feldman }
1520