[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.22.2.5

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:
1.12      djm        25: /* XXX: globbed ls */
1.1       djm        26: /* XXX: recursive operations */
                     27:
                     28: #include "includes.h"
1.22.2.5! miod       29: RCSID("$OpenBSD: sftp-int.c,v 1.40 2001/08/14 09:23:02 markus Exp $");
1.22.2.3  jason      30:
                     31: #include <glob.h>
1.1       djm        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"
1.22.2.3  jason      40: #include "sftp-glob.h"
1.1       djm        41: #include "sftp-client.h"
                     42: #include "sftp-int.h"
                     43:
1.22.2.3  jason      44: /* File to read commands from */
                     45: extern FILE *infile;
                     46:
                     47: /* Version of server we are speaking to */
                     48: int version;
                     49:
1.1       djm        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
1.22.2.3  jason      74: #define I_SYMLINK      21
                     75: #define I_VERSION      22
1.1       djm        76:
                     77: struct CMD {
1.6       deraadt    78:        const char *c;
1.1       djm        79:        const int n;
                     80: };
                     81:
                     82: const struct CMD cmds[] = {
1.22.2.5! miod       83:        { "bye",        I_QUIT },
1.15      stevesk    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 },
1.22.2.4  jason      92:        { "mget",       I_GET },
1.15      stevesk    93:        { "help",       I_HELP },
                     94:        { "lcd",        I_LCHDIR },
                     95:        { "lchdir",     I_LCHDIR },
                     96:        { "lls",        I_LLS },
                     97:        { "lmkdir",     I_LMKDIR },
1.22.2.3  jason      98:        { "ln",         I_SYMLINK },
1.15      stevesk    99:        { "lpwd",       I_LPWD },
                    100:        { "ls",         I_LS },
                    101:        { "lumask",     I_LUMASK },
                    102:        { "mkdir",      I_MKDIR },
                    103:        { "put",        I_PUT },
1.22.2.4  jason     104:        { "mput",       I_PUT },
1.15      stevesk   105:        { "pwd",        I_PWD },
                    106:        { "quit",       I_QUIT },
                    107:        { "rename",     I_RENAME },
                    108:        { "rm",         I_RM },
                    109:        { "rmdir",      I_RMDIR },
1.22.2.3  jason     110:        { "symlink",    I_SYMLINK },
                    111:        { "version",    I_VERSION },
1.6       deraadt   112:        { "!",          I_SHELL },
1.7       deraadt   113:        { "?",          I_HELP },
1.6       deraadt   114:        { NULL,                 -1}
1.1       djm       115: };
                    116:
1.22.2.5! miod      117: static void
1.1       djm       118: help(void)
                    119: {
                    120:        printf("Available commands:\n");
1.13      stevesk   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");
1.22.2.3  jason     129:        printf("ln oldpath newpath            Symlink remote file\n");
1.13      stevesk   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");
1.22.2.3  jason     142:        printf("symlink oldpath newpath       Symlink remote file\n");
                    143:        printf("version                       Show SFTP version\n");
1.1       djm       144:        printf("!command                      Execute 'command' in local shell\n");
                    145:        printf("!                             Escape to local shell\n");
1.13      stevesk   146:        printf("?                             Synonym for help\n");
1.1       djm       147: }
                    148:
1.22.2.5! miod      149: static void
1.1       djm       150: local_do_shell(const char *args)
                    151: {
1.22.2.4  jason     152:        int status;
1.1       djm       153:        char *shell;
                    154:        pid_t pid;
1.3       stevesk   155:
1.1       djm       156:        if (!*args)
                    157:                args = NULL;
1.3       stevesk   158:
1.1       djm       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);
1.22.2.5! miod      169:                        execl(shell, shell, "-c", args, (char *)NULL);
1.1       djm       170:                } else {
                    171:                        debug3("Executing %s", shell);
1.22.2.5! miod      172:                        execl(shell, shell, (char *)NULL);
1.1       djm       173:                }
1.3       stevesk   174:                fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
1.1       djm       175:                    strerror(errno));
                    176:                _exit(1);
                    177:        }
                    178:        if (waitpid(pid, &status, 0) == -1)
                    179:                fatal("Couldn't wait for child: %s", strerror(errno));
                    180:        if (!WIFEXITED(status))
                    181:                error("Shell exited abormally");
                    182:        else if (WEXITSTATUS(status))
                    183:                error("Shell exited with status %d", WEXITSTATUS(status));
                    184: }
                    185:
