[BACK]Return to sftp-int.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/sftp-int.c, Revision 1.1

1.1     ! djm         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"
        !            30: RCSID("$OpenBSD$");
        !            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:
        !            67: struct CMD {
        !            68:        const int n;
        !            69:        const char *c;
        !            70: };
        !            71:
        !            72: const 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:
        !            99: void
        !           100: help(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:
        !           127: void
        !           128: local_do_shell(const char *args)
        !           129: {
        !           130:        int ret, status;
        !           131:        char *shell;
        !           132:        pid_t pid;
        !           133:
        !           134:        if (!*args)
        !           135:                args = NULL;
        !           136:
        !           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:                }
        !           152:                fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
        !           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:
        !           164: void
        !           165: local_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:
        !           178: char *
        !           179: make_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:
        !           193: int
        !           194: parse_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:
        !           215: int
        !           216: get_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:
        !           264: int
        !           265: infer_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:
        !           287: int
        !           288: parse_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) {
        !           370:                        error("You must specify a path after a %s command.",
        !           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) {
        !           406:                        error("You must specify a path after a %s command.",
        !           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:
        !           425: int
        !           426: parse_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:
        !           553: void
        !           554: interactive_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:
        !           563:        setlinebuf(stdout);
        !           564:        setlinebuf(stdin);
        !           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: }