[BACK]Return to cmds.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / ftpd

Annotation of src/libexec/ftpd/cmds.c, Revision 1.25

1.25    ! martin      1: /*     $NetBSD: cmds.c,v 1.24 2006/02/01 14:20:12 christos Exp $       */
1.1       lukem       2:
                      3: /*
1.23      lukem       4:  * Copyright (c) 1999-2004 The NetBSD Foundation, Inc.
1.1       lukem       5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Luke Mewburn.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     21:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     22:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     23:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     25:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     26:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     27:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     28:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     29:  * POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
                     32: /*
                     33:  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
                     34:  *     The Regents of the University of California.  All rights reserved.
                     35:  *
                     36:  * Redistribution and use in source and binary forms, with or without
                     37:  * modification, are permitted provided that the following conditions
                     38:  * are met:
                     39:  * 1. Redistributions of source code must retain the above copyright
                     40:  *    notice, this list of conditions and the following disclaimer.
                     41:  * 2. Redistributions in binary form must reproduce the above copyright
                     42:  *    notice, this list of conditions and the following disclaimer in the
                     43:  *    documentation and/or other materials provided with the distribution.
1.22      agc        44:  * 3. Neither the name of the University nor the names of its contributors
1.1       lukem      45:  *    may be used to endorse or promote products derived from this software
                     46:  *    without specific prior written permission.
                     47:  *
                     48:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     49:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     50:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     51:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     52:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     53:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     54:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     55:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     56:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     57:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     58:  * SUCH DAMAGE.
                     59:  */
                     60:
                     61: /*
                     62:  * Copyright (C) 1997 and 1998 WIDE Project.
                     63:  * All rights reserved.
                     64:  *
                     65:  * Redistribution and use in source and binary forms, with or without
                     66:  * modification, are permitted provided that the following conditions
                     67:  * are met:
                     68:  * 1. Redistributions of source code must retain the above copyright
                     69:  *    notice, this list of conditions and the following disclaimer.
                     70:  * 2. Redistributions in binary form must reproduce the above copyright
                     71:  *    notice, this list of conditions and the following disclaimer in the
                     72:  *    documentation and/or other materials provided with the distribution.
                     73:  * 3. Neither the name of the project nor the names of its contributors
                     74:  *    may be used to endorse or promote products derived from this software
                     75:  *    without specific prior written permission.
                     76:  *
                     77:  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
                     78:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     79:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     80:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
                     81:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     82:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     83:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     84:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     85:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     86:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     87:  * SUCH DAMAGE.
                     88:  */
                     89:
                     90:
                     91: #include <sys/cdefs.h>
                     92: #ifndef lint
1.25    ! martin     93: __RCSID("$NetBSD: cmds.c,v 1.24 2006/02/01 14:20:12 christos Exp $");
1.1       lukem      94: #endif /* not lint */
                     95:
                     96: #include <sys/param.h>
                     97: #include <sys/stat.h>
                     98:
                     99: #include <arpa/ftp.h>
                    100:
                    101: #include <dirent.h>
                    102: #include <errno.h>
                    103: #include <stdio.h>
                    104: #include <stdlib.h>
                    105: #include <string.h>
                    106: #include <tzfile.h>
                    107: #include <unistd.h>
1.19      itojun    108: #include <ctype.h>
1.2       explorer  109:
                    110: #ifdef KERBEROS5
                    111: #include <krb5/krb5.h>
                    112: #endif
1.1       lukem     113:
                    114: #include "extern.h"
                    115:
1.16      lukem     116: typedef enum {
                    117:        FE_MLSD         = 1<<0,         /* if op is MLSD (MLST otherwise ) */
                    118:        FE_ISCURDIR     = 1<<1,         /* if name is the current directory */
                    119: } factflag_t;
                    120:
1.1       lukem     121: typedef struct {
                    122:        const char      *path;          /* full pathname */
                    123:        const char      *display;       /* name to display */
                    124:        struct stat     *stat;          /* stat of path */
                    125:        struct stat     *pdirstat;      /* stat of path's parent dir */
1.16      lukem     126:        factflag_t       flags;         /* flags */
1.1       lukem     127: } factelem;
                    128:
                    129: static void    ack(const char *);
1.3       lukem     130: static void    base64_encode(const char *, size_t, char *, int);
1.1       lukem     131: static void    fact_type(const char *, FILE *, factelem *);
                    132: static void    fact_size(const char *, FILE *, factelem *);
                    133: static void    fact_modify(const char *, FILE *, factelem *);
                    134: static void    fact_perm(const char *, FILE *, factelem *);
                    135: static void    fact_unique(const char *, FILE *, factelem *);