1.22.2.5! miod      186: static void
1.1       djm       187: local_do_ls(const char *args)
                    188: {
                    189:        if (!args || !*args)
1.18      stevesk   190:                local_do_shell(_PATH_LS);
1.1       djm       191:        else {
1.18      stevesk   192:                int len = strlen(_PATH_LS " ") + strlen(args) + 1;
1.16      deraadt   193:                char *buf = xmalloc(len);
1.1       djm       194:
                    195:                /* XXX: quoting - rip quoting code from ftp? */
1.18      stevesk   196:                snprintf(buf, len, _PATH_LS " %s", args);
1.1       djm       197:                local_do_shell(buf);
1.16      deraadt   198:                xfree(buf);
1.1       djm       199:        }
                    200: }
                    201:
1.22.2.5! miod      202: static char *
1.22.2.3  jason     203: path_append(char *p1, char *p2)
                    204: {
                    205:        char *ret;
                    206:        int len = strlen(p1) + strlen(p2) + 2;
                    207:
                    208:        ret = xmalloc(len);
                    209:        strlcpy(ret, p1, len);
1.22.2.5! miod      210:        if (strcmp(p1, "/") != 0)
        !           211:                strlcat(ret, "/", len);
1.22.2.3  jason     212:        strlcat(ret, p2, len);
                    213:
                    214:        return(ret);
                    215: }
                    216:
1.22.2.5! miod      217: static char *
1.1       djm       218: make_absolute(char *p, char *pwd)
                    219: {
1.22.2.3  jason     220:        char *abs;
1.1       djm       221:
                    222:        /* Derelativise */
                    223:        if (p && p[0] != '/') {
1.22.2.3  jason     224:                abs = path_append(pwd, p);
1.1       djm       225:                xfree(p);
1.22.2.3  jason     226:                return(abs);
                    227:        } else
                    228:                return(p);
                    229: }
                    230:
1.22.2.5! miod      231: static int
1.22.2.3  jason     232: infer_path(const char *p, char **ifp)
                    233: {
                    234:        char *cp;
                    235:
                    236:        cp = strrchr(p, '/');
                    237:        if (cp == NULL) {
                    238:                *ifp = xstrdup(p);
                    239:                return(0);
                    240:        }
                    241:
                    242:        if (!cp[1]) {
                    243:                error("Invalid path");
                    244:                return(-1);
1.1       djm       245:        }
                    246:
1.22.2.3  jason     247:        *ifp = xstrdup(cp + 1);
                    248:        return(0);
1.1       djm       249: }
                    250:
1.22.2.5! miod      251: static int
1.1       djm       252: parse_getput_flags(const char **cpp, int *pflag)
                    253: {
                    254:        const char *cp = *cpp;
                    255:
                    256:        /* Check for flags */
                    257:        if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
1.20      djm       258:                switch (cp[1]) {
1.22      djm       259:                case 'p':
1.1       djm       260:                case 'P':
                    261:                        *pflag = 1;
                    262:                        break;
                    263:                default:
1.22      djm       264:                        error("Invalid flag -%c", cp[1]);
1.1       djm       265:                        return(-1);
                    266:                }
                    267:                cp += 2;
                    268:                *cpp = cp + strspn(cp, WHITESPACE);
                    269:        }
                    270:
                    271:        return(0);
                    272: }
                    273:
1.22.2.5! miod      274: static int
1.1       djm       275: get_pathname(const char **cpp, char **path)
                    276: {
1.8       provos    277:        const char *cp = *cpp, *end;
                    278:        char quot;
1.1       djm       279:        int i;
                    280:
                    281:        cp += strspn(cp, WHITESPACE);
                    282:        if (!*cp) {
                    283:                *cpp = cp;
                    284:                *path = NULL;
1.8       provos    285:                return (0);
1.1       djm       286:        }
                    287:
                    288:        /* Check for quoted filenames */
                    289:        if (*cp == '\"' || *cp == '\'') {
1.8       provos    290:                quot = *cp++;
1.22.2.3  jason     291:
1.8       provos    292:                end = strchr(cp, quot);
                    293:                if (end == NULL) {
1.1       djm       294:                        error("Unterminated quote");
1.8       provos    295:                        goto fail;
1.1       djm       296:                }
1.8       provos    297:                if (cp == end) {
1.1       djm       298:                        error("Empty quotes");
1.8       provos    299:                        goto fail;
1.1       djm       300:                }
1.8       provos    301:                *cpp = end + 1 + strspn(end + 1, WHITESPACE);
                    302:        } else {
                    303:                /* Read to end of filename */
                    304:                end = strpbrk(cp, WHITESPACE);
                    305:                if (end == NULL)
                    306:                        end = strchr(cp, '\0');
                    307:                *cpp = end + strspn(end, WHITESPACE);
1.1       djm       308:        }
                    309:
1.8       provos    310:        i = end - cp;
1.1       djm       311:
                    312:        *path = xmalloc(i + 1);
                    313:        memcpy(*path, cp, i);
                    314:        (*path)[i] = '\0';
                    315:        return(0);
1.8       provos    316:
                    317:  fail:
                    318:        *path = NULL;
                    319:        return (-1);
1.1       djm       320: }
                    321:
1.22.2.5! miod      322: static int
1.22.2.3  jason     323: is_dir(char *path)
1.1       djm       324: {
1.22.2.3  jason     325:        struct stat sb;
1.1       djm       326:
1.22.2.3  jason     327:        /* XXX: report errors? */
                    328:        if (stat(path, &sb) == -1)
                    329:                return(0);
1.1       djm       330:
1.22.2.3  jason     331:        return(sb.st_mode & S_IFDIR);
                    332: }
                    333:
1.22.2.5! miod      334: static int
1.22.2.3  jason     335: remote_is_dir(int in, int out, char *path)
                    336: {
                    337:        Attrib *a;
                    338:
                    339:        /* XXX: report errors? */
                    340:        if ((a = do_stat(in, out, path, 1)) == NULL)
1.1       djm       341:                return(0);
1.22.2.3  jason     342:        if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
                    343:                return(0);
                    344:        return(a->perm & S_IFDIR);
                    345: }
1.1       djm       346:
1.22.2.5! miod      347: static int
1.22.2.3  jason     348: process_get(int in, int out, char *src, char *dst, char *pwd, int pflag)
                    349: {
                    350:        char *abs_src = NULL;
                    351:        char *abs_dst = NULL;
                    352:        char *tmp;
                    353:        glob_t g;
                    354:        int err = 0;
                    355:        int i;
                    356:
                    357:        abs_src = xstrdup(src);
                    358:        abs_src = make_absolute(abs_src, pwd);
                    359:
                    360:        memset(&g, 0, sizeof(g));
                    361:        debug3("Looking up %s", abs_src);
                    362:        if (remote_glob(in, out, abs_src, 0, NULL, &g)) {
                    363:                error("File \"%s\" not found.", abs_src);
                    364:                err = -1;
                    365:                goto out;
                    366:        }
                    367:
                    368:        /* Only one match, dst may be file, directory or unspecified */
                    369:        if (g.gl_pathv[0] && g.gl_matchc == 1) {
                    370:                if (dst) {
                    371:                        /* If directory specified, append filename */
                    372:                        if (is_dir(dst)) {
                    373:                                if (infer_path(g.gl_pathv[0], &tmp)) {
                    374:                                        err = 1;
                    375:                                        goto out;
                    376:                                }
                    377:                                abs_dst = path_append(dst, tmp);
                    378:                                xfree(tmp);
                    379:                        } else
                    380:                                abs_dst = xstrdup(dst);
                    381:                } else if (infer_path(g.gl_pathv[0], &abs_dst)) {
                    382:                        err = -1;
                    383:                        goto out;
                    384:                }
                    385:                printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst);
                    386:                err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag);
                    387:                goto out;
                    388:        }
                    389:
                    390:        /* Multiple matches, dst may be directory or unspecified */
                    391:        if (dst && !is_dir(dst)) {
                    392:                error("Multiple files match, but \"%s\" is not a directory",
                    393:                    dst);
                    394:                err = -1;
                    395:                goto out;
                    396:        }
                    397:
                    398:        for(i = 0; g.gl_pathv[i]; i++) {
                    399:                if (infer_path(g.gl_pathv[i], &tmp)) {
                    400:                        err = -1;
                    401:                        goto out;
                    402:                }
                    403:                if (dst) {
                    404:                        abs_dst = path_append(dst, tmp);
                    405:                        xfree(tmp);
                    406:                } else
                    407:                        abs_dst = tmp;
                    408:
                    409:                printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
                    410:                if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag) == -1)
                    411:                        err = -1;
                    412:                xfree(abs_dst);
                    413:                abs_dst = NULL;
                    414:        }
                    415:
                    416: out:
                    417:        xfree(abs_src);
                    418:        if (abs_dst)
                    419:                xfree(abs_dst);
                    420:        globfree(&g);
                    421:        return(err);
                    422: }
                    423:
