]> andersk Git - openssh.git/blame - sftp-int.c
- stevesk@cvs.openbsd.org 2001/02/04 08:32:27
[openssh.git] / sftp-int.c
CommitLineData
61e96248 1/*
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 */
27/* XXX: recursive operations */
28
29#include "includes.h"
30RCSID("$OpenBSD: sftp-int.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
31
32#include "buffer.h"
33#include "xmalloc.h"
34#include "log.h"
35#include "pathnames.h"
36
37#include "sftp.h"
38#include "sftp-common.h"
39#include "sftp-client.h"
40#include "sftp-int.h"
41
42/* Seperators for interactive commands */
43#define WHITESPACE " \t\r\n"
44
45/* Commands for interactive mode */
46#define I_CHDIR 1
47#define I_CHGRP 2
48#define I_CHMOD 3
49#define I_CHOWN 4
50#define I_GET 5
51#define I_HELP 6
52#define I_LCHDIR 7
53#define I_LLS 8
54#define I_LMKDIR 9
55#define I_LPWD 10
56#define I_LS 11
57#define I_LUMASK 12
58#define I_MKDIR 13
59#define I_PUT 14
60#define I_PWD 15
61#define I_QUIT 16
62#define I_RENAME 17
63#define I_RM 18
64#define I_RMDIR 19
65#define I_SHELL 20
66
67struct CMD {
68 const int n;
69 const char *c;
70};
71
72const struct CMD cmds[] = {
73 { I_CHDIR, "CD" },
74 { I_CHDIR, "CHDIR" },
75 { I_CHDIR, "LCD" },
76 { I_CHGRP, "CHGRP" },
77 { I_CHMOD, "CHMOD" },
78 { I_CHOWN, "CHOWN" },
79 { I_HELP, "HELP" },
80 { I_GET, "GET" },
81 { I_LCHDIR, "LCHDIR" },
82 { I_LLS, "LLS" },
83 { I_LMKDIR, "LMKDIR" },
84 { I_LPWD, "LPWD" },
85 { I_LS, "LS" },
86 { I_LUMASK, "LUMASK" },
87 { I_MKDIR, "MKDIR" },
88 { I_PUT, "PUT" },
89 { I_PWD, "PWD" },
90 { I_QUIT, "EXIT" },
91 { I_QUIT, "QUIT" },
92 { I_RENAME, "RENAME" },
93 { I_RMDIR, "RMDIR" },
94 { I_RM, "RM" },
95 { I_SHELL, "!" },
96 { -1, NULL}
97};
98
99void
100help(void)
101{
102 printf("Available commands:\n");
103 printf("CD path Change remote directory to 'path'\n");
104 printf("LCD path Change local directory to 'path'\n");
105 printf("CHGRP grp path Change group of file 'path' to 'grp'\n");
106 printf("CHMOD mode path Change permissions of file 'path' to 'mode'\n");
107 printf("CHOWN own path Change owner of file 'path' to 'own'\n");
108 printf("HELP Display this help text\n");
109 printf("GET remote-path [local-path] Download file\n");
110 printf("LLS [ls options] [path] Display local directory listing\n");
111 printf("LMKDIR path Create local directory\n");
112 printf("LPWD Print local working directory\n");
113 printf("LS [path] Display remote directory listing\n");
114 printf("LUMASK umask Set local umask to 'umask'\n");
115 printf("MKDIR path Create remote directory\n");
116 printf("PUT local-path [remote-path] Upload file\n");
117 printf("PWD Display remote working directory\n");
118 printf("EXIT Quit sftp\n");
119 printf("QUIT Quit sftp\n");
120 printf("RENAME oldpath newpath Rename remote file\n");
121 printf("RMDIR path Remove remote directory\n");
122 printf("RM path Delete remote file\n");
123 printf("!command Execute 'command' in local shell\n");
124 printf("! Escape to local shell\n");
125}
126
127void
128local_do_shell(const char *args)
129{
130 int ret, status;
131 char *shell;
132 pid_t pid;
2b87da3b 133
61e96248 134 if (!*args)
135 args = NULL;
2b87da3b 136
61e96248 137 if ((shell = getenv("SHELL")) == NULL)
138 shell = _PATH_BSHELL;
139
140 if ((pid = fork()) == -1)
141 fatal("Couldn't fork: %s", strerror(errno));
142
143 if (pid == 0) {
144 /* XXX: child has pipe fds to ssh subproc open - issue? */
145 if (args) {
146 debug3("Executing %s -c \"%s\"", shell, args);
147 ret = execl(shell, shell, "-c", args, NULL);
148 } else {
149 debug3("Executing %s", shell);
150 ret = execl(shell, shell, NULL);
151 }
2b87da3b 152 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
61e96248 153 strerror(errno));
154 _exit(1);
155 }
156 if (waitpid(pid, &status, 0) == -1)
157 fatal("Couldn't wait for child: %s", strerror(errno));
158 if (!WIFEXITED(status))
159 error("Shell exited abormally");
160 else if (WEXITSTATUS(status))
161 error("Shell exited with status %d", WEXITSTATUS(status));
162}
163
2b87da3b 164void
61e96248 165local_do_ls(const char *args)
166{
167 if (!args || !*args)
168 local_do_shell("ls");
169 else {
170 char *buf = xmalloc(8 + strlen(args) + 1);
171
172 /* XXX: quoting - rip quoting code from ftp? */
173 sprintf(buf, "/bin/ls %s", args);
174 local_do_shell(buf);
175 }
176}
177
178char *
179make_absolute(char *p, char *pwd)
180{
181 char buf[2048];
182
183 /* Derelativise */
184 if (p && p[0] != '/') {
185 snprintf(buf, sizeof(buf), "%s/%s", pwd, p);
186 xfree(p);
187 p = xstrdup(buf);
188 }
189
190 return(p);
191}
192
193int
194parse_getput_flags(const char **cpp, int *pflag)
195{
196 const char *cp = *cpp;
197
198 /* Check for flags */
199 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
200 switch (*cp) {
201 case 'P':
202 *pflag = 1;
203 break;
204 default:
205 error("Invalid flag -%c", *cp);
206 return(-1);
207 }
208 cp += 2;
209 *cpp = cp + strspn(cp, WHITESPACE);
210 }
211
212 return(0);
213}
214
215int
216get_pathname(const char **cpp, char **path)
217{
218 const char *quot, *cp = *cpp;
219 int i;
220
221 cp += strspn(cp, WHITESPACE);
222 if (!*cp) {
223 *cpp = cp;
224 *path = NULL;
225 return(0);
226 }
227
228 /* Check for quoted filenames */
229 if (*cp == '\"' || *cp == '\'') {
230 quot = cp++;
231 for(i = 0; cp[i] && cp[i] != *quot; i++)
232 ;
233 if (!cp[i]) {
234 error("Unterminated quote");
235 *path = NULL;
236 return(-1);
237 }
238 if (i == 0) {
239 error("Empty quotes");
240 *path = NULL;
241 return(-1);
242 }
243 *path = xmalloc(i + 1);
244 memcpy(*path, cp, i);
245 (*path)[i] = '\0';
246 cp += i + 1;
247 *cpp = cp + strspn(cp, WHITESPACE);
248 return(0);
249 }
250
251 /* Read to end of filename */
252 for(i = 0; cp[i] && cp[i] != ' '; i++)
253 ;
254
255 *path = xmalloc(i + 1);
256 memcpy(*path, cp, i);
257 (*path)[i] = '\0';
258 cp += i;
259 *cpp = cp + strspn(cp, WHITESPACE);
260
261 return(0);
262}
263
264int
265infer_path(const char *p, char **ifp)
266{
267 char *cp;
268
269 debug("XXX: P = \"%s\"", p);
270
271 cp = strrchr(p, '/');
272
273 if (cp == NULL) {
274 *ifp = xstrdup(p);
275 return(0);
276 }
277
278 if (!cp[1]) {
279 error("Invalid path");
280 return(-1);
281 }
282
283 *ifp = xstrdup(cp + 1);
284 return(0);
285}
286
287int
288parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
289 char **path1, char **path2)
290{
291 const char *cmd, *cp = *cpp;
292 int i, cmdnum;
293
294 /* Skip leading whitespace */
295 cp = cp + strspn(cp, WHITESPACE);
296
297 /* Ignore blank lines */
298 if (!*cp)
299 return(-1);
300
301 /* Figure out which command we have */
302 for(i = 0; cmds[i].c; i++) {
303 int cmdlen = strlen(cmds[i].c);
304
305 /* Check for command followed by whitespace */
306 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
307 strchr(WHITESPACE, cp[cmdlen])) {
308 cp += cmdlen;
309 cp = cp + strspn(cp, WHITESPACE);
310 break;
311 }
312 }
313 cmdnum = cmds[i].n;
314 cmd = cmds[i].c;
315
316 /* Special case */
317 if (*cp == '!') {
318 cp++;
319 cmdnum = I_SHELL;
320 } else if (cmdnum == -1) {
321 error("Invalid command.");
322 return(-1);
323 }
324
325 /* Get arguments and parse flags */
326 *pflag = *n_arg = 0;
327 *path1 = *path2 = NULL;
328 switch (cmdnum) {
329 case I_GET:
330 case I_PUT:
331 if (parse_getput_flags(&cp, pflag))
332 return(-1);
333 /* Get first pathname (mandatory) */
334 if (get_pathname(&cp, path1))
335 return(-1);
336 if (*path1 == NULL) {
337 error("You must specify at least one path after a "
338 "%s command.", cmd);
339 return(-1);
340 }
341 /* Try to get second pathname (optional) */
342 if (get_pathname(&cp, path2))
343 return(-1);
344 /* Otherwise try to guess it from first path */
345 if (*path2 == NULL && infer_path(*path1, path2))
346 return(-1);
347 break;
348 case I_RENAME:
349 /* Get first pathname (mandatory) */
350 if (get_pathname(&cp, path1))
351 return(-1);
352 if (get_pathname(&cp, path2))
353 return(-1);
354 if (!*path1 || !*path2) {
355 error("You must specify two paths after a %s "
356 "command.", cmd);
357 return(-1);
358 }
359 break;
360 case I_RM:
361 case I_MKDIR:
362 case I_RMDIR:
363 case I_CHDIR:
364 case I_LCHDIR:
365 case I_LMKDIR:
366 /* Get pathname (mandatory) */
367 if (get_pathname(&cp, path1))
368 return(-1);
369 if (*path1 == NULL) {
2b87da3b 370 error("You must specify a path after a %s command.",
61e96248 371 cmd);
372 return(-1);
373 }
374 break;
375 case I_LS:
376 /* Path is optional */
377 if (get_pathname(&cp, path1))
378 return(-1);
379 break;
380 case I_LLS:
381 case I_SHELL:
382 /* Uses the rest of the line */
383 break;
384 case I_LUMASK:
385 case I_CHMOD:
386 case I_CHOWN:
387 case I_CHGRP:
388 /* Get numeric arg (mandatory) */
389 if (*cp < '0' && *cp > '9') {
390 error("You must supply a numeric argument "
391 "to the %s command.", cmd);
392 return(-1);
393 }
394 *n_arg = strtoul(cp, (char**)&cp, 0);
395 if (!*cp || !strchr(WHITESPACE, *cp)) {
396 error("You must supply a numeric argument "
397 "to the %s command.", cmd);
398 return(-1);
399 }
400 cp += strspn(cp, WHITESPACE);
401
402 /* Get pathname (mandatory) */
403 if (get_pathname(&cp, path1))
404 return(-1);
405 if (*path1 == NULL) {
2b87da3b 406 error("You must specify a path after a %s command.",
61e96248 407 cmd);
408 return(-1);
409 }
410 break;
411 case I_QUIT:
412 case I_PWD:
413 case I_LPWD:
414 case I_HELP:
415 break;
416 default:
417 fatal("Command not implemented");
418 }
419
420 *cpp = cp;
421
422 return(cmdnum);
423}
424
425int
426parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
427{
428 char *path1, *path2;
429 int pflag, cmdnum;
430 unsigned long n_arg;
431 Attrib a, *aa;
432 char path_buf[PATH_MAX];
433
434 path1 = path2 = NULL;
435 cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
436
437 /* Perform command */
438 switch (cmdnum) {
439 case -1:
440 break;
441 case I_GET:
442 path1 = make_absolute(path1, *pwd);
443 do_download(in, out, path1, path2, pflag);
444 break;
445 case I_PUT:
446 path2 = make_absolute(path2, *pwd);
447 do_upload(in, out, path1, path2, pflag);
448 break;
449 case I_RENAME:
450 path1 = make_absolute(path1, *pwd);
451 path2 = make_absolute(path2, *pwd);
452 do_rename(in, out, path1, path2);
453 break;
454 case I_RM:
455 path1 = make_absolute(path1, *pwd);
456 do_rm(in, out, path1);
457 break;
458 case I_MKDIR:
459 path1 = make_absolute(path1, *pwd);
460 attrib_clear(&a);
461 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
462 a.perm = 0777;
463 do_mkdir(in, out, path1, &a);
464 break;
465 case I_RMDIR:
466 path1 = make_absolute(path1, *pwd);
467 do_rmdir(in, out, path1);
468 break;
469 case I_CHDIR:
470 path1 = make_absolute(path1, *pwd);
471 xfree(*pwd);
472 *pwd = do_realpath(in, out, path1);
473 break;
474 case I_LS:
475 path1 = make_absolute(path1, *pwd);
476 do_ls(in, out, path1?path1:*pwd);
477 break;
478 case I_LCHDIR:
479 if (chdir(path1) == -1)
480 error("Couldn't change local directory to "
481 "\"%s\": %s", path1, strerror(errno));
482 break;
483 case I_LMKDIR:
484 if (mkdir(path1, 0777) == -1)
485 error("Couldn't create local directory to "
486 "\"%s\": %s", path1, strerror(errno));
487 break;
488 case I_LLS:
489 local_do_ls(cmd);
490 break;
491 case I_SHELL:
492 local_do_shell(cmd);
493 break;
494 case I_LUMASK:
495 umask(n_arg);
496 break;
497 case I_CHMOD:
498 path1 = make_absolute(path1, *pwd);
499 attrib_clear(&a);
500 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
501 a.perm = n_arg;
502 do_setstat(in, out, path1, &a);
503 case I_CHOWN:
504 path1 = make_absolute(path1, *pwd);
505 aa = do_stat(in, out, path1);
506 if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) {
507 error("Can't get current ownership of "
508 "remote file \"%s\"", path1);
509 break;
510 }
511 aa->uid = n_arg;
512 do_setstat(in, out, path1, aa);
513 break;
514 case I_CHGRP:
515 path1 = make_absolute(path1, *pwd);
516 aa = do_stat(in, out, path1);
517 if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) {
518 error("Can't get current ownership of "
519 "remote file \"%s\"", path1);
520 break;
521 }
522 aa->gid = n_arg;
523 do_setstat(in, out, path1, aa);
524 break;
525 case I_PWD:
526 printf("Remote working directory: %s\n", *pwd);
527 break;
528 case I_LPWD:
529 if (!getcwd(path_buf, sizeof(path_buf)))
530 error("Couldn't get local cwd: %s\n",
531 strerror(errno));
532 else
533 printf("Local working directory: %s\n",
534 path_buf);
535 break;
536 case I_QUIT:
537 return(-1);
538 case I_HELP:
539 help();
540 break;
541 default:
542 fatal("%d is not implemented", cmdnum);
543 }
544
545 if (path1)
546 xfree(path1);
547 if (path2)
548 xfree(path2);
549
550 return(0);
551}
552
553void
554interactive_loop(int fd_in, int fd_out)
555{
556 char *pwd;
557 char cmd[2048];
558
559 pwd = do_realpath(fd_in, fd_out, ".");
560 if (pwd == NULL)
561 fatal("Need cwd");
562
c535e762 563 setvbuf(stdout, (char *)NULL, _IOLBF, 0);
564 setvbuf(stdin, (char *)NULL, _IOLBF, 0);
61e96248 565
566 for(;;) {
567 char *cp;
568
569 printf("sftp> ");
570
571 /* XXX: use libedit */
572 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
573 printf("\n");
574 break;
575 }
576 cp = strrchr(cmd, '\n');
577 if (cp)
578 *cp = '\0';
579 if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
580 break;
581 }
582 xfree(pwd);
583}
This page took 0.95728 seconds and 5 git commands to generate.