1.3       lukem     136: static int     matchgroup(gid_t);
1.1       lukem     137: static void    mlsname(FILE *, factelem *);
                    138: static void    replydirname(const char *, const char *);
                    139:
                    140: struct ftpfact {
                    141:        const char       *name;         /* name of fact */
                    142:        int               enabled;      /* if fact is enabled */
                    143:        void            (*display)(const char *, FILE *, factelem *);
                    144:                                        /* function to display fact */
                    145: };
                    146:
                    147: struct ftpfact facttab[] = {
                    148:        { "Type",       1, fact_type },
1.3       lukem     149: #define        FACT_TYPE 0
1.1       lukem     150:        { "Size",       1, fact_size },
                    151:        { "Modify",     1, fact_modify },
                    152:        { "Perm",       1, fact_perm },
                    153:        { "Unique",     1, fact_unique },
                    154:        /* "Create" */
                    155:        /* "Lang" */
                    156:        /* "Media-Type" */
                    157:        /* "CharSet" */
                    158: };
                    159:
1.4       lukem     160: #define FACTTABSIZE    (sizeof(facttab) / sizeof(struct ftpfact))
                    161:
1.20      manu      162: static char cached_path[MAXPATHLEN + 1] = "/";
                    163: static void discover_path(char *, const char *);