1.22.2.5! miod      424: static int
1.22.2.3  jason     425: process_put(int in, int out, char *src, char *dst, char *pwd, int pflag)
                    426: {
                    427:        char *tmp_dst = NULL;
                    428:        char *abs_dst = NULL;
                    429:        char *tmp;
                    430:        glob_t g;
                    431:        int err = 0;
                    432:        int i;
                    433:
                    434:        if (dst) {
                    435:                tmp_dst = xstrdup(dst);
                    436:                tmp_dst = make_absolute(tmp_dst, pwd);
                    437:        }
                    438:
                    439:        memset(&g, 0, sizeof(g));
                    440:        debug3("Looking up %s", src);
                    441:        if (glob(src, 0, NULL, &g)) {
                    442:                error("File \"%s\" not found.", src);
                    443:                err = -1;
                    444:                goto out;
                    445:        }
                    446:
                    447:        /* Only one match, dst may be file, directory or unspecified */
                    448:        if (g.gl_pathv[0] && g.gl_matchc == 1) {
                    449:                if (tmp_dst) {
                    450:                        /* If directory specified, append filename */
                    451:                        if (remote_is_dir(in, out, tmp_dst)) {
                    452:                                if (infer_path(g.gl_pathv[0], &tmp)) {
                    453:                                        err = 1;
                    454:                                        goto out;
                    455:                                }
                    456:                                abs_dst = path_append(tmp_dst, tmp);
                    457:                                xfree(tmp);
                    458:                        } else
                    459:                                abs_dst = xstrdup(tmp_dst);
1.22.2.4  jason     460:                } else {
                    461:                        if (infer_path(g.gl_pathv[0], &abs_dst)) {
                    462:                                err = -1;
                    463:                                goto out;
                    464:                        }
                    465:                        abs_dst = make_absolute(abs_dst, pwd);
1.22.2.3  jason     466:                }
                    467:                printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst);
                    468:                err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag);
                    469:                goto out;
                    470:        }
                    471:
                    472:        /* Multiple matches, dst may be directory or unspecified */
                    473:        if (tmp_dst && !remote_is_dir(in, out, tmp_dst)) {
                    474:                error("Multiple files match, but \"%s\" is not a directory",
                    475:                    tmp_dst);
                    476:                err = -1;
                    477:                goto out;
                    478:        }
                    479:
                    480:        for(i = 0; g.gl_pathv[i]; i++) {
                    481:                if (infer_path(g.gl_pathv[i], &tmp)) {
                    482:                        err = -1;
                    483:                        goto out;
                    484:                }
                    485:                if (tmp_dst) {
                    486:                        abs_dst = path_append(tmp_dst, tmp);
                    487:                        xfree(tmp);
                    488:                } else
                    489:                        abs_dst = make_absolute(tmp, pwd);
                    490:
                    491:                printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
                    492:                if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag) == -1)
                    493:                        err = -1;
1.1       djm       494:        }
                    495:
1.22.2.3  jason     496: out:
                    497:        if (abs_dst)
                    498:                xfree(abs_dst);
                    499:        if (tmp_dst)
                    500:                xfree(tmp_dst);
                    501:        return(err);
1.1       djm       502: }
                    503:
