blob: 6f5b3677a3d50c7f584d8e05b7229ac3a371bde9 [file] [log] [blame]
Damien Miller33804262001-02-04 23:20:18 +11001/*
2 * Copyright (c) 2001 Damien Miller. All rights reserved.
3 *
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
25/* XXX: finish implementation of all commands */
26/* XXX: do fnmatch() instead of using raw pathname */
Damien Millerd7686fd2001-02-10 00:40:03 +110027/* XXX: globbed ls */
Damien Miller33804262001-02-04 23:20:18 +110028/* XXX: recursive operations */
29
30#include "includes.h"
Damien Miller058316f2001-03-08 10:08:49 +110031RCSID("$OpenBSD: sftp-int.c,v 1.26 2001/03/07 10:11:23 djm Exp $");
Damien Miller33804262001-02-04 23:20:18 +110032
33#include "buffer.h"
34#include "xmalloc.h"
35#include "log.h"
36#include "pathnames.h"
37
38#include "sftp.h"
39#include "sftp-common.h"
40#include "sftp-client.h"
41#include "sftp-int.h"
42
Damien Miller058316f2001-03-08 10:08:49 +110043/* File to read commands from */
44extern FILE *infile;
45
46/* Version of server we are speaking to */
47int version;
Ben Lindstrom562c26b2001-03-07 01:26:48 +000048
Damien Miller33804262001-02-04 23:20:18 +110049/* Seperators for interactive commands */
50#define WHITESPACE " \t\r\n"
51
52/* Commands for interactive mode */
53#define I_CHDIR 1
54#define I_CHGRP 2
55#define I_CHMOD 3
56#define I_CHOWN 4
57#define I_GET 5
58#define I_HELP 6
59#define I_LCHDIR 7
60#define I_LLS 8
61#define I_LMKDIR 9
62#define I_LPWD 10
63#define I_LS 11
64#define I_LUMASK 12
65#define I_MKDIR 13
66#define I_PUT 14
67#define I_PWD 15
68#define I_QUIT 16
69#define I_RENAME 17
70#define I_RM 18
71#define I_RMDIR 19
72#define I_SHELL 20
Damien Miller058316f2001-03-08 10:08:49 +110073#define I_SYMLINK 21
Damien Miller33804262001-02-04 23:20:18 +110074
75struct CMD {
Damien Miller33804262001-02-04 23:20:18 +110076 const char *c;
Kevin Steves62c45db2001-02-05 13:42:43 +000077 const int n;
Damien Miller33804262001-02-04 23:20:18 +110078};
79
80const struct CMD cmds[] = {
Damien Millerd7686fd2001-02-10 00:40:03 +110081 { "cd", I_CHDIR },
82 { "chdir", I_CHDIR },
83 { "chgrp", I_CHGRP },
84 { "chmod", I_CHMOD },
85 { "chown", I_CHOWN },
86 { "dir", I_LS },
87 { "exit", I_QUIT },
88 { "get", I_GET },
89 { "help", I_HELP },
90 { "lcd", I_LCHDIR },
91 { "lchdir", I_LCHDIR },
92 { "lls", I_LLS },
93 { "lmkdir", I_LMKDIR },
Damien Miller058316f2001-03-08 10:08:49 +110094 { "ln", I_SYMLINK },
Damien Millerd7686fd2001-02-10 00:40:03 +110095 { "lpwd", I_LPWD },
96 { "ls", I_LS },
97 { "lumask", I_LUMASK },
98 { "mkdir", I_MKDIR },
99 { "put", I_PUT },
100 { "pwd", I_PWD },
101 { "quit", I_QUIT },
102 { "rename", I_RENAME },
103 { "rm", I_RM },
104 { "rmdir", I_RMDIR },
Damien Miller058316f2001-03-08 10:08:49 +1100105 { "symlink", I_SYMLINK },
Kevin Steves62c45db2001-02-05 13:42:43 +0000106 { "!", I_SHELL },
107 { "?", I_HELP },
108 { NULL, -1}
Damien Miller33804262001-02-04 23:20:18 +1100109};
110
111void
112help(void)
113{
114 printf("Available commands:\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100115 printf("cd path Change remote directory to 'path'\n");
116 printf("lcd path Change local directory to 'path'\n");
117 printf("chgrp grp path Change group of file 'path' to 'grp'\n");
118 printf("chmod mode path Change permissions of file 'path' to 'mode'\n");
119 printf("chown own path Change owner of file 'path' to 'own'\n");
120 printf("help Display this help text\n");
121 printf("get remote-path [local-path] Download file\n");
122 printf("lls [ls-options [path]] Display local directory listing\n");
Damien Miller058316f2001-03-08 10:08:49 +1100123 printf("ln oldpath newpath Symlink remote file\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100124 printf("lmkdir path Create local directory\n");
125 printf("lpwd Print local working directory\n");
126 printf("ls [path] Display remote directory listing\n");
127 printf("lumask umask Set local umask to 'umask'\n");
128 printf("mkdir path Create remote directory\n");
129 printf("put local-path [remote-path] Upload file\n");
130 printf("pwd Display remote working directory\n");
131 printf("exit Quit sftp\n");
132 printf("quit Quit sftp\n");
133 printf("rename oldpath newpath Rename remote file\n");
134 printf("rmdir path Remove remote directory\n");
135 printf("rm path Delete remote file\n");
Damien Miller058316f2001-03-08 10:08:49 +1100136 printf("symlink oldpath newpath Symlink remote file\n");
Damien Miller33804262001-02-04 23:20:18 +1100137 printf("!command Execute 'command' in local shell\n");
138 printf("! Escape to local shell\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100139 printf("? Synonym for help\n");
Damien Miller33804262001-02-04 23:20:18 +1100140}
141
142void
143local_do_shell(const char *args)
144{
145 int ret, status;
146 char *shell;
147 pid_t pid;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000148
Damien Miller33804262001-02-04 23:20:18 +1100149 if (!*args)
150 args = NULL;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000151
Damien Miller33804262001-02-04 23:20:18 +1100152 if ((shell = getenv("SHELL")) == NULL)
153 shell = _PATH_BSHELL;
154
155 if ((pid = fork()) == -1)
156 fatal("Couldn't fork: %s", strerror(errno));
157
158 if (pid == 0) {
159 /* XXX: child has pipe fds to ssh subproc open - issue? */
160 if (args) {
161 debug3("Executing %s -c \"%s\"", shell, args);
162 ret = execl(shell, shell, "-c", args, NULL);
163 } else {
164 debug3("Executing %s", shell);
165 ret = execl(shell, shell, NULL);
166 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000167 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
Damien Miller33804262001-02-04 23:20:18 +1100168 strerror(errno));
169 _exit(1);
170 }
171 if (waitpid(pid, &status, 0) == -1)
172 fatal("Couldn't wait for child: %s", strerror(errno));
173 if (!WIFEXITED(status))
174 error("Shell exited abormally");
175 else if (WEXITSTATUS(status))
176 error("Shell exited with status %d", WEXITSTATUS(status));
177}
178
Kevin Stevesef4eea92001-02-05 12:42:17 +0000179void
Damien Miller33804262001-02-04 23:20:18 +1100180local_do_ls(const char *args)
181{
182 if (!args || !*args)
Damien Millerd7686fd2001-02-10 00:40:03 +1100183 local_do_shell(_PATH_LS);
Damien Miller33804262001-02-04 23:20:18 +1100184 else {
Damien Millerd7686fd2001-02-10 00:40:03 +1100185 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
186 char *buf = xmalloc(len);
Damien Miller33804262001-02-04 23:20:18 +1100187
188 /* XXX: quoting - rip quoting code from ftp? */
Damien Millerd7686fd2001-02-10 00:40:03 +1100189 snprintf(buf, len, _PATH_LS " %s", args);
Damien Miller33804262001-02-04 23:20:18 +1100190 local_do_shell(buf);
Damien Millerd7686fd2001-02-10 00:40:03 +1100191 xfree(buf);
Damien Miller33804262001-02-04 23:20:18 +1100192 }
193}
194
195char *
196make_absolute(char *p, char *pwd)
197{
198 char buf[2048];
199
200 /* Derelativise */
201 if (p && p[0] != '/') {
202 snprintf(buf, sizeof(buf), "%s/%s", pwd, p);
203 xfree(p);
204 p = xstrdup(buf);
205 }
206
207 return(p);
208}
209
210int
211parse_getput_flags(const char **cpp, int *pflag)
212{
213 const char *cp = *cpp;
214
215 /* Check for flags */
216 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100217 switch (cp[1]) {
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000218 case 'p':
Damien Miller33804262001-02-04 23:20:18 +1100219 case 'P':
220 *pflag = 1;
221 break;
222 default:
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000223 error("Invalid flag -%c", cp[1]);
Damien Miller33804262001-02-04 23:20:18 +1100224 return(-1);
225 }
226 cp += 2;
227 *cpp = cp + strspn(cp, WHITESPACE);
228 }
229
230 return(0);
231}
232
233int
234get_pathname(const char **cpp, char **path)
235{
Damien Millerd7686fd2001-02-10 00:40:03 +1100236 const char *cp = *cpp, *end;
237 char quot;
Damien Miller33804262001-02-04 23:20:18 +1100238 int i;
239
240 cp += strspn(cp, WHITESPACE);
241 if (!*cp) {
242 *cpp = cp;
243 *path = NULL;
Damien Millerd7686fd2001-02-10 00:40:03 +1100244 return (0);
Damien Miller33804262001-02-04 23:20:18 +1100245 }
246
247 /* Check for quoted filenames */
248 if (*cp == '\"' || *cp == '\'') {
Damien Millerd7686fd2001-02-10 00:40:03 +1100249 quot = *cp++;
250
251 end = strchr(cp, quot);
252 if (end == NULL) {
Damien Miller33804262001-02-04 23:20:18 +1100253 error("Unterminated quote");
Damien Millerd7686fd2001-02-10 00:40:03 +1100254 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100255 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100256 if (cp == end) {
Damien Miller33804262001-02-04 23:20:18 +1100257 error("Empty quotes");
Damien Millerd7686fd2001-02-10 00:40:03 +1100258 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100259 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100260 *cpp = end + 1 + strspn(end + 1, WHITESPACE);
261 } else {
262 /* Read to end of filename */
263 end = strpbrk(cp, WHITESPACE);
264 if (end == NULL)
265 end = strchr(cp, '\0');
266 *cpp = end + strspn(end, WHITESPACE);
Damien Miller33804262001-02-04 23:20:18 +1100267 }
268
Damien Millerd7686fd2001-02-10 00:40:03 +1100269 i = end - cp;
Damien Miller33804262001-02-04 23:20:18 +1100270
271 *path = xmalloc(i + 1);
272 memcpy(*path, cp, i);
273 (*path)[i] = '\0';
Damien Miller33804262001-02-04 23:20:18 +1100274 return(0);
Damien Millerd7686fd2001-02-10 00:40:03 +1100275
276 fail:
277 *path = NULL;
278 return (-1);
Damien Miller33804262001-02-04 23:20:18 +1100279}
280
281int
282infer_path(const char *p, char **ifp)
283{
284 char *cp;
285
286 debug("XXX: P = \"%s\"", p);
287
288 cp = strrchr(p, '/');
Damien Miller33804262001-02-04 23:20:18 +1100289 if (cp == NULL) {
290 *ifp = xstrdup(p);
291 return(0);
292 }
293
294 if (!cp[1]) {
295 error("Invalid path");
296 return(-1);
297 }
298
299 *ifp = xstrdup(cp + 1);
300 return(0);
301}
302
303int
304parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
305 char **path1, char **path2)
306{
307 const char *cmd, *cp = *cpp;
Ben Lindstrom66904942001-02-15 03:19:56 +0000308 char *cp2;
Kevin Steves62c45db2001-02-05 13:42:43 +0000309 int base = 0;
Ben Lindstrom66904942001-02-15 03:19:56 +0000310 long l;
Damien Miller33804262001-02-04 23:20:18 +1100311 int i, cmdnum;
312
313 /* Skip leading whitespace */
314 cp = cp + strspn(cp, WHITESPACE);
315
316 /* Ignore blank lines */
317 if (!*cp)
318 return(-1);
319
320 /* Figure out which command we have */
321 for(i = 0; cmds[i].c; i++) {
322 int cmdlen = strlen(cmds[i].c);
323
324 /* Check for command followed by whitespace */
325 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
326 strchr(WHITESPACE, cp[cmdlen])) {
327 cp += cmdlen;
328 cp = cp + strspn(cp, WHITESPACE);
329 break;
330 }
331 }
332 cmdnum = cmds[i].n;
333 cmd = cmds[i].c;
334
335 /* Special case */
336 if (*cp == '!') {
337 cp++;
338 cmdnum = I_SHELL;
339 } else if (cmdnum == -1) {
340 error("Invalid command.");
341 return(-1);
342 }
343
344 /* Get arguments and parse flags */
345 *pflag = *n_arg = 0;
346 *path1 = *path2 = NULL;
347 switch (cmdnum) {
348 case I_GET:
349 case I_PUT:
350 if (parse_getput_flags(&cp, pflag))
351 return(-1);
352 /* Get first pathname (mandatory) */
353 if (get_pathname(&cp, path1))
354 return(-1);
355 if (*path1 == NULL) {
356 error("You must specify at least one path after a "
357 "%s command.", cmd);
358 return(-1);
359 }
360 /* Try to get second pathname (optional) */
361 if (get_pathname(&cp, path2))
362 return(-1);
363 /* Otherwise try to guess it from first path */
364 if (*path2 == NULL && infer_path(*path1, path2))
365 return(-1);
366 break;
367 case I_RENAME:
Damien Miller058316f2001-03-08 10:08:49 +1100368 case I_SYMLINK:
Damien Miller33804262001-02-04 23:20:18 +1100369 if (get_pathname(&cp, path1))
370 return(-1);
371 if (get_pathname(&cp, path2))
372 return(-1);
373 if (!*path1 || !*path2) {
374 error("You must specify two paths after a %s "
375 "command.", cmd);
376 return(-1);
377 }
378 break;
379 case I_RM:
380 case I_MKDIR:
381 case I_RMDIR:
382 case I_CHDIR:
383 case I_LCHDIR:
384 case I_LMKDIR:
385 /* Get pathname (mandatory) */
386 if (get_pathname(&cp, path1))
387 return(-1);
388 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000389 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100390 cmd);
391 return(-1);
392 }
393 break;
394 case I_LS:
395 /* Path is optional */
396 if (get_pathname(&cp, path1))
397 return(-1);
398 break;
399 case I_LLS:
400 case I_SHELL:
401 /* Uses the rest of the line */
402 break;
403 case I_LUMASK:
Ben Lindstrom66904942001-02-15 03:19:56 +0000404 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100405 case I_CHMOD:
Kevin Steves62c45db2001-02-05 13:42:43 +0000406 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100407 case I_CHOWN:
408 case I_CHGRP:
409 /* Get numeric arg (mandatory) */
Ben Lindstrom66904942001-02-15 03:19:56 +0000410 l = strtol(cp, &cp2, base);
411 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
412 errno == ERANGE) || l < 0) {
Damien Miller33804262001-02-04 23:20:18 +1100413 error("You must supply a numeric argument "
414 "to the %s command.", cmd);
415 return(-1);
416 }
Ben Lindstrom66904942001-02-15 03:19:56 +0000417 cp = cp2;
418 *n_arg = l;
419 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
420 break;
421 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
Damien Miller33804262001-02-04 23:20:18 +1100422 error("You must supply a numeric argument "
423 "to the %s command.", cmd);
424 return(-1);
425 }
426 cp += strspn(cp, WHITESPACE);
427
428 /* Get pathname (mandatory) */
429 if (get_pathname(&cp, path1))
430 return(-1);
431 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000432 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100433 cmd);
434 return(-1);
435 }
436 break;
437 case I_QUIT:
438 case I_PWD:
439 case I_LPWD:
440 case I_HELP:
441 break;
442 default:
443 fatal("Command not implemented");
444 }
445
446 *cpp = cp;
Damien Miller33804262001-02-04 23:20:18 +1100447 return(cmdnum);
448}
449
450int
451parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
452{
Damien Millerd7686fd2001-02-10 00:40:03 +1100453 char *path1, *path2, *tmp;
Damien Miller33804262001-02-04 23:20:18 +1100454 int pflag, cmdnum;
455 unsigned long n_arg;
456 Attrib a, *aa;
Ben Lindstrom0a7e3542001-02-15 03:50:49 +0000457 char path_buf[MAXPATHLEN];
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000458 int err = 0;
Damien Miller33804262001-02-04 23:20:18 +1100459
460 path1 = path2 = NULL;
461 cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
462
463 /* Perform command */
464 switch (cmdnum) {
465 case -1:
466 break;
467 case I_GET:
468 path1 = make_absolute(path1, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000469 err = do_download(in, out, path1, path2, pflag);
Damien Miller33804262001-02-04 23:20:18 +1100470 break;
471 case I_PUT:
472 path2 = make_absolute(path2, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000473 err = do_upload(in, out, path1, path2, pflag);
Damien Miller33804262001-02-04 23:20:18 +1100474 break;
475 case I_RENAME:
476 path1 = make_absolute(path1, *pwd);
477 path2 = make_absolute(path2, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000478 err = do_rename(in, out, path1, path2);
Damien Miller33804262001-02-04 23:20:18 +1100479 break;
Damien Miller058316f2001-03-08 10:08:49 +1100480 case I_SYMLINK:
481 if (version < 3) {
482 error("The server (version %d) does not support "
483 "this operation", version);
484 err = -1;
485 } else {
486 path2 = make_absolute(path2, *pwd);
487 err = do_symlink(in, out, path1, path2);
488 }
489 break;
Damien Miller33804262001-02-04 23:20:18 +1100490 case I_RM:
491 path1 = make_absolute(path1, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000492 err = do_rm(in, out, path1);
Damien Miller33804262001-02-04 23:20:18 +1100493 break;
494 case I_MKDIR:
495 path1 = make_absolute(path1, *pwd);
496 attrib_clear(&a);
497 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
498 a.perm = 0777;
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000499 err = do_mkdir(in, out, path1, &a);
Damien Miller33804262001-02-04 23:20:18 +1100500 break;
501 case I_RMDIR:
502 path1 = make_absolute(path1, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000503 err = do_rmdir(in, out, path1);
Damien Miller33804262001-02-04 23:20:18 +1100504 break;
505 case I_CHDIR:
506 path1 = make_absolute(path1, *pwd);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000507 if ((tmp = do_realpath(in, out, path1)) == NULL) {
508 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100509 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000510 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100511 if ((aa = do_stat(in, out, tmp)) == NULL) {
512 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000513 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100514 break;
515 }
516 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
517 error("Can't change directory: Can't check target");
518 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000519 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100520 break;
521 }
522 if (!S_ISDIR(aa->perm)) {
523 error("Can't change directory: \"%s\" is not "
524 "a directory", tmp);
525 xfree(tmp);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000526 err = 1;
Damien Millerd7686fd2001-02-10 00:40:03 +1100527 break;
528 }
Damien Miller33804262001-02-04 23:20:18 +1100529 xfree(*pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100530 *pwd = tmp;
Damien Miller33804262001-02-04 23:20:18 +1100531 break;
532 case I_LS:
Damien Millerd7686fd2001-02-10 00:40:03 +1100533 if (!path1) {
534 do_ls(in, out, *pwd);
535 break;
536 }
Damien Miller33804262001-02-04 23:20:18 +1100537 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100538 if ((tmp = do_realpath(in, out, path1)) == NULL)
539 break;
540 xfree(path1);
541 path1 = tmp;
542 if ((aa = do_stat(in, out, path1)) == NULL)
543 break;
544 if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
545 !S_ISDIR(aa->perm)) {
546 error("Can't ls: \"%s\" is not a directory", path1);
547 break;
548 }
549 do_ls(in, out, path1);
Damien Miller33804262001-02-04 23:20:18 +1100550 break;
551 case I_LCHDIR:
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000552 if (chdir(path1) == -1) {
Damien Miller33804262001-02-04 23:20:18 +1100553 error("Couldn't change local directory to "
554 "\"%s\": %s", path1, strerror(errno));
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000555 err = 1;
556 }
Damien Miller33804262001-02-04 23:20:18 +1100557 break;
558 case I_LMKDIR:
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000559 if (mkdir(path1, 0777) == -1) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100560 error("Couldn't create local directory "
Damien Miller33804262001-02-04 23:20:18 +1100561 "\"%s\": %s", path1, strerror(errno));
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000562 err = 1;
563 }
Damien Miller33804262001-02-04 23:20:18 +1100564 break;
565 case I_LLS:
566 local_do_ls(cmd);
567 break;
568 case I_SHELL:
569 local_do_shell(cmd);
570 break;
571 case I_LUMASK:
572 umask(n_arg);
Ben Lindstrom66904942001-02-15 03:19:56 +0000573 printf("Local umask: %03lo\n", n_arg);
Damien Miller33804262001-02-04 23:20:18 +1100574 break;
575 case I_CHMOD:
576 path1 = make_absolute(path1, *pwd);
577 attrib_clear(&a);
578 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
579 a.perm = n_arg;
580 do_setstat(in, out, path1, &a);
Kevin Steves62c45db2001-02-05 13:42:43 +0000581 break;
Damien Miller33804262001-02-04 23:20:18 +1100582 case I_CHOWN:
583 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100584 if (!(aa = do_stat(in, out, path1)))
585 break;
Kevin Steves62c45db2001-02-05 13:42:43 +0000586 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
Damien Miller33804262001-02-04 23:20:18 +1100587 error("Can't get current ownership of "
588 "remote file \"%s\"", path1);
589 break;
590 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100591 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
Damien Miller33804262001-02-04 23:20:18 +1100592 aa->uid = n_arg;
593 do_setstat(in, out, path1, aa);
594 break;
595 case I_CHGRP:
596 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100597 if (!(aa = do_stat(in, out, path1)))
598 break;
Kevin Steves62c45db2001-02-05 13:42:43 +0000599 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
Damien Miller33804262001-02-04 23:20:18 +1100600 error("Can't get current ownership of "
601 "remote file \"%s\"", path1);
602 break;
603 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100604 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
Damien Miller33804262001-02-04 23:20:18 +1100605 aa->gid = n_arg;
606 do_setstat(in, out, path1, aa);
607 break;
608 case I_PWD:
609 printf("Remote working directory: %s\n", *pwd);
610 break;
611 case I_LPWD:
612 if (!getcwd(path_buf, sizeof(path_buf)))
Ben Lindstrom6df8ef42001-03-05 07:47:23 +0000613 error("Couldn't get local cwd: %s",
Damien Miller33804262001-02-04 23:20:18 +1100614 strerror(errno));
615 else
616 printf("Local working directory: %s\n",
617 path_buf);
618 break;
619 case I_QUIT:
620 return(-1);
621 case I_HELP:
622 help();
623 break;
624 default:
625 fatal("%d is not implemented", cmdnum);
626 }
627
628 if (path1)
629 xfree(path1);
630 if (path2)
631 xfree(path2);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000632
633 /* If an error occurs in batch mode we should abort. */
634 if (infile != stdin && err > 0)
635 return -1;
636
Damien Miller33804262001-02-04 23:20:18 +1100637 return(0);
638}
639
640void
641interactive_loop(int fd_in, int fd_out)
642{
643 char *pwd;
644 char cmd[2048];
645
Damien Miller058316f2001-03-08 10:08:49 +1100646 version = do_init(fd_in, fd_out);
647 if (version == -1)
648 fatal("Couldn't initialise connection to server");
649
Damien Miller33804262001-02-04 23:20:18 +1100650 pwd = do_realpath(fd_in, fd_out, ".");
651 if (pwd == NULL)
652 fatal("Need cwd");
653
Damien Millerd7686fd2001-02-10 00:40:03 +1100654 setvbuf(stdout, NULL, _IOLBF, 0);
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000655 setvbuf(infile, NULL, _IOLBF, 0);
Damien Miller33804262001-02-04 23:20:18 +1100656
657 for(;;) {
658 char *cp;
659
660 printf("sftp> ");
661
662 /* XXX: use libedit */
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000663 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
Damien Miller33804262001-02-04 23:20:18 +1100664 printf("\n");
665 break;
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000666 } else if (infile != stdin) /* Bluff typing */
667 printf("%s", cmd);
668
Damien Miller33804262001-02-04 23:20:18 +1100669 cp = strrchr(cmd, '\n');
670 if (cp)
671 *cp = '\0';
Ben Lindstrom562c26b2001-03-07 01:26:48 +0000672
Damien Miller33804262001-02-04 23:20:18 +1100673 if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
674 break;
675 }
676 xfree(pwd);
677}