blob: 6a2012910d4adade3290fbcb973a7c869f308a9d [file] [log] [blame]
Damien Miller33804262001-02-04 23:20:18 +11001/*
Damien Miller3db5f532002-02-13 14:10:32 +11002 * Copyright (c) 2001,2002 Damien Miller. All rights reserved.
Damien Miller33804262001-02-04 23:20:18 +11003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
Damien Miller33804262001-02-04 23:20:18 +110025/* XXX: recursive operations */
26
27#include "includes.h"
Damien Miller622accf2002-09-12 10:34:13 +100028RCSID("$OpenBSD: sftp-int.c,v 1.49 2002/09/12 00:13:06 djm Exp $");
Damien Miller4870afd2001-03-14 10:27:09 +110029
Damien Miller33804262001-02-04 23:20:18 +110030#include "buffer.h"
31#include "xmalloc.h"
32#include "log.h"
33#include "pathnames.h"
34
35#include "sftp.h"
36#include "sftp-common.h"
Damien Miller4870afd2001-03-14 10:27:09 +110037#include "sftp-glob.h"
Damien Miller33804262001-02-04 23:20:18 +110038#include "sftp-client.h"
39#include "sftp-int.h"
40
Damien Miller058316f2001-03-08 10:08:49 +110041/* File to read commands from */
42extern FILE *infile;
43
Damien Miller8829d362002-02-08 22:04:05 +110044/* Size of buffer used when copying files */
45extern size_t copy_buffer_len;
46
Damien Miller16a13332002-02-13 14:03:56 +110047/* Number of concurrent outstanding requests */
48extern int num_requests;
49
Damien Miller33804262001-02-04 23:20:18 +110050/* Seperators for interactive commands */
51#define WHITESPACE " \t\r\n"
52
53/* Commands for interactive mode */
54#define I_CHDIR 1
55#define I_CHGRP 2
56#define I_CHMOD 3
57#define I_CHOWN 4
58#define I_GET 5
59#define I_HELP 6
60#define I_LCHDIR 7
61#define I_LLS 8
62#define I_LMKDIR 9
63#define I_LPWD 10
64#define I_LS 11
65#define I_LUMASK 12
66#define I_MKDIR 13
67#define I_PUT 14
68#define I_PWD 15
69#define I_QUIT 16
70#define I_RENAME 17
71#define I_RM 18
72#define I_RMDIR 19
73#define I_SHELL 20
Damien Miller058316f2001-03-08 10:08:49 +110074#define I_SYMLINK 21
Ben Lindstromf78682d2001-03-14 21:26:27 +000075#define I_VERSION 22
Damien Miller33804262001-02-04 23:20:18 +110076
77struct CMD {
Damien Miller33804262001-02-04 23:20:18 +110078 const char *c;
Kevin Steves62c45db2001-02-05 13:42:43 +000079 const int n;
Damien Miller33804262001-02-04 23:20:18 +110080};
81
82const struct CMD cmds[] = {
Ben Lindstrom59e12492001-08-15 23:22:56 +000083 { "bye", I_QUIT },
Damien Millerd7686fd2001-02-10 00:40:03 +110084 { "cd", I_CHDIR },
85 { "chdir", I_CHDIR },
86 { "chgrp", I_CHGRP },
87 { "chmod", I_CHMOD },
88 { "chown", I_CHOWN },
89 { "dir", I_LS },
90 { "exit", I_QUIT },
91 { "get", I_GET },
Ben Lindstrom23d9a6d2001-04-11 23:05:17 +000092 { "mget", I_GET },
Damien Millerd7686fd2001-02-10 00:40:03 +110093 { "help", I_HELP },
94 { "lcd", I_LCHDIR },
95 { "lchdir", I_LCHDIR },
96 { "lls", I_LLS },
97 { "lmkdir", I_LMKDIR },
Damien Miller058316f2001-03-08 10:08:49 +110098 { "ln", I_SYMLINK },
Damien Millerd7686fd2001-02-10 00:40:03 +110099 { "lpwd", I_LPWD },
100 { "ls", I_LS },
101 { "lumask", I_LUMASK },
102 { "mkdir", I_MKDIR },
103 { "put", I_PUT },
Ben Lindstrom23d9a6d2001-04-11 23:05:17 +0000104 { "mput", I_PUT },
Damien Millerd7686fd2001-02-10 00:40:03 +1100105 { "pwd", I_PWD },
106 { "quit", I_QUIT },
107 { "rename", I_RENAME },
108 { "rm", I_RM },
109 { "rmdir", I_RMDIR },
Damien Miller058316f2001-03-08 10:08:49 +1100110 { "symlink", I_SYMLINK },
Ben Lindstromf78682d2001-03-14 21:26:27 +0000111 { "version", I_VERSION },
Kevin Steves62c45db2001-02-05 13:42:43 +0000112 { "!", I_SHELL },
113 { "?", I_HELP },
114 { NULL, -1}
Damien Miller33804262001-02-04 23:20:18 +1100115};
116
Ben Lindstrombba81212001-06-25 05:01:22 +0000117static void
Damien Miller33804262001-02-04 23:20:18 +1100118help(void)
119{
120 printf("Available commands:\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100121 printf("cd path Change remote directory to 'path'\n");
122 printf("lcd path Change local directory to 'path'\n");
123 printf("chgrp grp path Change group of file 'path' to 'grp'\n");
124 printf("chmod mode path Change permissions of file 'path' to 'mode'\n");
125 printf("chown own path Change owner of file 'path' to 'own'\n");
126 printf("help Display this help text\n");
127 printf("get remote-path [local-path] Download file\n");
128 printf("lls [ls-options [path]] Display local directory listing\n");
Damien Miller058316f2001-03-08 10:08:49 +1100129 printf("ln oldpath newpath Symlink remote file\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100130 printf("lmkdir path Create local directory\n");
131 printf("lpwd Print local working directory\n");
132 printf("ls [path] Display remote directory listing\n");
133 printf("lumask umask Set local umask to 'umask'\n");
134 printf("mkdir path Create remote directory\n");
135 printf("put local-path [remote-path] Upload file\n");
136 printf("pwd Display remote working directory\n");
137 printf("exit Quit sftp\n");
138 printf("quit Quit sftp\n");
139 printf("rename oldpath newpath Rename remote file\n");
140 printf("rmdir path Remove remote directory\n");
141 printf("rm path Delete remote file\n");
Damien Miller058316f2001-03-08 10:08:49 +1100142 printf("symlink oldpath newpath Symlink remote file\n");
Ben Lindstromf78682d2001-03-14 21:26:27 +0000143 printf("version Show SFTP version\n");
Damien Miller33804262001-02-04 23:20:18 +1100144 printf("!command Execute 'command' in local shell\n");
145 printf("! Escape to local shell\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100146 printf("? Synonym for help\n");
Damien Miller33804262001-02-04 23:20:18 +1100147}
148
Ben Lindstrombba81212001-06-25 05:01:22 +0000149static void
Damien Miller33804262001-02-04 23:20:18 +1100150local_do_shell(const char *args)
151{
Ben Lindstrom206941f2001-04-15 14:27:16 +0000152 int status;
Damien Miller33804262001-02-04 23:20:18 +1100153 char *shell;
154 pid_t pid;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000155
Damien Miller33804262001-02-04 23:20:18 +1100156 if (!*args)
157 args = NULL;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000158
Damien Miller33804262001-02-04 23:20:18 +1100159 if ((shell = getenv("SHELL")) == NULL)
160 shell = _PATH_BSHELL;
161
162 if ((pid = fork()) == -1)
163 fatal("Couldn't fork: %s", strerror(errno));
164
165 if (pid == 0) {
166 /* XXX: child has pipe fds to ssh subproc open - issue? */
167 if (args) {
168 debug3("Executing %s -c \"%s\"", shell, args);
Damien Millerefb1edf2001-07-14 12:19:36 +1000169 execl(shell, shell, "-c", args, (char *)NULL);
Damien Miller33804262001-02-04 23:20:18 +1100170 } else {
171 debug3("Executing %s", shell);
Damien Millerefb1edf2001-07-14 12:19:36 +1000172 execl(shell, shell, (char *)NULL);
Damien Miller33804262001-02-04 23:20:18 +1100173 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000174 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
Damien Miller33804262001-02-04 23:20:18 +1100175 strerror(errno));
176 _exit(1);
177 }
Ben Lindstrom47fd8112002-04-02 20:48:19 +0000178 while (waitpid(pid, &status, 0) == -1)
179 if (errno != EINTR)
180 fatal("Couldn't wait for child: %s", strerror(errno));
Damien Miller33804262001-02-04 23:20:18 +1100181 if (!WIFEXITED(status))
182 error("Shell exited abormally");
183 else if (WEXITSTATUS(status))
184 error("Shell exited with status %d", WEXITSTATUS(status));
185}
186
Ben Lindstrombba81212001-06-25 05:01:22 +0000187static void
Damien Miller33804262001-02-04 23:20:18 +1100188local_do_ls(const char *args)
189{
190 if (!args || !*args)
Damien Millerd7686fd2001-02-10 00:40:03 +1100191 local_do_shell(_PATH_LS);
Damien Miller33804262001-02-04 23:20:18 +1100192 else {
Damien Millerd7686fd2001-02-10 00:40:03 +1100193 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
194 char *buf = xmalloc(len);
Damien Miller33804262001-02-04 23:20:18 +1100195
196 /* XXX: quoting - rip quoting code from ftp? */
Damien Millerd7686fd2001-02-10 00:40:03 +1100197 snprintf(buf, len, _PATH_LS " %s", args);
Damien Miller33804262001-02-04 23:20:18 +1100198 local_do_shell(buf);
Damien Millerd7686fd2001-02-10 00:40:03 +1100199 xfree(buf);
Damien Miller33804262001-02-04 23:20:18 +1100200 }
201}
202
Damien Millere1a49812002-09-12 09:54:25 +1000203/* Strip one path (usually the pwd) from the start of another */
204static char *
205path_strip(char *path, char *strip)
206{
207 size_t len;
Damien Millere1a49812002-09-12 09:54:25 +1000208
209 if (strip == NULL)
210 return (xstrdup(path));
211
212 len = strlen(strip);
213 if (strip != NULL && strncmp(path, strip, len) == 0) {
214 if (strip[len - 1] != '/' && path[len] == '/')
215 len++;
216 return (xstrdup(path + len));
217 }
218
219 return (xstrdup(path));
220}
221
Ben Lindstrombba81212001-06-25 05:01:22 +0000222static char *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000223path_append(char *p1, char *p2)
224{
225 char *ret;
Ben Lindstromcf00df62001-03-17 00:37:31 +0000226 int len = strlen(p1) + strlen(p2) + 2;
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000227
Ben Lindstromcf00df62001-03-17 00:37:31 +0000228 ret = xmalloc(len);
229 strlcpy(ret, p1, len);
Damien Millere1a49812002-09-12 09:54:25 +1000230 if (p1[strlen(p1) - 1] != '/')
Ben Lindstrom95148e32001-08-06 21:30:53 +0000231 strlcat(ret, "/", len);
Ben Lindstromcf00df62001-03-17 00:37:31 +0000232 strlcat(ret, p2, len);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000233
234 return(ret);
235}
236
Ben Lindstrombba81212001-06-25 05:01:22 +0000237static char *
Damien Miller33804262001-02-04 23:20:18 +1100238make_absolute(char *p, char *pwd)
239{
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000240 char *abs;
Damien Miller33804262001-02-04 23:20:18 +1100241
242 /* Derelativise */
243 if (p && p[0] != '/') {
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000244 abs = path_append(pwd, p);
Damien Miller33804262001-02-04 23:20:18 +1100245 xfree(p);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000246 return(abs);
247 } else
248 return(p);
249}
250
Ben Lindstrombba81212001-06-25 05:01:22 +0000251static int
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000252infer_path(const char *p, char **ifp)
253{
254 char *cp;
255
256 cp = strrchr(p, '/');
257 if (cp == NULL) {
258 *ifp = xstrdup(p);
259 return(0);
Damien Miller33804262001-02-04 23:20:18 +1100260 }
261
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000262 if (!cp[1]) {
263 error("Invalid path");
264 return(-1);
265 }
266
267 *ifp = xstrdup(cp + 1);
268 return(0);
Damien Miller33804262001-02-04 23:20:18 +1100269}
270
Ben Lindstrombba81212001-06-25 05:01:22 +0000271static int
Damien Miller33804262001-02-04 23:20:18 +1100272parse_getput_flags(const char **cpp, int *pflag)
273{
274 const char *cp = *cpp;
275
276 /* Check for flags */
277 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100278 switch (cp[1]) {
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000279 case 'p':
Damien Miller33804262001-02-04 23:20:18 +1100280 case 'P':
281 *pflag = 1;
282 break;
283 default:
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000284 error("Invalid flag -%c", cp[1]);
Damien Miller33804262001-02-04 23:20:18 +1100285 return(-1);
286 }
287 cp += 2;
288 *cpp = cp + strspn(cp, WHITESPACE);
289 }
290
291 return(0);
292}
293
Ben Lindstrombba81212001-06-25 05:01:22 +0000294static int
Damien Millere1a49812002-09-12 09:54:25 +1000295parse_ls_flags(const char **cpp, int *lflag)
296{
297 const char *cp = *cpp;
298
299 /* Check for flags */
300 if (cp++[0] == '-') {
301 for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
302 switch (*cp) {
303 case 'l':
304 *lflag = 1;
305 break;
306 default:
307 error("Invalid flag -%c", *cp);
308 return(-1);
309 }
310 }
311 *cpp = cp + strspn(cp, WHITESPACE);
312 }
313
314 return(0);
315}
316
317static int
Damien Miller33804262001-02-04 23:20:18 +1100318get_pathname(const char **cpp, char **path)
319{
Damien Millerd7686fd2001-02-10 00:40:03 +1100320 const char *cp = *cpp, *end;
321 char quot;
Damien Miller33804262001-02-04 23:20:18 +1100322 int i;
323
324 cp += strspn(cp, WHITESPACE);
325 if (!*cp) {
326 *cpp = cp;
327 *path = NULL;
Damien Millerd7686fd2001-02-10 00:40:03 +1100328 return (0);
Damien Miller33804262001-02-04 23:20:18 +1100329 }
330
331 /* Check for quoted filenames */
332 if (*cp == '\"' || *cp == '\'') {
Damien Millerd7686fd2001-02-10 00:40:03 +1100333 quot = *cp++;
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000334
Damien Millerd7686fd2001-02-10 00:40:03 +1100335 end = strchr(cp, quot);
336 if (end == NULL) {
Damien Miller33804262001-02-04 23:20:18 +1100337 error("Unterminated quote");
Damien Millerd7686fd2001-02-10 00:40:03 +1100338 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100339 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100340 if (cp == end) {
Damien Miller33804262001-02-04 23:20:18 +1100341 error("Empty quotes");
Damien Millerd7686fd2001-02-10 00:40:03 +1100342 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100343 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100344 *cpp = end + 1 + strspn(end + 1, WHITESPACE);
345 } else {
346 /* Read to end of filename */
347 end = strpbrk(cp, WHITESPACE);
348 if (end == NULL)
349 end = strchr(cp, '\0');
350 *cpp = end + strspn(end, WHITESPACE);
Damien Miller33804262001-02-04 23:20:18 +1100351 }
352
Damien Millerd7686fd2001-02-10 00:40:03 +1100353 i = end - cp;
Damien Miller33804262001-02-04 23:20:18 +1100354
355 *path = xmalloc(i + 1);
356 memcpy(*path, cp, i);
357 (*path)[i] = '\0';
Damien Miller33804262001-02-04 23:20:18 +1100358 return(0);
Damien Millerd7686fd2001-02-10 00:40:03 +1100359
360 fail:
361 *path = NULL;
362 return (-1);
Damien Miller33804262001-02-04 23:20:18 +1100363}
364
Ben Lindstrombba81212001-06-25 05:01:22 +0000365static int
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000366is_dir(char *path)
Damien Miller33804262001-02-04 23:20:18 +1100367{
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000368 struct stat sb;
Damien Miller33804262001-02-04 23:20:18 +1100369
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000370 /* XXX: report errors? */
371 if (stat(path, &sb) == -1)
Damien Miller33804262001-02-04 23:20:18 +1100372 return(0);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000373
374 return(sb.st_mode & S_IFDIR);
375}
376
Ben Lindstrombba81212001-06-25 05:01:22 +0000377static int
Damien Miller3db5f532002-02-13 14:10:32 +1100378remote_is_dir(struct sftp_conn *conn, char *path)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000379{
380 Attrib *a;
381
382 /* XXX: report errors? */
Damien Miller3db5f532002-02-13 14:10:32 +1100383 if ((a = do_stat(conn, path, 1)) == NULL)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000384 return(0);
385 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
386 return(0);
387 return(a->perm & S_IFDIR);
388}
389
Ben Lindstrombba81212001-06-25 05:01:22 +0000390static int
Damien Miller3db5f532002-02-13 14:10:32 +1100391process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000392{
393 char *abs_src = NULL;
394 char *abs_dst = NULL;
395 char *tmp;
396 glob_t g;
397 int err = 0;
398 int i;
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000399
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000400 abs_src = xstrdup(src);
401 abs_src = make_absolute(abs_src, pwd);
402
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000403 memset(&g, 0, sizeof(g));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000404 debug3("Looking up %s", abs_src);
Damien Miller3db5f532002-02-13 14:10:32 +1100405 if (remote_glob(conn, abs_src, 0, NULL, &g)) {
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000406 error("File \"%s\" not found.", abs_src);
407 err = -1;
408 goto out;
Damien Miller33804262001-02-04 23:20:18 +1100409 }
410
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000411 /* Only one match, dst may be file, directory or unspecified */
412 if (g.gl_pathv[0] && g.gl_matchc == 1) {
413 if (dst) {
414 /* If directory specified, append filename */
415 if (is_dir(dst)) {
416 if (infer_path(g.gl_pathv[0], &tmp)) {
417 err = 1;
418 goto out;
419 }
420 abs_dst = path_append(dst, tmp);
421 xfree(tmp);
422 } else
423 abs_dst = xstrdup(dst);
424 } else if (infer_path(g.gl_pathv[0], &abs_dst)) {
425 err = -1;
426 goto out;
427 }
428 printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst);
Damien Miller3db5f532002-02-13 14:10:32 +1100429 err = do_download(conn, g.gl_pathv[0], abs_dst, pflag);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000430 goto out;
Damien Miller33804262001-02-04 23:20:18 +1100431 }
432
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000433 /* Multiple matches, dst may be directory or unspecified */
434 if (dst && !is_dir(dst)) {
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000435 error("Multiple files match, but \"%s\" is not a directory",
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000436 dst);
437 err = -1;
438 goto out;
439 }
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000440
Damien Miller9f0f5c62001-12-21 14:45:46 +1100441 for (i = 0; g.gl_pathv[i]; i++) {
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000442 if (infer_path(g.gl_pathv[i], &tmp)) {
443 err = -1;
444 goto out;
445 }
446 if (dst) {
447 abs_dst = path_append(dst, tmp);
448 xfree(tmp);
449 } else
450 abs_dst = tmp;
451
452 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
Damien Miller3db5f532002-02-13 14:10:32 +1100453 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000454 err = -1;
455 xfree(abs_dst);
456 abs_dst = NULL;
457 }
458
459out:
460 xfree(abs_src);
461 if (abs_dst)
462 xfree(abs_dst);
463 globfree(&g);
464 return(err);
465}
466
Ben Lindstrombba81212001-06-25 05:01:22 +0000467static int
Damien Miller3db5f532002-02-13 14:10:32 +1100468process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000469{
470 char *tmp_dst = NULL;
471 char *abs_dst = NULL;
472 char *tmp;
473 glob_t g;
474 int err = 0;
475 int i;
476
477 if (dst) {
478 tmp_dst = xstrdup(dst);
479 tmp_dst = make_absolute(tmp_dst, pwd);
480 }
481
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000482 memset(&g, 0, sizeof(g));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000483 debug3("Looking up %s", src);
484 if (glob(src, 0, NULL, &g)) {
485 error("File \"%s\" not found.", src);
486 err = -1;
487 goto out;
488 }
489
490 /* Only one match, dst may be file, directory or unspecified */
491 if (g.gl_pathv[0] && g.gl_matchc == 1) {
492 if (tmp_dst) {
493 /* If directory specified, append filename */
Damien Miller3db5f532002-02-13 14:10:32 +1100494 if (remote_is_dir(conn, tmp_dst)) {
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000495 if (infer_path(g.gl_pathv[0], &tmp)) {
496 err = 1;
497 goto out;
498 }
499 abs_dst = path_append(tmp_dst, tmp);
500 xfree(tmp);
501 } else
502 abs_dst = xstrdup(tmp_dst);
Ben Lindstrom7527f8b2001-03-24 00:39:12 +0000503 } else {
504 if (infer_path(g.gl_pathv[0], &abs_dst)) {
505 err = -1;
506 goto out;
507 }
508 abs_dst = make_absolute(abs_dst, pwd);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000509 }
510 printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst);
Damien Miller3db5f532002-02-13 14:10:32 +1100511 err = do_upload(conn, g.gl_pathv[0], abs_dst, pflag);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000512 goto out;
513 }
514
515 /* Multiple matches, dst may be directory or unspecified */
Damien Miller3db5f532002-02-13 14:10:32 +1100516 if (tmp_dst && !remote_is_dir(conn, tmp_dst)) {
Ben Lindstrom5df2ffa2001-03-17 00:36:17 +0000517 error("Multiple files match, but \"%s\" is not a directory",
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000518 tmp_dst);
519 err = -1;
520 goto out;
521 }
522
Damien Miller9f0f5c62001-12-21 14:45:46 +1100523 for (i = 0; g.gl_pathv[i]; i++) {
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000524 if (infer_path(g.gl_pathv[i], &tmp)) {
525 err = -1;
526 goto out;
527 }
528 if (tmp_dst) {
529 abs_dst = path_append(tmp_dst, tmp);
530 xfree(tmp);
531 } else
532 abs_dst = make_absolute(tmp, pwd);
533
534 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
Damien Miller3db5f532002-02-13 14:10:32 +1100535 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000536 err = -1;
537 }
538
539out:
540 if (abs_dst)
541 xfree(abs_dst);
542 if (tmp_dst)
543 xfree(tmp_dst);
544 return(err);
Damien Miller33804262001-02-04 23:20:18 +1100545}
546
Ben Lindstrombba81212001-06-25 05:01:22 +0000547static int
Damien Millere1a49812002-09-12 09:54:25 +1000548sdirent_comp(const void *aa, const void *bb)
549{
550 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
551 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
552
553 return (strcmp(a->filename, b->filename));
554}
555
556/* sftp ls.1 replacement for directories */
557static int
558do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
559{
560 int n;
561 SFTP_DIRENT **d;
562
563 if ((n = do_readdir(conn, path, &d)) != 0)
564 return (n);
565
566 /* Count entries for sort */
567 for (n = 0; d[n] != NULL; n++)
568 ;
569
570 qsort(d, n, sizeof(*d), sdirent_comp);
571
572 for (n = 0; d[n] != NULL; n++) {
573 char *tmp, *fname;
574
575 tmp = path_append(path, d[n]->filename);
576 fname = path_strip(tmp, strip_path);
577 xfree(tmp);
578
579 if (lflag) {
580 char *lname;
581 struct stat sb;
582
583 memset(&sb, 0, sizeof(sb));
584 attrib_to_stat(&d[n]->a, &sb);
585 lname = ls_file(fname, &sb, 1);
586 printf("%s\n", lname);
587 xfree(lname);
588 } else {
589 /* XXX - multicolumn display would be nice here */
590 printf("%s\n", fname);
591 }
592
593 xfree(fname);
594 }
595
596 free_sftp_dirents(d);
597 return (0);
598}
599
600/* sftp ls.1 replacement which handles path globs */
601static int
602do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
603 int lflag)
604{
605 glob_t g;
606 int i;
607 Attrib *a;
608 struct stat sb;
609
610 memset(&g, 0, sizeof(g));
611
612 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
613 NULL, &g)) {
614 error("Can't ls: \"%s\" not found", path);
615 return (-1);
616 }
617
618 /*
619 * If the glob returns a single match, which is the same as the
620 * input glob, and it is a directory, then just list its contents
621 */
622 if (g.gl_pathc == 1 &&
623 strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
624 if ((a = do_lstat(conn, path, 1)) == NULL) {
625 globfree(&g);
626 return (-1);
627 }
628 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
629 S_ISDIR(a->perm)) {
630 globfree(&g);
631 return (do_ls_dir(conn, path, strip_path, lflag));
632 }
633 }
634
635 for (i = 0; g.gl_pathv[i]; i++) {
636 char *fname, *lname;
637
638 fname = path_strip(g.gl_pathv[i], strip_path);
639
640 if (lflag) {
641 /*
642 * XXX: this is slow - 1 roundtrip per path
643 * A solution to this is to fork glob() and
644 * build a sftp specific version which keeps the
645 * attribs (which currently get thrown away)
646 * that the server returns as well as the filenames.
647 */
648 memset(&sb, 0, sizeof(sb));
649 a = do_lstat(conn, g.gl_pathv[i], 1);
650 if (a != NULL)
651 attrib_to_stat(a, &sb);
652 lname = ls_file(fname, &sb, 1);
653 printf("%s\n", lname);
654 xfree(lname);
655 } else {
656 /* XXX - multicolumn display would be nice here */
657 printf("%s\n", fname);
658 }
659 xfree(fname);
660 }
661
662 if (g.gl_pathc)
663 globfree(&g);
664
665 return (0);
666}
667
668static int
669parse_args(const char **cpp, int *pflag, int *lflag,
670 unsigned long *n_arg, char **path1, char **path2)
Damien Miller33804262001-02-04 23:20:18 +1100671{
672 const char *cmd, *cp = *cpp;
Ben Lindstrom66904942001-02-15 03:19:56 +0000673 char *cp2;
Kevin Steves62c45db2001-02-05 13:42:43 +0000674 int base = 0;
Ben Lindstrom66904942001-02-15 03:19:56 +0000675 long l;
Damien Miller33804262001-02-04 23:20:18 +1100676 int i, cmdnum;
677
678 /* Skip leading whitespace */
679 cp = cp + strspn(cp, WHITESPACE);
680
681 /* Ignore blank lines */
682 if (!*cp)
683 return(-1);
684
685 /* Figure out which command we have */
Damien Miller9f0f5c62001-12-21 14:45:46 +1100686 for (i = 0; cmds[i].c; i++) {
Damien Miller33804262001-02-04 23:20:18 +1100687 int cmdlen = strlen(cmds[i].c);
688
689 /* Check for command followed by whitespace */
690 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
691 strchr(WHITESPACE, cp[cmdlen])) {
692 cp += cmdlen;
693 cp = cp + strspn(cp, WHITESPACE);
694 break;
695 }
696 }
697 cmdnum = cmds[i].n;
698 cmd = cmds[i].c;
699
700 /* Special case */
701 if (*cp == '!') {
702 cp++;
703 cmdnum = I_SHELL;
704 } else if (cmdnum == -1) {
705 error("Invalid command.");
706 return(-1);
707 }
708
709 /* Get arguments and parse flags */
Damien Millere1a49812002-09-12 09:54:25 +1000710 *lflag = *pflag = *n_arg = 0;
Damien Miller33804262001-02-04 23:20:18 +1100711 *path1 = *path2 = NULL;
712 switch (cmdnum) {
713 case I_GET:
714 case I_PUT:
715 if (parse_getput_flags(&cp, pflag))
716 return(-1);
717 /* Get first pathname (mandatory) */
718 if (get_pathname(&cp, path1))
719 return(-1);
720 if (*path1 == NULL) {
721 error("You must specify at least one path after a "
722 "%s command.", cmd);
723 return(-1);
724 }
725 /* Try to get second pathname (optional) */
726 if (get_pathname(&cp, path2))
727 return(-1);
Damien Miller33804262001-02-04 23:20:18 +1100728 break;
729 case I_RENAME:
Damien Miller058316f2001-03-08 10:08:49 +1100730 case I_SYMLINK:
Damien Miller33804262001-02-04 23:20:18 +1100731 if (get_pathname(&cp, path1))
732 return(-1);
733 if (get_pathname(&cp, path2))
734 return(-1);
735 if (!*path1 || !*path2) {
736 error("You must specify two paths after a %s "
737 "command.", cmd);
738 return(-1);
739 }
740 break;
741 case I_RM:
742 case I_MKDIR:
743 case I_RMDIR:
744 case I_CHDIR:
745 case I_LCHDIR:
746 case I_LMKDIR:
747 /* Get pathname (mandatory) */
748 if (get_pathname(&cp, path1))
749 return(-1);
750 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000751 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100752 cmd);
753 return(-1);
754 }
755 break;
756 case I_LS:
Damien Millere1a49812002-09-12 09:54:25 +1000757 if (parse_ls_flags(&cp, lflag))
758 return(-1);
Damien Miller33804262001-02-04 23:20:18 +1100759 /* Path is optional */
760 if (get_pathname(&cp, path1))
761 return(-1);
762 break;
763 case I_LLS:
764 case I_SHELL:
765 /* Uses the rest of the line */
766 break;
767 case I_LUMASK:
Ben Lindstrom66904942001-02-15 03:19:56 +0000768 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100769 case I_CHMOD:
Kevin Steves62c45db2001-02-05 13:42:43 +0000770 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100771 case I_CHOWN:
772 case I_CHGRP:
773 /* Get numeric arg (mandatory) */
Ben Lindstrom66904942001-02-15 03:19:56 +0000774 l = strtol(cp, &cp2, base);
775 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
776 errno == ERANGE) || l < 0) {
Damien Miller33804262001-02-04 23:20:18 +1100777 error("You must supply a numeric argument "
778 "to the %s command.", cmd);
779 return(-1);
780 }
Ben Lindstrom66904942001-02-15 03:19:56 +0000781 cp = cp2;
782 *n_arg = l;
783 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
784 break;
785 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
Damien Miller33804262001-02-04 23:20:18 +1100786 error("You must supply a numeric argument "
787 "to the %s command.", cmd);
788 return(-1);
789 }
790 cp += strspn(cp, WHITESPACE);
791
792 /* Get pathname (mandatory) */
793 if (get_pathname(&cp, path1))
794 return(-1);
795 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000796 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100797 cmd);
798 return(-1);
799 }
800 break;
801 case I_QUIT:
802 case I_PWD:
803 case I_LPWD:
804 case I_HELP:
Ben Lindstromf78682d2001-03-14 21:26:27 +0000805 case I_VERSION:
Damien Miller33804262001-02-04 23:20:18 +1100806 break;
807 default:
808 fatal("Command not implemented");
809 }
810
811 *cpp = cp;
Damien Miller33804262001-02-04 23:20:18 +1100812 return(cmdnum);
813}
814
Ben Lindstrombba81212001-06-25 05:01:22 +0000815static int
Damien Miller3db5f532002-02-13 14:10:32 +1100816parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd)
Damien Miller33804262001-02-04 23:20:18 +1100817{
Damien Millerd7686fd2001-02-10 00:40:03 +1100818 char *path1, *path2, *tmp;
Damien Millere1a49812002-09-12 09:54:25 +1000819 int pflag, lflag, cmdnum, i;
Damien Miller33804262001-02-04 23:20:18 +1100820 unsigned long n_arg;
821 Attrib a, *aa;
Ben Lindstrom0a7e3542001-02-15 03:50:49 +0000822 char path_buf[MAXPATHLEN];
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000823 int err = 0;
Damien Miller4870afd2001-03-14 10:27:09 +1100824 glob_t g;
Damien Miller33804262001-02-04 23:20:18 +1100825
826 path1 = path2 = NULL;
Damien Millere1a49812002-09-12 09:54:25 +1000827 cmdnum = parse_args(&cmd, &pflag, &lflag, &n_arg,
828 &path1, &path2);
Damien Miller33804262001-02-04 23:20:18 +1100829
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000830 memset(&g, 0, sizeof(g));
831
Damien Miller33804262001-02-04 23:20:18 +1100832 /* Perform command */
833 switch (cmdnum) {
834 case -1:
835 break;
836 case I_GET:
Damien Miller3db5f532002-02-13 14:10:32 +1100837 err = process_get(conn, path1, path2, *pwd, pflag);
Damien Miller33804262001-02-04 23:20:18 +1100838 break;
839 case I_PUT:
Damien Miller3db5f532002-02-13 14:10:32 +1100840 err = process_put(conn, path1, path2, *pwd, pflag);
Ben Lindstroma3700052001-04-05 23:26:32 +0000841 break;
842 case I_RENAME:
Damien Miller33804262001-02-04 23:20:18 +1100843 path1 = make_absolute(path1, *pwd);
844 path2 = make_absolute(path2, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100845 err = do_rename(conn, path1, path2);
Damien Miller33804262001-02-04 23:20:18 +1100846 break;
Damien Miller058316f2001-03-08 10:08:49 +1100847 case I_SYMLINK:
Damien Miller3db5f532002-02-13 14:10:32 +1100848 path2 = make_absolute(path2, *pwd);
849 err = do_symlink(conn, path1, path2);
Damien Miller058316f2001-03-08 10:08:49 +1100850 break;
Damien Miller33804262001-02-04 23:20:18 +1100851 case I_RM:
852 path1 = make_absolute(path1, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100853 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100854 for (i = 0; g.gl_pathv[i]; i++) {
Damien Miller4870afd2001-03-14 10:27:09 +1100855 printf("Removing %s\n", g.gl_pathv[i]);
Damien Miller3db5f532002-02-13 14:10:32 +1100856 if (do_rm(conn, g.gl_pathv[i]) == -1)
Damien Miller4870afd2001-03-14 10:27:09 +1100857 err = -1;
858 }
Damien Miller33804262001-02-04 23:20:18 +1100859 break;
860 case I_MKDIR:
861 path1 = make_absolute(path1, *pwd);
862 attrib_clear(&a);
863 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
864 a.perm = 0777;
Damien Miller3db5f532002-02-13 14:10:32 +1100865 err = do_mkdir(conn, path1, &a);
Damien Miller33804262001-02-04 23:20:18 +1100866 break;
867 case I_RMDIR:
868 path1 = make_absolute(path1, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100869 err = do_rmdir(conn, path1);
Damien Miller33804262001-02-04 23:20:18 +1100870 break;
871 case I_CHDIR:
872 path1 = make_absolute(path1, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100873 if ((tmp = do_realpath(conn, path1)) == NULL) {
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000874 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100875 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000876 }
Damien Miller3db5f532002-02-13 14:10:32 +1100877 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100878 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000879 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100880 break;
881 }
882 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
883 error("Can't change directory: Can't check target");
884 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000885 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100886 break;
887 }
888 if (!S_ISDIR(aa->perm)) {
889 error("Can't change directory: \"%s\" is not "
890 "a directory", tmp);
891 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000892 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100893 break;
894 }
Damien Miller33804262001-02-04 23:20:18 +1100895 xfree(*pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100896 *pwd = tmp;
Damien Miller33804262001-02-04 23:20:18 +1100897 break;
898 case I_LS:
Damien Millerd7686fd2001-02-10 00:40:03 +1100899 if (!path1) {
Damien Millere1a49812002-09-12 09:54:25 +1000900 do_globbed_ls(conn, *pwd, *pwd, lflag);
Damien Millerd7686fd2001-02-10 00:40:03 +1100901 break;
902 }
Damien Millere1a49812002-09-12 09:54:25 +1000903
904 /* Strip pwd off beginning of non-absolute paths */
905 tmp = NULL;
906 if (*path1 != '/')
907 tmp = *pwd;
908
Damien Miller33804262001-02-04 23:20:18 +1100909 path1 = make_absolute(path1, *pwd);
Damien Millere1a49812002-09-12 09:54:25 +1000910
911 do_globbed_ls(conn, path1, tmp, lflag);
Damien Miller33804262001-02-04 23:20:18 +1100912 break;
913 case I_LCHDIR:
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000914 if (chdir(path1) == -1) {
Damien Miller33804262001-02-04 23:20:18 +1100915 error("Couldn't change local directory to "
916 "\"%s\": %s", path1, strerror(errno));
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000917 err = 1;
918 }
Damien Miller33804262001-02-04 23:20:18 +1100919 break;
920 case I_LMKDIR:
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000921 if (mkdir(path1, 0777) == -1) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100922 error("Couldn't create local directory "
Damien Miller33804262001-02-04 23:20:18 +1100923 "\"%s\": %s", path1, strerror(errno));
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000924 err = 1;
925 }
Damien Miller33804262001-02-04 23:20:18 +1100926 break;
927 case I_LLS:
928 local_do_ls(cmd);
929 break;
930 case I_SHELL:
931 local_do_shell(cmd);
932 break;
933 case I_LUMASK:
934 umask(n_arg);
Ben Lindstrom66904942001-02-15 03:19:56 +0000935 printf("Local umask: %03lo\n", n_arg);
Damien Miller33804262001-02-04 23:20:18 +1100936 break;
937 case I_CHMOD:
938 path1 = make_absolute(path1, *pwd);
939 attrib_clear(&a);
940 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
941 a.perm = n_arg;
Damien Miller3db5f532002-02-13 14:10:32 +1100942 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100943 for (i = 0; g.gl_pathv[i]; i++) {
Damien Miller4870afd2001-03-14 10:27:09 +1100944 printf("Changing mode on %s\n", g.gl_pathv[i]);
Damien Miller3db5f532002-02-13 14:10:32 +1100945 do_setstat(conn, g.gl_pathv[i], &a);
Damien Miller4870afd2001-03-14 10:27:09 +1100946 }
Kevin Steves62c45db2001-02-05 13:42:43 +0000947 break;
Damien Miller33804262001-02-04 23:20:18 +1100948 case I_CHOWN:
949 path1 = make_absolute(path1, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100950 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100951 for (i = 0; g.gl_pathv[i]; i++) {
Damien Miller3db5f532002-02-13 14:10:32 +1100952 if (!(aa = do_stat(conn, g.gl_pathv[i], 0)))
Damien Miller4870afd2001-03-14 10:27:09 +1100953 continue;
954 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
955 error("Can't get current ownership of "
956 "remote file \"%s\"", g.gl_pathv[i]);
957 continue;
958 }
959 printf("Changing owner on %s\n", g.gl_pathv[i]);
960 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
961 aa->uid = n_arg;
Damien Miller3db5f532002-02-13 14:10:32 +1100962 do_setstat(conn, g.gl_pathv[i], aa);
Damien Miller33804262001-02-04 23:20:18 +1100963 }
Damien Miller33804262001-02-04 23:20:18 +1100964 break;
965 case I_CHGRP:
966 path1 = make_absolute(path1, *pwd);
Damien Miller3db5f532002-02-13 14:10:32 +1100967 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100968 for (i = 0; g.gl_pathv[i]; i++) {
Damien Miller3db5f532002-02-13 14:10:32 +1100969 if (!(aa = do_stat(conn, g.gl_pathv[i], 0)))
Damien Miller4870afd2001-03-14 10:27:09 +1100970 continue;
971 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
972 error("Can't get current ownership of "
973 "remote file \"%s\"", g.gl_pathv[i]);
974 continue;
975 }
976 printf("Changing group on %s\n", g.gl_pathv[i]);
977 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
978 aa->gid = n_arg;
Damien Miller3db5f532002-02-13 14:10:32 +1100979 do_setstat(conn, g.gl_pathv[i], aa);
Damien Miller33804262001-02-04 23:20:18 +1100980 }
Damien Miller33804262001-02-04 23:20:18 +1100981 break;
982 case I_PWD:
983 printf("Remote working directory: %s\n", *pwd);
984 break;
985 case I_LPWD:
986 if (!getcwd(path_buf, sizeof(path_buf)))
Ben Lindstrom6df8ef42001-03-05 07:47:23 +0000987 error("Couldn't get local cwd: %s",
Damien Miller33804262001-02-04 23:20:18 +1100988 strerror(errno));
989 else
990 printf("Local working directory: %s\n",
991 path_buf);
992 break;
993 case I_QUIT:
994 return(-1);
995 case I_HELP:
996 help();
997 break;
Ben Lindstromf78682d2001-03-14 21:26:27 +0000998 case I_VERSION:
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000999 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
Ben Lindstromf78682d2001-03-14 21:26:27 +00001000 break;
Damien Miller33804262001-02-04 23:20:18 +11001001 default:
1002 fatal("%d is not implemented", cmdnum);
1003 }
1004
Ben Lindstromc8d1c302001-03-17 00:34:46 +00001005 if (g.gl_pathc)
1006 globfree(&g);
Damien Miller33804262001-02-04 23:20:18 +11001007 if (path1)
1008 xfree(path1);
1009 if (path2)
1010 xfree(path2);
Ben Lindstrom562c26b2001-03-07 01:26:48 +00001011
1012 /* If an error occurs in batch mode we should abort. */
1013 if (infile != stdin && err > 0)
1014 return -1;
1015
Damien Miller33804262001-02-04 23:20:18 +11001016 return(0);
1017}
1018
1019void
Ben Lindstrom63667f62001-04-13 00:00:14 +00001020interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
Damien Miller33804262001-02-04 23:20:18 +11001021{
1022 char *pwd;
Ben Lindstrom63667f62001-04-13 00:00:14 +00001023 char *dir = NULL;
Damien Miller33804262001-02-04 23:20:18 +11001024 char cmd[2048];
Damien Miller3db5f532002-02-13 14:10:32 +11001025 struct sftp_conn *conn;
Damien Miller33804262001-02-04 23:20:18 +11001026
Damien Miller3db5f532002-02-13 14:10:32 +11001027 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1028 if (conn == NULL)
Damien Miller058316f2001-03-08 10:08:49 +11001029 fatal("Couldn't initialise connection to server");
1030
Damien Miller3db5f532002-02-13 14:10:32 +11001031 pwd = do_realpath(conn, ".");
Damien Miller33804262001-02-04 23:20:18 +11001032 if (pwd == NULL)
1033 fatal("Need cwd");
1034
Ben Lindstrom63667f62001-04-13 00:00:14 +00001035 if (file1 != NULL) {
1036 dir = xstrdup(file1);
1037 dir = make_absolute(dir, pwd);
1038
Damien Miller3db5f532002-02-13 14:10:32 +11001039 if (remote_is_dir(conn, dir) && file2 == NULL) {
Ben Lindstrom63667f62001-04-13 00:00:14 +00001040 printf("Changing to: %s\n", dir);
1041 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
Damien Miller3db5f532002-02-13 14:10:32 +11001042 parse_dispatch_command(conn, cmd, &pwd);
Ben Lindstrom63667f62001-04-13 00:00:14 +00001043 } else {
1044 if (file2 == NULL)
1045 snprintf(cmd, sizeof cmd, "get %s", dir);
1046 else
1047 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1048 file2);
1049
Damien Miller3db5f532002-02-13 14:10:32 +11001050 parse_dispatch_command(conn, cmd, &pwd);
Ben Lindstromcb1f60e2002-03-22 02:47:28 +00001051 xfree(dir);
Ben Lindstrom63667f62001-04-13 00:00:14 +00001052 return;
1053 }
Ben Lindstromcb1f60e2002-03-22 02:47:28 +00001054 xfree(dir);
Ben Lindstrom63667f62001-04-13 00:00:14 +00001055 }
Ben Lindstrom6aebb342001-05-09 00:38:19 +00001056#if HAVE_SETVBUF
Damien Millerd7686fd2001-02-10 00:40:03 +11001057 setvbuf(stdout, NULL, _IOLBF, 0);
Ben Lindstrom562c26b2001-03-07 01:26:48 +00001058 setvbuf(infile, NULL, _IOLBF, 0);
Ben Lindstrom6aebb342001-05-09 00:38:19 +00001059#else
1060 setlinebuf(stdout);
1061 setlinebuf(infile);
1062#endif
Damien Miller33804262001-02-04 23:20:18 +11001063
Damien Miller9f0f5c62001-12-21 14:45:46 +11001064 for (;;) {
Damien Miller33804262001-02-04 23:20:18 +11001065 char *cp;
1066
1067 printf("sftp> ");
1068
1069 /* XXX: use libedit */
Ben Lindstrom562c26b2001-03-07 01:26:48 +00001070 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
Damien Miller33804262001-02-04 23:20:18 +11001071 printf("\n");
1072 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +00001073 } else if (infile != stdin) /* Bluff typing */
1074 printf("%s", cmd);
1075
Damien Miller33804262001-02-04 23:20:18 +11001076 cp = strrchr(cmd, '\n');
1077 if (cp)
1078 *cp = '\0';
Ben Lindstrom562c26b2001-03-07 01:26:48 +00001079
Damien Miller3db5f532002-02-13 14:10:32 +11001080 if (parse_dispatch_command(conn, cmd, &pwd))
Damien Miller33804262001-02-04 23:20:18 +11001081 break;
1082 }
1083 xfree(pwd);
1084}