1.22.2.5! miod      504: static int
1.1       djm       505: parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
                    506:     char **path1, char **path2)
                    507: {
                    508:        const char *cmd, *cp = *cpp;
1.21      stevesk   509:        char *cp2;
1.4       markus    510:        int base = 0;
1.21      stevesk   511:        long l;
1.1       djm       512:        int i, cmdnum;
                    513:
                    514:        /* Skip leading whitespace */
                    515:        cp = cp + strspn(cp, WHITESPACE);
                    516:
                    517:        /* Ignore blank lines */
                    518:        if (!*cp)
                    519:                return(-1);
                    520:
                    521:        /* Figure out which command we have */
                    522:        for(i = 0; cmds[i].c; i++) {
                    523:                int cmdlen = strlen(cmds[i].c);
                    524:
                    525:                /* Check for command followed by whitespace */
                    526:                if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
                    527:                    strchr(WHITESPACE, cp[cmdlen])) {
                    528:                        cp += cmdlen;
                    529:                        cp = cp + strspn(cp, WHITESPACE);
                    530:                        break;
                    531:                }
                    532:        }
                    533:        cmdnum = cmds[i].n;
                    534:        cmd = cmds[i].c;
                    535:
                    536:        /* Special case */
                    537:        if (*cp == '!') {
                    538:                cp++;
                    539:                cmdnum = I_SHELL;
                    540:        } else if (cmdnum == -1) {
                    541:                error("Invalid command.");
                    542:                return(-1);
                    543:        }
                    544:
                    545:        /* Get arguments and parse flags */
                    546:        *pflag = *n_arg = 0;
                    547:        *path1 = *path2 = NULL;
                    548:        switch (cmdnum) {
                    549:        case I_GET:
                    550:        case I_PUT:
                    551:                if (parse_getput_flags(&cp, pflag))
                    552:                        return(-1);
                    553:                /* Get first pathname (mandatory) */
                    554:                if (get_pathname(&cp, path1))
                    555:                        return(-1);
                    556:                if (*path1 == NULL) {
                    557:                        error("You must specify at least one path after a "
                    558:                            "%s command.", cmd);
                    559:                        return(-1);
                    560:                }
                    561:                /* Try to get second pathname (optional) */
                    562:                if (get_pathname(&cp, path2))
                    563:                        return(-1);
                    564:                break;
                    565:        case I_RENAME:
1.22.2.3  jason     566:        case I_SYMLINK:
1.1       djm       567:                if (get_pathname(&cp, path1))
                    568:                        return(-1);
                    569:                if (get_pathname(&cp, path2))
                    570:                        return(-1);
                    571:                if (!*path1 || !*path2) {
                    572:                        error("You must specify two paths after a %s "
                    573:                            "command.", cmd);
                    574:                        return(-1);
                    575:                }
                    576:                break;
                    577:        case I_RM:
                    578:        case I_MKDIR:
                    579:        case I_RMDIR:
                    580:        case I_CHDIR:
                    581:        case I_LCHDIR:
                    582:        case I_LMKDIR:
                    583:                /* Get pathname (mandatory) */
                    584:                if (get_pathname(&cp, path1))
                    585:                        return(-1);
                    586:                if (*path1 == NULL) {
1.3       stevesk   587:                        error("You must specify a path after a %s command.",
1.1       djm       588:                            cmd);
                    589:                        return(-1);
                    590:                }
                    591:                break;
                    592:        case I_LS:
                    593:                /* Path is optional */
                    594:                if (get_pathname(&cp, path1))
                    595:                        return(-1);
                    596:                break;
                    597:        case I_LLS:
                    598:        case I_SHELL:
                    599:                /* Uses the rest of the line */
                    600:                break;
                    601:        case I_LUMASK:
1.21      stevesk   602:                base = 8;
1.1       djm       603:        case I_CHMOD:
1.4       markus    604:                base = 8;
1.1       djm       605:        case I_CHOWN:
                    606:        case I_CHGRP:
                    607:                /* Get numeric arg (mandatory) */
1.21      stevesk   608:                l = strtol(cp, &cp2, base);
                    609:                if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
                    610:                    errno == ERANGE) || l < 0) {
1.1       djm       611:                        error("You must supply a numeric argument "
                    612:                            "to the %s command.", cmd);
                    613:                        return(-1);
                    614:                }
1.21      stevesk   615:                cp = cp2;
                    616:                *n_arg = l;
                    617:                if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
                    618:                        break;
                    619:                if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
