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

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

CVSweb <webmaster@jp.NetBSD.org>