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