1.1       djm       620:                        error("You must supply a numeric argument "
                    621:                            "to the %s command.", cmd);
                    622:                        return(-1);
                    623:                }
                    624:                cp += strspn(cp, WHITESPACE);
                    625:
                    626:                /* Get pathname (mandatory) */
                    627:                if (get_pathname(&cp, path1))
                    628:                        return(-1);
                    629:                if (*path1 == NULL) {
1.3       stevesk   630:                        error("You must specify a path after a %s command.",
1.1       djm       631:                            cmd);
                    632:                        return(-1);
                    633:                }
                    634:                break;
                    635:        case I_QUIT:
                    636:        case I_PWD:
                    637:        case I_LPWD:
                    638:        case I_HELP:
1.22.2.3  jason     639:        case I_VERSION:
1.1       djm       640:                break;
                    641:        default:
                    642:                fatal("Command not implemented");
                    643:        }
                    644:
                    645:        *cpp = cp;
                    646:        return(cmdnum);
                    647: }
                    648:
1.22.2.5! miod      649: static int
1.1       djm       650: parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
                    651: {
1.8       provos    652:        char *path1, *path2, *tmp;
1.22.2.3  jason     653:        int pflag, cmdnum, i;
1.1       djm       654:        unsigned long n_arg;
                    655:        Attrib a, *aa;
1.22.2.3  jason     656:        char path_buf[MAXPATHLEN];
                    657:        int err = 0;
                    658:        glob_t g;
1.1       djm       659:
                    660:        path1 = path2 = NULL;
                    661:        cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
                    662:
1.22.2.3  jason     663:        memset(&g, 0, sizeof(g));
                    664:
1.1       djm       665:        /* Perform command */
                    666:        switch (cmdnum) {
                    667:        case -1:
                    668:                break;
                    669:        case I_GET:
1.22.2.3  jason     670:                err = process_get(in, out, path1, path2, *pwd, pflag);
1.1       djm       671:                break;
                    672:        case I_PUT:
1.22.2.3  jason     673:                err = process_put(in, out, path1, path2, *pwd, pflag);
1.22.2.4  jason     674:                break;
                    675:        case I_RENAME:
1.1       djm       676:                path1 = make_absolute(path1, *pwd);
                    677:                path2 = make_absolute(path2, *pwd);
1.22.2.3  jason     678:                err = do_rename(in, out, path1, path2);
                    679:                break;
                    680:        case I_SYMLINK:
                    681:                if (version < 3) {
                    682:                        error("The server (version %d) does not support "
                    683:                            "this operation", version);
                    684:                        err = -1;
                    685:                } else {
                    686:                        path2 = make_absolute(path2, *pwd);
                    687:                        err = do_symlink(in, out, path1, path2);
                    688:                }
1.1       djm       689:                break;
                    690:        case I_RM:
                    691:                path1 = make_absolute(path1, *pwd);
1.22.2.3  jason     692:                remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                    693:                for(i = 0; g.gl_pathv[i]; i++) {
                    694:                        printf("Removing %s\n", g.gl_pathv[i]);
                    695:                        if (do_rm(in, out, g.gl_pathv[i]) == -1)
                    696:                                err = -1;
                    697:                }
1.1       djm       698:                break;
                    699:        case I_MKDIR:
                    700:                path1 = make_absolute(path1, *pwd);
                    701:                attrib_clear(&a);
                    702:                a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
                    703:                a.perm = 0777;
1.22.2.3  jason     704:                err = do_mkdir(in, out, path1, &a);
1.1       djm       705:                break;
                    706:        case I_RMDIR:
                    707:                path1 = make_absolute(path1, *pwd);
1.22.2.3  jason     708:                err = do_rmdir(in, out, path1);
1.1       djm       709:                break;
                    710:        case I_CHDIR:
                    711:                path1 = make_absolute(path1, *pwd);
1.22.2.3  jason     712:                if ((tmp = do_realpath(in, out, path1)) == NULL) {
                    713:                        err = 1;
1.11      markus    714:                        break;
1.22.2.3  jason     715:                }
                    716:                if ((aa = do_stat(in, out, tmp, 0)) == NULL) {
1.11      markus    717:                        xfree(tmp);
1.22.2.3  jason     718:                        err = 1;
1.11      markus    719:                        break;
                    720:                }
1.9       djm       721:                if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
                    722:                        error("Can't change directory: Can't check target");
                    723:                        xfree(tmp);
1.22.2.3  jason     724:                        err = 1;
1.9       djm       725:                        break;
                    726:                }
                    727:                if (!S_ISDIR(aa->perm)) {
                    728:                        error("Can't change directory: \"%s\" is not "
                    729:                            "a directory", tmp);
                    730:                        xfree(tmp);
1.22.2.3  jason     731:                        err = 1;
1.9       djm       732:                        break;
                    733:                }
1.11      markus    734:                xfree(*pwd);
                    735:                *pwd = tmp;
1.1       djm       736:                break;
                    737:        case I_LS:
1.12      djm       738:                if (!path1) {
                    739:                        do_ls(in, out, *pwd);
                    740:                        break;
                    741:                }
1.1       djm       742:                path1 = make_absolute(path1, *pwd);
1.12      djm       743:                if ((tmp = do_realpath(in, out, path1)) == NULL)
                    744:                        break;
                    745:                xfree(path1);
                    746:                path1 = tmp;
1.22.2.3  jason     747:                if ((aa = do_stat(in, out, path1, 0)) == NULL)
1.12      djm       748:                        break;
1.22.2.3  jason     749:                if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1.12      djm       750:                    !S_ISDIR(aa->perm)) {
                    751:                        error("Can't ls: \"%s\" is not a directory", path1);
                    752:                        break;
                    753:                }
                    754:                do_ls(in, out, path1);
