source: trunk/third/openssh/sftp-int.c @ 18759

Revision 18759, 23.8 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18758, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 2001,2002 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: recursive operations */
26
27#include "includes.h"
28RCSID("$OpenBSD: sftp-int.c,v 1.49 2002/09/12 00:13:06 djm Exp $");
29
30#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"
37#include "sftp-glob.h"
38#include "sftp-client.h"
39#include "sftp-int.h"
40
41/* File to read commands from */
42extern FILE *infile;
43
44/* Size of buffer used when copying files */
45extern size_t copy_buffer_len;
46
47/* Number of concurrent outstanding requests */
48extern int num_requests;
49
50/* 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
74#define I_SYMLINK       21
75#define I_VERSION       22
76
77struct CMD {
78        const char *c;
79        const int n;
80};
81
82const struct CMD cmds[] = {
83        { "bye",        I_QUIT },
84        { "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 },
92        { "mget",       I_GET },
93        { "help",       I_HELP },
94        { "lcd",        I_LCHDIR },
95        { "lchdir",     I_LCHDIR },
96        { "lls",        I_LLS },
97        { "lmkdir",     I_LMKDIR },
98        { "ln",         I_SYMLINK },
99        { "lpwd",       I_LPWD },
100        { "ls",         I_LS },
101        { "lumask",     I_LUMASK },
102        { "mkdir",      I_MKDIR },
103        { "put",        I_PUT },
104        { "mput",       I_PUT },
105        { "pwd",        I_PWD },
106        { "quit",       I_QUIT },
107        { "rename",     I_RENAME },
108        { "rm",         I_RM },
109        { "rmdir",      I_RMDIR },
110        { "symlink",    I_SYMLINK },
111        { "version",    I_VERSION },
112        { "!",          I_SHELL },
113        { "?",          I_HELP },
114        { NULL,                 -1}
115};
116
117static void
118help(void)
119{
120        printf("Available commands:\n");
121        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");
129        printf("ln oldpath newpath            Symlink remote file\n");
130        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");
142        printf("symlink oldpath newpath       Symlink remote file\n");
143        printf("version                       Show SFTP version\n");
144        printf("!command                      Execute 'command' in local shell\n");
145        printf("!                             Escape to local shell\n");
146        printf("?                             Synonym for help\n");
147}
148
149static void
150local_do_shell(const char *args)
151{
152        int status;
153        char *shell;
154        pid_t pid;
155
156        if (!*args)
157                args = NULL;
158
159        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);
169                        execl(shell, shell, "-c", args, (char *)NULL);
170                } else {
171                        debug3("Executing %s", shell);
172                        execl(shell, shell, (char *)NULL);
173                }
174                fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
175                    strerror(errno));
176                _exit(1);
177        }
178        while (waitpid(pid, &status, 0) == -1)
179                if (errno != EINTR)
180                        fatal("Couldn't wait for child: %s", strerror(errno));
181        if (!WIFEXITED(status))
182                error("Shell exited abormally");
183        else if (WEXITSTATUS(status))
184                error("Shell exited with status %d", WEXITSTATUS(status));
185}
186
187static void
188local_do_ls(const char *args)
189{
190        if (!args || !*args)
191                local_do_shell(_PATH_LS);
192        else {
193                int len = strlen(_PATH_LS " ") + strlen(args) + 1;
194                char *buf = xmalloc(len);
195
196                /* XXX: quoting - rip quoting code from ftp? */
197                snprintf(buf, len, _PATH_LS " %s", args);
198                local_do_shell(buf);
199                xfree(buf);
200        }
201}
202
203/* Strip one path (usually the pwd) from the start of another */
204static char *
205path_strip(char *path, char *strip)
206{
207        size_t len;
208
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
222static char *
223path_append(char *p1, char *p2)
224{
225        char *ret;
226        int len = strlen(p1) + strlen(p2) + 2;
227
228        ret = xmalloc(len);
229        strlcpy(ret, p1, len);
230        if (p1[strlen(p1) - 1] != '/')
231                strlcat(ret, "/", len);
232        strlcat(ret, p2, len);
233
234        return(ret);
235}
236
237static char *
238make_absolute(char *p, char *pwd)
239{
240        char *abs;
241
242        /* Derelativise */
243        if (p && p[0] != '/') {
244                abs = path_append(pwd, p);
245                xfree(p);
246                return(abs);
247        } else
248                return(p);
249}
250
251static int
252infer_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);
260        }
261
262        if (!cp[1]) {
263                error("Invalid path");
264                return(-1);
265        }
266
267        *ifp = xstrdup(cp + 1);
268        return(0);
269}
270
271static int
272parse_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])) {
278                switch (cp[1]) {
279                case 'p':
280                case 'P':
281                        *pflag = 1;
282                        break;
283                default:
284                        error("Invalid flag -%c", cp[1]);
285                        return(-1);
286                }
287                cp += 2;
288                *cpp = cp + strspn(cp, WHITESPACE);
289        }
290
291        return(0);
292}
293
294static int
295parse_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
318get_pathname(const char **cpp, char **path)
319{
320        const char *cp = *cpp, *end;
321        char quot;
322        int i;
323
324        cp += strspn(cp, WHITESPACE);
325        if (!*cp) {
326                *cpp = cp;
327                *path = NULL;
328                return (0);
329        }
330
331        /* Check for quoted filenames */
332        if (*cp == '\"' || *cp == '\'') {
333                quot = *cp++;
334
335                end = strchr(cp, quot);
336                if (end == NULL) {
337                        error("Unterminated quote");
338                        goto fail;
339                }
340                if (cp == end) {
341                        error("Empty quotes");
342                        goto fail;
343                }
344                *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);
351        }
352
353        i = end - cp;
354
355        *path = xmalloc(i + 1);
356        memcpy(*path, cp, i);
357        (*path)[i] = '\0';
358        return(0);
359
360 fail:
361        *path = NULL;
362        return (-1);
363}
364
365static int
366is_dir(char *path)
367{
368        struct stat sb;
369
370        /* XXX: report errors? */
371        if (stat(path, &sb) == -1)
372                return(0);
373
374        return(sb.st_mode & S_IFDIR);
375}
376
377static int
378remote_is_dir(struct sftp_conn *conn, char *path)
379{
380        Attrib *a;
381
382        /* XXX: report errors? */
383        if ((a = do_stat(conn, path, 1)) == NULL)
384                return(0);
385        if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
386                return(0);
387        return(a->perm & S_IFDIR);
388}
389
390static int
391process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
392{
393        char *abs_src = NULL;
394        char *abs_dst = NULL;
395        char *tmp;
396        glob_t g;
397        int err = 0;
398        int i;
399
400        abs_src = xstrdup(src);
401        abs_src = make_absolute(abs_src, pwd);
402
403        memset(&g, 0, sizeof(g));
404        debug3("Looking up %s", abs_src);
405        if (remote_glob(conn, abs_src, 0, NULL, &g)) {
406                error("File \"%s\" not found.", abs_src);
407                err = -1;
408                goto out;
409        }
410
411        /* 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);
429                err = do_download(conn, g.gl_pathv[0], abs_dst, pflag);
430                goto out;
431        }
432
433        /* Multiple matches, dst may be directory or unspecified */
434        if (dst && !is_dir(dst)) {
435                error("Multiple files match, but \"%s\" is not a directory",
436                    dst);
437                err = -1;
438                goto out;
439        }
440
441        for (i = 0; g.gl_pathv[i]; i++) {
442                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);
453                if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
454                        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
467static int
468process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
469{
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
482        memset(&g, 0, sizeof(g));
483        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 */
494                        if (remote_is_dir(conn, tmp_dst)) {
495                                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);
503                } 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);
509                }
510                printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst);
511                err = do_upload(conn, g.gl_pathv[0], abs_dst, pflag);
512                goto out;
513        }
514
515        /* Multiple matches, dst may be directory or unspecified */
516        if (tmp_dst && !remote_is_dir(conn, tmp_dst)) {
517                error("Multiple files match, but \"%s\" is not a directory",
518                    tmp_dst);
519                err = -1;
520                goto out;
521        }
522
523        for (i = 0; g.gl_pathv[i]; i++) {
524                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);
535                if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
536                        err = -1;
537        }
538
539out:
540        if (abs_dst)
541                xfree(abs_dst);
542        if (tmp_dst)
543                xfree(tmp_dst);
544        return(err);
545}
546
547static int
548sdirent_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)
671{
672        const char *cmd, *cp = *cpp;
673        char *cp2;
674        int base = 0;
675        long l;
676        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 */
686        for (i = 0; cmds[i].c; i++) {
687                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 */
710        *lflag = *pflag = *n_arg = 0;
711        *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);
728                break;
729        case I_RENAME:
730        case I_SYMLINK:
731                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) {
751                        error("You must specify a path after a %s command.",
752                            cmd);
753                        return(-1);
754                }
755                break;
756        case I_LS:
757                if (parse_ls_flags(&cp, lflag))
758                        return(-1);
759                /* 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:
768                base = 8;
769        case I_CHMOD:
770                base = 8;
771        case I_CHOWN:
772        case I_CHGRP:
773                /* Get numeric arg (mandatory) */
774                l = strtol(cp, &cp2, base);
775                if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
776                    errno == ERANGE) || l < 0) {
777                        error("You must supply a numeric argument "
778                            "to the %s command.", cmd);
779                        return(-1);
780                }
781                cp = cp2;
782                *n_arg = l;
783                if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
784                        break;
785                if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
786                        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) {
796                        error("You must specify a path after a %s command.",
797                            cmd);
798                        return(-1);
799                }
800                break;
801        case I_QUIT:
802        case I_PWD:
803        case I_LPWD:
804        case I_HELP:
805        case I_VERSION:
806                break;
807        default:
808                fatal("Command not implemented");
809        }
810
811        *cpp = cp;
812        return(cmdnum);
813}
814
815static int
816parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd)
817{
818        char *path1, *path2, *tmp;
819        int pflag, lflag, cmdnum, i;
820        unsigned long n_arg;
821        Attrib a, *aa;
822        char path_buf[MAXPATHLEN];
823        int err = 0;
824        glob_t g;
825
826        path1 = path2 = NULL;
827        cmdnum = parse_args(&cmd, &pflag, &lflag, &n_arg,
828            &path1, &path2);
829
830        memset(&g, 0, sizeof(g));
831
832        /* Perform command */
833        switch (cmdnum) {
834        case -1:
835                break;
836        case I_GET:
837                err = process_get(conn, path1, path2, *pwd, pflag);
838                break;
839        case I_PUT:
840                err = process_put(conn, path1, path2, *pwd, pflag);
841                break;
842        case I_RENAME:
843                path1 = make_absolute(path1, *pwd);
844                path2 = make_absolute(path2, *pwd);
845                err = do_rename(conn, path1, path2);
846                break;
847        case I_SYMLINK:
848                path2 = make_absolute(path2, *pwd);
849                err = do_symlink(conn, path1, path2);
850                break;
851        case I_RM:
852                path1 = make_absolute(path1, *pwd);
853                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
854                for (i = 0; g.gl_pathv[i]; i++) {
855                        printf("Removing %s\n", g.gl_pathv[i]);
856                        if (do_rm(conn, g.gl_pathv[i]) == -1)
857                                err = -1;
858                }
859                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;
865                err = do_mkdir(conn, path1, &a);
866                break;
867        case I_RMDIR:
868                path1 = make_absolute(path1, *pwd);
869                err = do_rmdir(conn, path1);
870                break;
871        case I_CHDIR:
872                path1 = make_absolute(path1, *pwd);
873                if ((tmp = do_realpath(conn, path1)) == NULL) {
874                        err = 1;
875                        break;
876                }
877                if ((aa = do_stat(conn, tmp, 0)) == NULL) {
878                        xfree(tmp);
879                        err = 1;
880                        break;
881                }
882                if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
883                        error("Can't change directory: Can't check target");
884                        xfree(tmp);
885                        err = 1;
886                        break;
887                }
888                if (!S_ISDIR(aa->perm)) {
889                        error("Can't change directory: \"%s\" is not "
890                            "a directory", tmp);
891                        xfree(tmp);
892                        err = 1;
893                        break;
894                }
895                xfree(*pwd);
896                *pwd = tmp;
897                break;
898        case I_LS:
899                if (!path1) {
900                        do_globbed_ls(conn, *pwd, *pwd, lflag);
901                        break;
902                }
903               
904                /* Strip pwd off beginning of non-absolute paths */
905                tmp = NULL;
906                if (*path1 != '/')
907                        tmp = *pwd;
908
909                path1 = make_absolute(path1, *pwd);
910
911                do_globbed_ls(conn, path1, tmp, lflag);
912                break;
913        case I_LCHDIR:
914                if (chdir(path1) == -1) {
915                        error("Couldn't change local directory to "
916                            "\"%s\": %s", path1, strerror(errno));
917                        err = 1;
918                }
919                break;
920        case I_LMKDIR:
921                if (mkdir(path1, 0777) == -1) {
922                        error("Couldn't create local directory "
923                            "\"%s\": %s", path1, strerror(errno));
924                        err = 1;
925                }
926                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);
935                printf("Local umask: %03lo\n", n_arg);
936                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;
942                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
943                for (i = 0; g.gl_pathv[i]; i++) {
944                        printf("Changing mode on %s\n", g.gl_pathv[i]);
945                        do_setstat(conn, g.gl_pathv[i], &a);
946                }
947                break;
948        case I_CHOWN:
949                path1 = make_absolute(path1, *pwd);
950                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
951                for (i = 0; g.gl_pathv[i]; i++) {
952                        if (!(aa = do_stat(conn, g.gl_pathv[i], 0)))
953                                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;
962                        do_setstat(conn, g.gl_pathv[i], aa);
963                }
964                break;
965        case I_CHGRP:
966                path1 = make_absolute(path1, *pwd);
967                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
968                for (i = 0; g.gl_pathv[i]; i++) {
969                        if (!(aa = do_stat(conn, g.gl_pathv[i], 0)))
970                                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;
979                        do_setstat(conn, g.gl_pathv[i], aa);
980                }
981                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)))
987                        error("Couldn't get local cwd: %s",
988                            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;
998        case I_VERSION:
999                printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1000                break;
1001        default:
1002                fatal("%d is not implemented", cmdnum);
1003        }
1004
1005        if (g.gl_pathc)
1006                globfree(&g);
1007        if (path1)
1008                xfree(path1);
1009        if (path2)
1010                xfree(path2);
1011
1012        /* If an error occurs in batch mode we should abort. */
1013        if (infile != stdin && err > 0)
1014                return -1;
1015
1016        return(0);
1017}
1018
1019void
1020interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1021{
1022        char *pwd;
1023        char *dir = NULL;
1024        char cmd[2048];
1025        struct sftp_conn *conn;
1026
1027        conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1028        if (conn == NULL)
1029                fatal("Couldn't initialise connection to server");
1030
1031        pwd = do_realpath(conn, ".");
1032        if (pwd == NULL)
1033                fatal("Need cwd");
1034
1035        if (file1 != NULL) {
1036                dir = xstrdup(file1);
1037                dir = make_absolute(dir, pwd);
1038
1039                if (remote_is_dir(conn, dir) && file2 == NULL) {
1040                        printf("Changing to: %s\n", dir);
1041                        snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1042                        parse_dispatch_command(conn, cmd, &pwd);
1043                } 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
1050                        parse_dispatch_command(conn, cmd, &pwd);
1051                        xfree(dir);
1052                        return;
1053                }
1054                xfree(dir);
1055        }
1056#if HAVE_SETVBUF
1057        setvbuf(stdout, NULL, _IOLBF, 0);
1058        setvbuf(infile, NULL, _IOLBF, 0);
1059#else
1060        setlinebuf(stdout);
1061        setlinebuf(infile);
1062#endif
1063
1064        for (;;) {
1065                char *cp;
1066
1067                printf("sftp> ");
1068
1069                /* XXX: use libedit */
1070                if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1071                        printf("\n");
1072                        break;
1073                } else if (infile != stdin) /* Bluff typing */
1074                        printf("%s", cmd);
1075
1076                cp = strrchr(cmd, '\n');
1077                if (cp)
1078                        *cp = '\0';
1079
1080                if (parse_dispatch_command(conn, cmd, &pwd))
1081                        break;
1082        }
1083        xfree(pwd);
1084}
Note: See TracBrowser for help on using the repository browser.