1.1       lukem     164:
                    165: void
                    166: cwd(const char *path)
                    167: {
                    168:
                    169:        if (chdir(path) < 0)
                    170:                perror_reply(550, path);
                    171:        else {
                    172:                show_chdir_messages(250);
                    173:                ack("CWD");
1.20      manu      174:                if (getcwd(cached_path, MAXPATHLEN) == NULL) {
                    175:                        discover_path(cached_path, path);
                    176:                }
1.1       lukem     177:        }
                    178: }
                    179:
                    180: void
                    181: delete(const char *name)
                    182: {
                    183:        char *p = NULL;
                    184:
                    185:        if (remove(name) < 0) {
                    186:                p = strerror(errno);
                    187:                perror_reply(550, name);
                    188:        } else
                    189:                ack("DELE");
1.10      lukem     190:        logxfer("delete", -1, name, NULL, NULL, p);
1.1       lukem     191: }
                    192:
                    193: void
                    194: feat(void)
                    195: {
                    196:        int i;
                    197:
1.3       lukem     198:        reply(-211, "Features supported");
                    199:        cprintf(stdout, " MDTM\r\n");
                    200:        cprintf(stdout, " MLST ");
1.4       lukem     201:        for (i = 0; i < FACTTABSIZE; i++)
1.3       lukem     202:                cprintf(stdout, "%s%s;", facttab[i].name,
1.1       lukem     203:                    facttab[i].enabled ? "*" : "");
1.3       lukem     204:        cprintf(stdout, "\r\n");
                    205:        cprintf(stdout, " REST STREAM\r\n");
                    206:        cprintf(stdout, " SIZE\r\n");
                    207:        cprintf(stdout, " TVFS\r\n");
1.1       lukem     208:        reply(211,  "End");
                    209: }
                    210:
                    211: void
                    212: makedir(const char *name)
                    213: {
                    214:        char *p = NULL;
                    215:
                    216:        if (mkdir(name, 0777) < 0) {
                    217:                p = strerror(errno);
                    218:                perror_reply(550, name);
                    219:        } else
                    220:                replydirname(name, "directory created.");
1.10      lukem     221:        logxfer("mkdir", -1, name, NULL, NULL, p);
1.1       lukem     222: }
                    223:
                    224: void
                    225: mlsd(const char *path)
                    226: {
1.3       lukem     227:        struct dirent   *dp;
                    228:        struct stat      sb, pdirstat;
1.1       lukem     229:        factelem f;
1.3       lukem     230:        FILE    *dout;
                    231:        DIR     *dirp;
                    232:        char    name[MAXPATHLEN];
                    233:        int     hastypefact;
1.1       lukem     234:
1.3       lukem     235:        hastypefact = facttab[FACT_TYPE].enabled;
1.1       lukem     236:        if (path == NULL)
                    237:                path = ".";
                    238:        if (stat(path, &pdirstat) == -1) {
                    239:  mlsdperror:
                    240:                perror_reply(550, path);
                    241:                return;
                    242:        }
                    243:        if (! S_ISDIR(pdirstat.st_mode)) {
                    244:                errno = ENOTDIR;
1.3       lukem     245:                perror_reply(501, path);
                    246:                return;
1.1       lukem     247:        }
1.15      lukem     248:        if ((dirp = opendir(path)) == NULL)
                    249:                goto mlsdperror;
                    250:
1.1       lukem     251:        dout = dataconn("MLSD", (off_t)-1, "w");
                    252:        if (dout == NULL)
                    253:                return;
                    254:
1.16      lukem     255:        memset(&f, 0, sizeof(f));
1.1       lukem     256:        f.stat = &sb;
1.16      lukem     257:        f.flags |= FE_MLSD;
1.1       lukem     258:        while ((dp = readdir(dirp)) != NULL) {
                    259:                snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
                    260:                if (ISDOTDIR(dp->d_name)) {     /* special case curdir: */
1.3       lukem     261:                        if (! hastypefact)
                    262:                                continue;
                    263:                        f.pdirstat = NULL;      /*   require stat of parent */
1.1       lukem     264:                        f.display = path;       /*   set name to real name */
1.16      lukem     265:                        f.flags |= FE_ISCURDIR; /*   flag name is curdir */
1.1       lukem     266:                } else {
1.3       lukem     267:                        if (ISDOTDOTDIR(dp->d_name)) {
                    268:                                if (! hastypefact)
                    269:                                        continue;
                    270:                                f.pdirstat = NULL;
                    271:                        } else
                    272:                                f.pdirstat = &pdirstat; /* cache parent stat */
1.1       lukem     273:                        f.display = dp->d_name;
1.16      lukem     274:                        f.flags &= ~FE_ISCURDIR;
1.1       lukem     275:                }
1.3       lukem     276:                if (stat(name, &sb) == -1)
                    277:                        continue;
                    278:                f.path = name;
1.1       lukem     279:                mlsname(dout, &f);
                    280:        }
                    281:        (void)closedir(dirp);
                    282:
                    283:        if (ferror(dout) != 0)
                    284:                perror_reply(550, "Data connection");
                    285:        else
                    286:                reply(226, "MLSD complete.");
1.3       lukem     287:        closedataconn(dout);
1.1       lukem     288:        total_xfers_out++;
                    289:        total_xfers++;
                    290: }
                    291:
                    292: void
                    293: mlst(const char *path)
                    294: {
                    295:        struct stat sb;
                    296:        factelem f;
                    297:
                    298:        if (path == NULL)
                    299:                path = ".";
                    300:        if (stat(path, &sb) == -1) {
                    301:                perror_reply(550, path);
                    302:                return;
                    303:        }
1.3       lukem     304:        reply(-250, "MLST %s", path);
1.16      lukem     305:        memset(&f, 0, sizeof(f));
1.1       lukem     306:        f.path = path;
                    307:        f.display = path;
                    308:        f.stat = &sb;
                    309:        f.pdirstat = NULL;
1.3       lukem     310:        CPUTC(' ', stdout);
1.1       lukem     311:        mlsname(stdout, &f);
                    312:        reply(250, "End");
                    313: }
                    314:
                    315:
                    316: void
                    317: opts(const char *command)
                    318: {
                    319:        struct tab *c;
                    320:        char *ep;
                    321:
                    322:        if ((ep = strchr(command, ' ')) != NULL)
                    323:                *ep++ = '\0';
                    324:        c = lookup(cmdtab, command);
                    325:        if (c == NULL) {
1.18      darrenr   326:                reply(502, "Unknown command '%s'.", command);
1.1       lukem     327:                return;
                    328:        }
                    329:        if (! CMD_IMPLEMENTED(c)) {
1.17      darrenr   330:                reply(502, "%s command not implemented.", c->name);
1.1       lukem     331:                return;
                    332:        }
                    333:        if (! CMD_HAS_OPTIONS(c)) {
                    334:                reply(501, "%s command does not support persistent options.",
                    335:                    c->name);
                    336:                return;
                    337:        }
                    338:
                    339:                        /* special case: MLST */
                    340:        if (strcasecmp(command, "MLST") == 0) {
1.4       lukem     341:                int      enabled[FACTTABSIZE];
1.1       lukem     342:                int      i, onedone;
1.3       lukem     343:                size_t   len;
1.1       lukem     344:                char    *p;
                    345:
1.3       lukem     346:                for (i = 0; i < sizeof(enabled) / sizeof(int); i++)
                    347:                        enabled[i] = 0;
1.1       lukem     348:                if (ep == NULL || *ep == '\0')
                    349:                        goto displaymlstopts;
                    350:
                    351:                                /* don't like spaces, and need trailing ; */
1.3       lukem     352:                len = strlen(ep);
                    353:                if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') {
                    354:  badmlstopt:
1.1       lukem     355:                        reply(501, "Invalid MLST options");
                    356:                        return;
                    357:                }
1.3       lukem     358:                ep[len - 1] = '\0';
1.1       lukem     359:                while ((p = strsep(&ep, ";")) != NULL) {
1.3       lukem     360:                        if (*p == '\0')
                    361:                                goto badmlstopt;
1.4       lukem     362:                        for (i = 0; i < FACTTABSIZE; i++)
1.1       lukem     363:                                if (strcasecmp(p, facttab[i].name) == 0) {
1.3       lukem     364:                                        enabled[i] = 1;
1.1       lukem     365:                                        break;
                    366:                                }
                    367:                }
                    368:
                    369:  displaymlstopts:
1.4       lukem     370:                for (i = 0; i < FACTTABSIZE; i++)
1.3       lukem     371:                        facttab[i].enabled = enabled[i];
                    372:                cprintf(stdout, "200 MLST OPTS");
1.4       lukem     373:                for (i = onedone = 0; i < FACTTABSIZE; i++) {
1.1       lukem     374:                        if (facttab[i].enabled) {
1.3       lukem     375:                                cprintf(stdout, "%s%s;", onedone ? "" : " ",
1.1       lukem     376:                                    facttab[i].name);
                    377:                                onedone++;
                    378:                        }
                    379:                }
1.3       lukem     380:                cprintf(stdout, "\r\n");
                    381:                fflush(stdout);
1.1       lukem     382:                return;
                    383:        }
                    384:
                    385:                        /* default cases */
                    386:        if (ep != NULL && *ep != '\0')