1.1       djm       755:                break;
                    756:        case I_LCHDIR:
1.22.2.3  jason     757:                if (chdir(path1) == -1) {
1.1       djm       758:                        error("Couldn't change local directory to "
                    759:                            "\"%s\": %s", path1, strerror(errno));
1.22.2.3  jason     760:                        err = 1;
                    761:                }
1.1       djm       762:                break;
                    763:        case I_LMKDIR:
1.22.2.3  jason     764:                if (mkdir(path1, 0777) == -1) {
1.17      stevesk   765:                        error("Couldn't create local directory "
1.1       djm       766:                            "\"%s\": %s", path1, strerror(errno));
1.22.2.3  jason     767:                        err = 1;
                    768:                }
1.1       djm       769:                break;
                    770:        case I_LLS:
                    771:                local_do_ls(cmd);
                    772:                break;
                    773:        case I_SHELL:
                    774:                local_do_shell(cmd);
                    775:                break;
                    776:        case I_LUMASK:
                    777:                umask(n_arg);
1.21      stevesk   778:                printf("Local umask: %03lo\n", n_arg);
1.1       djm       779:                break;
                    780:        case I_CHMOD:
                    781:                path1 = make_absolute(path1, *pwd);
                    782:                attrib_clear(&a);
                    783:                a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
                    784:                a.perm = n_arg;
1.22.2.3  jason     785:                remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                    786:                for(i = 0; g.gl_pathv[i]; i++) {
                    787:                        printf("Changing mode on %s\n", g.gl_pathv[i]);
                    788:                        do_setstat(in, out, g.gl_pathv[i], &a);
                    789:                }
1.5       stevesk   790:                break;
1.1       djm       791:        case I_CHOWN:
                    792:                path1 = make_absolute(path1, *pwd);
1.22.2.3  jason     793:                remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                    794:                for(i = 0; g.gl_pathv[i]; i++) {
                    795:                        if (!(aa = do_stat(in, out, g.gl_pathv[i], 0)))
                    796:                                continue;
                    797:                        if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
                    798:                                error("Can't get current ownership of "
                    799:                                    "remote file \"%s\"", g.gl_pathv[i]);
                    800:                                continue;
                    801:                        }
                    802:                        printf("Changing owner on %s\n", g.gl_pathv[i]);
                    803:                        aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
                    804:                        aa->uid = n_arg;
                    805:                        do_setstat(in, out, g.gl_pathv[i], aa);
1.1       djm       806:                }
                    807:                break;
                    808:        case I_CHGRP:
                    809:                path1 = make_absolute(path1, *pwd);