1.24      christos  387:                REASSIGN(c->options, ftpd_strdup(ep));
1.1       lukem     388:        if (c->options != NULL)
                    389:                reply(200, "Options for %s are '%s'.", c->name,
                    390:                    c->options);
                    391:        else
                    392:                reply(200, "No options defined for %s.", c->name);
                    393: }
                    394:
                    395: void
                    396: pwd(void)
                    397: {
                    398:        char path[MAXPATHLEN];
                    399:
1.20      manu      400:        if (getcwd(path, sizeof(path) - 1) == NULL) {
                    401:                if (chdir(cached_path) < 0) {
                    402:                        reply(550, "Can't get the current directory: %s.",
                    403:                            strerror(errno));
                    404:                        return;
                    405:                }
                    406:                (void)strlcpy(path, cached_path, MAXPATHLEN);
                    407:        }
                    408:        replydirname(path, "is the current directory.");
1.1       lukem     409: }
                    410:
                    411: void
                    412: removedir(const char *name)
                    413: {
                    414:        char *p = NULL;
                    415:
                    416:        if (rmdir(name) < 0) {
                    417:                p = strerror(errno);
                    418:                perror_reply(550, name);
                    419:        } else
                    420:                ack("RMD");
1.10      lukem     421:        logxfer("rmdir", -1, name, NULL, NULL, p);
1.1       lukem     422: }
                    423:
                    424: char *
                    425: renamefrom(const char *name)
                    426: {
                    427:        struct stat st;
                    428:
                    429:        if (stat(name, &st) < 0) {
                    430:                perror_reply(550, name);
                    431:                return (NULL);
                    432:        }
                    433:        reply(350, "File exists, ready for destination name");
1.24      christos  434:        return (ftpd_strdup(name));
1.1       lukem     435: }
                    436:
                    437: void
                    438: renamecmd(const char *from, const char *to)
                    439: {
                    440:        char *p = NULL;
                    441:
                    442:        if (rename(from, to) < 0) {
                    443:                p = strerror(errno);
                    444:                perror_reply(550, "rename");
                    445:        } else
                    446:                ack("RNTO");
1.10      lukem     447:        logxfer("rename", -1, from, to, NULL, p);
1.1       lukem     448: }
                    449:
                    450: void
                    451: sizecmd(const char *filename)
                    452: {
                    453:        switch (type) {
                    454:        case TYPE_L:
1.10      lukem     455:        case TYPE_I:
                    456:            {
1.1       lukem     457:                struct stat stbuf;
                    458:                if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
                    459:                        reply(550, "%s: not a plain file.", filename);
                    460:                else
1.7       lukem     461:                        reply(213, ULLF, (ULLT)stbuf.st_size);
1.10      lukem     462:                break;
                    463:            }
                    464:        case TYPE_A:
                    465:            {
1.1       lukem     466:                FILE *fin;
                    467:                int c;
                    468:                off_t count;
                    469:                struct stat stbuf;
                    470:                fin = fopen(filename, "r");
                    471:                if (fin == NULL) {
                    472:                        perror_reply(550, filename);
                    473:                        return;
                    474:                }
                    475:                if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
                    476:                        reply(550, "%s: not a plain file.", filename);
                    477:                        (void) fclose(fin);
                    478:                        return;
                    479:                }
1.14      itojun    480:                if (stbuf.st_size > 10240) {
                    481:                        reply(550, "%s: file too large for SIZE.", filename);
                    482:                        (void) fclose(fin);
                    483:                        return;
                    484:                }
1.1       lukem     485:
                    486:                count = 0;
1.14      itojun    487:                while((c = getc(fin)) != EOF) {
1.1       lukem     488:                        if (c == '\n')  /* will get expanded to \r\n */
                    489:                                count++;
                    490:                        count++;
                    491:                }
                    492:                (void) fclose(fin);
                    493:
1.7       lukem     494:                reply(213, LLF, (LLT)count);
1.10      lukem     495:                break;
                    496:            }
1.1       lukem     497:        default:
                    498:                reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
                    499:        }
                    500: }
                    501:
                    502: void
                    503: statfilecmd(const char *filename)
                    504: {
                    505:        FILE *fin;
                    506:        int c;
1.19      itojun    507:        int atstart;
1.1       lukem     508:        char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
                    509:
                    510:        argv[2] = (char *)filename;
                    511:        fin = ftpd_popen(argv, "r", STDOUT_FILENO);
1.3       lukem     512:        reply(-211, "status of %s:", filename);
                    513: /* XXX: use fgetln() or fparseln() here? */
1.19      itojun    514:        atstart = 1;
1.1       lukem     515:        while ((c = getc(fin)) != EOF) {
                    516:                if (c == '\n') {
                    517:                        if (ferror(stdout)){
                    518:                                perror_reply(421, "control connection");
                    519:                                (void) ftpd_pclose(fin);
                    520:                                dologout(1);
                    521:                                /* NOTREACHED */
                    522:                        }
                    523:                        if (ferror(fin)) {
                    524:                                perror_reply(551, filename);
                    525:                                (void) ftpd_pclose(fin);
                    526:                                return;
                    527:                        }
1.3       lukem     528:                        CPUTC('\r', stdout);
1.1       lukem     529:                }
1.19      itojun    530:                if (atstart && isdigit(c))
                    531:                        CPUTC(' ', stdout);
1.3       lukem     532:                CPUTC(c, stdout);
1.19      itojun    533:                atstart = (c == '\n');
1.1       lukem     534:        }
                    535:        (void) ftpd_pclose(fin);
                    536:        reply(211, "End of Status");
                    537: }
                    538:
                    539: /* -- */
                    540:
                    541: static void
                    542: ack(const char *s)
                    543: {
                    544:
                    545:        reply(250, "%s command successful.", s);
                    546: }
                    547:
1.3       lukem     548: /*
                    549:  * Encode len bytes starting at clear using base64 encoding into encoded,
                    550:  * which should be at least ((len + 2) * 4 / 3 + 1) in size.
1.6       lukem     551:  * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary
                    552:  * with `='.
1.3       lukem     553:  */
                    554: static void
                    555: base64_encode(const char *clear, size_t len, char *encoded, int nulterm)
                    556: {
1.5       lukem     557:        static const char base64[] =
1.3       lukem     558:            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.5       lukem     559:        const char *c;
                    560:        char    *e, termchar;
1.3       lukem     561:        int      i;
                    562:
1.6       lukem     563:                        /* determine whether to pad with '=' or NUL terminate */
1.5       lukem     564:        termchar = nulterm ? '\0' : '=';
                    565:        c = clear;
                    566:        e = encoded;
1.6       lukem     567:                        /* convert all but last 2 bytes */
                    568:        for (i = len; i > 2; i -= 3, c += 3) {
                    569:                *e++ = base64[(c[0] >> 2) & 0x3f];
                    570:                *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)];
                    571:                *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)];
                    572:                *e++ = base64[(c[2]) & 0x3f];
                    573:        }
                    574:                        /* handle slop at end */
                    575:        if (i > 0) {
                    576:                *e++ = base64[(c[0] >> 2) & 0x3f];
1.5       lukem     577:                *e++ = base64[((c[0] << 4) & 0x30) |
1.6       lukem     578:                     (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)];
                    579:                *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar;
                    580:                *e++ = termchar;
1.5       lukem     581:        }
                    582:        *e = '\0';
1.3       lukem     583: }
                    584:
1.1       lukem     585: static void
                    586: fact_modify(const char *fact, FILE *fd, factelem *fe)
                    587: {
                    588:        struct tm *t;
                    589:
                    590:        t = gmtime(&(fe->stat->st_mtime));
1.3       lukem     591:        cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
1.1       lukem     592:            TM_YEAR_BASE + t->tm_year,
                    593:            t->tm_mon+1, t->tm_mday,
                    594:            t->tm_hour, t->tm_min, t->tm_sec);
                    595: }
                    596:
                    597: static void
                    598: fact_perm(const char *fact, FILE *fd, factelem *fe)
                    599: {
1.3       lukem     600:        int             rok, wok, xok, pdirwok;
1.1       lukem     601:        struct stat     *pdir;
                    602:
                    603:        if (fe->stat->st_uid == geteuid()) {
                    604:                rok = ((fe->stat->st_mode & S_IRUSR) != 0);
                    605:                wok = ((fe->stat->st_mode & S_IWUSR) != 0);
                    606:                xok = ((fe->stat->st_mode & S_IXUSR) != 0);
1.3       lukem     607:        } else if (matchgroup(fe->stat->st_gid)) {
1.1       lukem     608:                rok = ((fe->stat->st_mode & S_IRGRP) != 0);
                    609:                wok = ((fe->stat->st_mode & S_IWGRP) != 0);
                    610:                xok = ((fe->stat->st_mode & S_IXGRP) != 0);
                    611:        } else {
                    612:                rok = ((fe->stat->st_mode & S_IROTH) != 0);
                    613:                wok = ((fe->stat->st_mode & S_IWOTH) != 0);
                    614:                xok = ((fe->stat->st_mode & S_IXOTH) != 0);
                    615:        }
                    616:
1.3       lukem     617:        cprintf(fd, "%s=", fact);
1.1       lukem     618:
                    619:                        /*
                    620:                         * if parent info not provided, look it up, but
                    621:                         * only if the current class has modify rights,
                    622:                         * since we only need this info in such a case.
                    623:                         */
                    624:        pdir = fe->pdirstat;
1.8       lukem     625:        if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) {
1.1       lukem     626:                size_t          len;
                    627:                char            realdir[MAXPATHLEN], *p;
                    628:                struct stat     dir;
                    629:
                    630:                len = strlcpy(realdir, fe->path, sizeof(realdir));
                    631:                if (len < sizeof(realdir) - 4) {
                    632:                        if (S_ISDIR(fe->stat->st_mode))
                    633:                                strlcat(realdir, "/..", sizeof(realdir));
                    634:                        else {
                    635:                                        /* if has a /, move back to it */
                    636:                                        /* otherwise use '..' */
                    637:                                if ((p = strrchr(realdir, '/')) != NULL) {
                    638:                                        if (p == realdir)
                    639:                                                p++;
                    640:                                        *p = '\0';
                    641:                                } else
                    642:                                        strlcpy(realdir, "..", sizeof(realdir));
                    643:                        }
                    644:                        if (stat(realdir, &dir) == 0)
                    645:                                pdir = &dir;
                    646:                }
                    647:        }
                    648:        pdirwok = 0;
                    649:        if (pdir != NULL) {
                    650:                if (pdir->st_uid == geteuid())
                    651:                        pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
1.3       lukem     652:                else if (matchgroup(pdir->st_gid))
1.1       lukem     653:                        pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
                    654:                else
                    655:                        pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
                    656:        }
                    657:
                    658:                        /* 'a': can APPE to file */