1.22.2.3  jason     810:                remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                    811:                for(i = 0; g.gl_pathv[i]; i++) {
                    812:                        if (!(aa = do_stat(in, out, g.gl_pathv[i], 0)))
                    813:                                continue;
                    814:                        if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
                    815:                                error("Can't get current ownership of "
                    816:                                    "remote file \"%s\"", g.gl_pathv[i]);
                    817:                                continue;
                    818:                        }
                    819:                        printf("Changing group on %s\n", g.gl_pathv[i]);
                    820:                        aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
                    821:                        aa->gid = n_arg;
                    822:                        do_setstat(in, out, g.gl_pathv[i], aa);
1.1       djm       823:                }
                    824:                break;
                    825:        case I_PWD:
                    826:                printf("Remote working directory: %s\n", *pwd);
                    827:                break;
                    828:        case I_LPWD:
                    829:                if (!getcwd(path_buf, sizeof(path_buf)))
1.22.2.3  jason     830:                        error("Couldn't get local cwd: %s",
1.1       djm       831:                            strerror(errno));
                    832:                else
                    833:                        printf("Local working directory: %s\n",
                    834:                            path_buf);
                    835:                break;
                    836:        case I_QUIT:
                    837:                return(-1);
                    838:        case I_HELP:
                    839:                help();
                    840:                break;
1.22.2.3  jason     841:        case I_VERSION:
                    842:                printf("SFTP protocol version %d\n", version);
                    843:                break;
1.1       djm       844:        default:
                    845:                fatal("%d is not implemented", cmdnum);
                    846:        }
                    847:
1.22.2.3  jason     848:        if (g.gl_pathc)
                    849:                globfree(&g);
1.1       djm       850:        if (path1)
                    851:                xfree(path1);
                    852:        if (path2)
                    853:                xfree(path2);
1.22.2.3  jason     854:
                    855:        /* If an error occurs in batch mode we should abort. */
                    856:        if (infile != stdin && err > 0)
                    857:                return -1;
                    858:
1.1       djm       859:        return(0);
                    860: }
                    861:
                    862: void
1.22.2.4  jason     863: interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1.1       djm       864: {
                    865:        char *pwd;
1.22.2.4  jason     866:        char *dir = NULL;
1.1       djm       867:        char cmd[2048];
                    868:
1.22.2.3  jason     869:        version = do_init(fd_in, fd_out);
                    870:        if (version == -1)
                    871:                fatal("Couldn't initialise connection to server");
                    872:
1.1       djm       873:        pwd = do_realpath(fd_in, fd_out, ".");
                    874:        if (pwd == NULL)
                    875:                fatal("Need cwd");
                    876:
1.22.2.4  jason     877:        if (file1 != NULL) {
                    878:                dir = xstrdup(file1);
                    879:                dir = make_absolute(dir, pwd);
                    880:
                    881:                if (remote_is_dir(fd_in, fd_out, dir) && file2 == NULL) {
                    882:                        printf("Changing to: %s\n", dir);
                    883:                        snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
                    884:                        parse_dispatch_command(fd_in, fd_out, cmd, &pwd);
                    885:                } else {
                    886:                        if (file2 == NULL)
                    887:                                snprintf(cmd, sizeof cmd, "get %s", dir);
                    888:                        else
                    889:                                snprintf(cmd, sizeof cmd, "get %s %s", dir,
                    890:                                    file2);
                    891:
                    892:                        parse_dispatch_command(fd_in, fd_out, cmd, &pwd);
                    893:                        return;
                    894:                }
                    895:        }
1.14      stevesk   896:        setvbuf(stdout, NULL, _IOLBF, 0);
1.22.2.3  jason     897:        setvbuf(infile, NULL, _IOLBF, 0);
1.1       djm       898:
                    899:        for(;;) {
                    900:                char *cp;
                    901:
                    902:                printf("sftp> ");
                    903:
                    904:                /* XXX: use libedit */
1.22.2.3  jason     905:                if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1.1       djm       906:                        printf("\n");
                    907:                        break;
1.22.2.3  jason     908:                } else if (infile != stdin) /* Bluff typing */
                    909:                        printf("%s", cmd);
                    910:
1.1       djm       911:                cp = strrchr(cmd, '\n');
                    912:                if (cp)
                    913:                        *cp = '\0';
1.22.2.3  jason     914:
1.1       djm       915:                if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
                    916:                        break;
                    917:        }
                    918:        xfree(pwd);
                    919: }