1.8       lukem     659:        if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
1.3       lukem     660:                CPUTC('a', fd);
1.1       lukem     661:
                    662:                        /* 'c': can create or append to files in directory */
1.8       lukem     663:        if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3       lukem     664:                CPUTC('c', fd);
1.1       lukem     665:
                    666:                        /* 'd': can delete file or directory */
1.8       lukem     667:        if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) {
1.1       lukem     668:                int candel;
                    669:
                    670:                candel = 1;
                    671:                if (S_ISDIR(fe->stat->st_mode)) {
                    672:                        DIR *dirp;
                    673:                        struct dirent *dp;
                    674:
                    675:                        if ((dirp = opendir(fe->display)) == NULL)
                    676:                                candel = 0;
                    677:                        else {
                    678:                                while ((dp = readdir(dirp)) != NULL) {
                    679:                                        if (ISDOTDIR(dp->d_name) ||
                    680:                                            ISDOTDOTDIR(dp->d_name))
                    681:                                                continue;
                    682:                                        candel = 0;
                    683:                                        break;
                    684:                                }
                    685:                                closedir(dirp);
                    686:                        }
                    687:                }
                    688:                if (candel)
1.3       lukem     689:                        CPUTC('d', fd);
1.1       lukem     690:        }
                    691:
                    692:                        /* 'e': can enter directory */
                    693:        if (xok && S_ISDIR(fe->stat->st_mode))
1.3       lukem     694:                CPUTC('e', fd);
1.1       lukem     695:
                    696:                        /* 'f': can rename file or directory */
1.8       lukem     697:        if (pdirwok && CURCLASS_FLAGS_ISSET(modify))
1.3       lukem     698:                CPUTC('f', fd);
1.1       lukem     699:
                    700:                        /* 'l': can list directory */
                    701:        if (rok && xok && S_ISDIR(fe->stat->st_mode))
1.3       lukem     702:                CPUTC('l', fd);
1.1       lukem     703:
                    704:                        /* 'm': can create directory */
1.8       lukem     705:        if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3       lukem     706:                CPUTC('m', fd);
1.1       lukem     707:
                    708:                        /* 'p': can remove files in directory */
1.8       lukem     709:        if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3       lukem     710:                CPUTC('p', fd);
1.1       lukem     711:
                    712:                        /* 'r': can RETR file */
                    713:        if (rok && S_ISREG(fe->stat->st_mode))
1.3       lukem     714:                CPUTC('r', fd);
1.1       lukem     715:
                    716:                        /* 'w': can STOR file */
1.8       lukem     717:        if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
1.3       lukem     718:                CPUTC('w', fd);
1.1       lukem     719:
1.3       lukem     720:        CPUTC(';', fd);
1.1       lukem     721: }
                    722:
                    723: static void
                    724: fact_size(const char *fact, FILE *fd, factelem *fe)
                    725: {
                    726:
                    727:        if (S_ISREG(fe->stat->st_mode))
1.7       lukem     728:                cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size);
1.1       lukem     729: }
                    730:
                    731: static void
                    732: fact_type(const char *fact, FILE *fd, factelem *fe)
                    733: {
                    734:
1.3       lukem     735:        cprintf(fd, "%s=", fact);
1.1       lukem     736:        switch (fe->stat->st_mode & S_IFMT) {
                    737:        case S_IFDIR:
1.16      lukem     738:                if (fe->flags & FE_MLSD) {
                    739:                        if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display))
                    740:                                cprintf(fd, "cdir");
                    741:                        else if (ISDOTDOTDIR(fe->display))
                    742:                                cprintf(fd, "pdir");
                    743:                        else
                    744:                                cprintf(fd, "dir");
                    745:                } else {
1.3       lukem     746:                        cprintf(fd, "dir");
1.16      lukem     747:                }
1.1       lukem     748:                break;
                    749:        case S_IFREG:
1.3       lukem     750:                cprintf(fd, "file");
1.1       lukem     751:                break;
                    752:        case S_IFIFO:
1.3       lukem     753:                cprintf(fd, "OS.unix=fifo");
1.1       lukem     754:                break;
1.3       lukem     755:        case S_IFLNK:           /* XXX: probably a NO-OP with stat() */
                    756:                cprintf(fd, "OS.unix=slink");
1.1       lukem     757:                break;
                    758:        case S_IFSOCK:
1.3       lukem     759:                cprintf(fd, "OS.unix=socket");
1.1       lukem     760:                break;
                    761:        case S_IFBLK:
                    762:        case S_IFCHR:
1.3       lukem     763:                cprintf(fd, "OS.unix=%s-%d/%d",
1.1       lukem     764:                    S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
                    765:                    major(fe->stat->st_rdev), minor(fe->stat->st_rdev));
                    766:                break;
                    767:        default:
1.3       lukem     768:                cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
1.1       lukem     769:                break;
                    770:        }
1.3       lukem     771:        CPUTC(';', fd);
1.1       lukem     772: }
                    773:
                    774: static void
                    775: fact_unique(const char *fact, FILE *fd, factelem *fe)
                    776: {
1.5       lukem     777:        char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2];
                    778:        char tbuf[sizeof(dev_t) + sizeof(ino_t)];
1.1       lukem     779:
1.5       lukem     780:        memcpy(tbuf,
                    781:            (char *)&(fe->stat->st_dev), sizeof(dev_t));
                    782:        memcpy(tbuf + sizeof(dev_t),
                    783:            (char *)&(fe->stat->st_ino), sizeof(ino_t));
                    784:        base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1);
                    785:        cprintf(fd, "%s=%s;", fact, obuf);
1.1       lukem     786: }
                    787:
                    788: static int
1.3       lukem     789: matchgroup(gid_t gid)
1.1       lukem     790: {
                    791:        int     i;
                    792:
1.3       lukem     793:        for (i = 0; i < gidcount; i++)
1.1       lukem     794:                if (gid == gidlist[i])
                    795:                        return(1);
                    796:        return (0);
                    797: }
                    798:
                    799: static void
                    800: mlsname(FILE *fp, factelem *fe)
                    801: {
1.16      lukem     802:        char realfile[MAXPATHLEN];
1.21      erh       803:        int i, userf = 0;
1.1       lukem     804:
1.4       lukem     805:        for (i = 0; i < FACTTABSIZE; i++) {
1.1       lukem     806:                if (facttab[i].enabled)
                    807:                        (facttab[i].display)(facttab[i].name, fp, fe);
                    808:        }
1.16      lukem     809:        if ((fe->flags & FE_MLSD) &&
                    810:            !(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) {
                    811:                        /* if MLSD and not "." entry, display as-is */
                    812:                userf = 0;
                    813:        } else {
                    814:                        /* if MLST, or MLSD and "." entry, realpath(3) it */
                    815:                if (realpath(fe->display, realfile) != NULL)
                    816:                        userf = 1;
                    817:        }
                    818:        cprintf(fp, " %s\r\n", userf ? realfile : fe->display);
1.1       lukem     819: }
                    820:
                    821: static void
                    822: replydirname(const char *name, const char *message)
                    823: {
1.9       itojun    824:        char *p, *ep;
1.12      itojun    825:        char npath[MAXPATHLEN * 2];
1.1       lukem     826:
1.9       itojun    827:        p = npath;
                    828:        ep = &npath[sizeof(npath) - 1];
                    829:        while (*name) {
1.11      itojun    830:                if (*name == '"') {
                    831:                        if (ep - p < 2)
                    832:                                break;
1.9       itojun    833:                        *p++ = *name++;
                    834:                        *p++ = '"';
1.11      itojun    835:                } else {
                    836:                        if (ep - p < 1)
                    837:                                break;
1.9       itojun    838:                        *p++ = *name++;
1.11      itojun    839:                }
1.1       lukem     840:        }
1.9       itojun    841:        *p = '\0';
1.1       lukem     842:        reply(257, "\"%s\" %s", npath, message);
                    843: }
1.20      manu      844:
                    845: static void
                    846: discover_path(last_path, new_path)
                    847:        char *last_path;
                    848:        const char *new_path;
                    849: {
                    850:        char tp[MAXPATHLEN + 1] = "";
                    851:        char tq[MAXPATHLEN + 1] = "";
                    852:        char *cp;
                    853:        char *cq;
                    854:        int sz1, sz2;
                    855:        int nomorelink;
                    856:        struct stat st1, st2;
                    857:
                    858:        if (new_path[0] != '/') {
                    859:                (void)strlcpy(tp, last_path, MAXPATHLEN);
                    860:                (void)strlcat(tp, "/", MAXPATHLEN);
                    861:        }
                    862:        (void)strlcat(tp, new_path, MAXPATHLEN);
                    863:        (void)strlcat(tp, "/", MAXPATHLEN);
                    864:
                    865:        /*
                    866:         * resolve symlinks. A symlink may introduce another symlink, so we
                    867:         * loop trying to resolve symlinks until we don't find any of them.
                    868:         */
                    869:        do {
                    870:                /* Collapse any // into / */
                    871:                while ((cp = strstr(tp, "//")) != NULL)
                    872:                        (void)memmove(cp, cp + 1, strlen(cp) - 1 + 1);
                    873:
                    874:                /* Collapse any /./ into / */
                    875:                while ((cp = strstr(tp, "/./")) != NULL)
                    876:                        (void)memmove(cp, cp + 2, strlen(cp) - 2 + 1);
                    877:
                    878:                cp = tp;
                    879:                nomorelink = 1;
                    880:
                    881:                while ((cp = strstr(++cp, "/")) != NULL) {
                    882:                        sz1 = (u_long)cp - (u_long)tp;
                    883:                        if (sz1 > MAXPATHLEN)
                    884:                                goto bad;
                    885:                        *cp = 0;
                    886:                        sz2 = readlink(tp, tq, MAXPATHLEN);
                    887:                        *cp = '/';
                    888:
                    889:                        /* If this is not a symlink, move to next / */
                    890:                        if (sz2 <= 0)
                    891:                                continue;
                    892:
                    893:                        /*
                    894:                         * We found a symlink, so we will have to
                    895:                         * do one more pass to check there is no
                    896:                         * more symlink in the path
                    897:                         */
                    898:                        nomorelink = 0;
                    899:
                    900:                        /*
                    901:                         * Null terminate the string and remove trailing /
                    902:                         */
                    903:                        tq[sz2] = 0;
                    904:                        sz2 = strlen(tq);
                    905:                        if (tq[sz2 - 1] == '/')
                    906:                                tq[--sz2] = 0;
                    907:
                    908:                        /*
                    909:                         * Is this an absolute link or a relative link?
                    910:                         */
                    911:                        if (tq[0] == '/') {
                    912:                                /* absolute link */
                    913:                                if (strlen(cp) + sz2 > MAXPATHLEN)
                    914:                                        goto bad;
                    915:                                memmove(tp + sz2, cp, strlen(cp) + 1);
                    916:                                memcpy(tp, tq, sz2);
                    917:                        } else {
                    918:                                /* relative link */
                    919:                                for (cq = cp - 1; *cq != '/'; cq--);
                    920:                                if (strlen(tp) - ((u_long)cq - (u_long)cp)
                    921:                                    + 1 + sz2 > MAXPATHLEN)
                    922:                                        goto bad;
                    923:                                (void)memmove(cq + 1 + sz2,
                    924:                                    cp, strlen(cp) + 1);
                    925:                                (void)memcpy(cq + 1, tq, sz2);
                    926:                        }
                    927:
                    928:                        /*
                    929:                         * start over, looking for new symlinks
                    930:                         */
                    931:                        break;
                    932:                }
                    933:        } while (nomorelink == 0);
                    934:
                    935:        /* Collapse any /foo/../ into /foo/ */
                    936:        while ((cp = strstr(tp, "/../")) != NULL) {
                    937:                /* ^/../foo/ becomes ^/foo/ */
                    938:                if (cp == tp) {
                    939:                        (void)memmove(cp, cp + 3,
                    940:                            strlen(cp) - 3 + 1);
                    941:                } else {
                    942:                        for (cq = cp - 1; *cq != '/'; cq--);
                    943:                        (void)memmove(cq, cp + 3,
                    944:                            strlen(cp) - 3 + 1);
                    945:                }
                    946:        }
                    947:
                    948:        /* strip strailing / */
                    949:        if (strlen(tp) != 1)
                    950:                tp[strlen(tp) - 1] = '\0';
                    951:
                    952:        /* check that the path is correct */
                    953:        stat(tp, &st1);
                    954:        stat(".", &st2);
                    955:        if ((st1.st_dev != st2.st_dev) || (st1.st_ino != st2.st_ino))
                    956:                goto bad;
                    957:
                    958:        (void)strlcpy(last_path, tp, MAXPATHLEN);
                    959:        return;
                    960:
                    961: bad:
                    962:        (void)strlcat(last_path, "/", MAXPATHLEN);
                    963:        (void)strlcat(last_path, new_path, MAXPATHLEN);
                    964:        return;
                    965: }
                    966:

CVSweb <webmaster@jp.NetBSD.org>